From 5f595b91e4ea3cb6196bf1fcb83eab165432b73d Mon Sep 17 00:00:00 2001
From: dbadol <13353088+dbadol@users.noreply.github.com>
Date: Mon, 15 Jul 2024 10:15:14 +0200
Subject: [PATCH] Explorer: Add block/kernel search field (#1991)
* Explorer: Allow querying all node args through URL
- Use same query 'types' in URL as in node query.
- Add the "treasury" as a pseudo-type in URL (just for convenience).
- Make all arguments into global variables that can be used in URL.
- Add error messages for blocks & kernels not found.
- Display Genesis block (instead of Treasury) by default for "block" query without specific height nor kernel.
- Highlight the queried kernel in block details.
* Explorer: Add block/kernel search
* Explorer: merge AddClass and AddClassEx
---
explorer/htm/BeamExplorer.htm | 298 ++++++++++++++++++++++++----------
1 file changed, 209 insertions(+), 89 deletions(-)
diff --git a/explorer/htm/BeamExplorer.htm b/explorer/htm/BeamExplorer.htm
index 64512d31a..db400e41e 100644
--- a/explorer/htm/BeamExplorer.htm
+++ b/explorer/htm/BeamExplorer.htm
@@ -3,7 +3,7 @@
Block Headers
@@ -714,8 +779,17 @@
" + MakeRef(UrlSelfWithID("assets", h, ""), "Confidential Assets at this block height") + "
";
+ return "
" + MakeRef(UrlSelfWithID("assets", h), "Confidential Assets at this block height") + "
";
}
function DisplayBlock()
{
let text = "";
+
+ const jData = JSON.parse(this.responseText);
+ let j = jData["info"];
+ if (!j) {
+ // Display error message if kernel or height was queried but no block was found
+ if (g_kernel || g_id > 0) {
+ if (g_kernel) {
+ text += "
Kernel not found.
"
+ } else {
+ text += "
Block not found.
"
+ }
+ SetContent(text);
+ return;
+ }
+ // Display Genesis block (instead of Treasury) if nothing at all was queried
+ if (!g_kernel && (!g_id && g_id !== 0)) { g_id = 1 }
+ }
+
+ // If the request was a kernel, get the resulting height
+ if (g_kernel) { g_id = j["value"][0][1]["value"] }
+
text += "
";
- if (g_CurrentID > 0) {
- text += AddClassEx(MakeRef(UrlSelf("hdrs", "&hMax=" + g_CurrentID), ""),"listOfBlockHeaders","title='List of blocks up to this one'");
+ if (g_id > 0) {
+ text += AddClass(MakeRef(UrlSelf("hdrs", "&hMax=" + g_id), ""),"listOfBlockHeaders","title='List of blocks up to this one'");
text += " ";
- text += "Block " + AddClass(g_CurrentID, "blockHeight");
+ text += "Block " + AddClass(g_id, "blockHeight");
text += " ";
- text += AddClassEx(MakeRef(UrlBlock(g_CurrentID - 1), ""),"previousBlock","title='Previous block'");
+ text += AddClass(MakeRef(UrlBlock(g_id - 1), ""),"previousBlock","title='Previous block'");
}
else {
text += "Treasury";
text += " ";
- text += AddClassEx("","previousBlock off","title=''");
+ text += AddClass("","previousBlock off","title=''");
}
- text += AddClassEx(MakeRef(UrlBlock(g_CurrentID - 1 + 2), ""),"nextBlock","title='Next block'");
+ text += AddClass(MakeRef(UrlBlock(g_id - 1 + 2), ""),"nextBlock","title='Next block'");
text += "
"
- const jData = JSON.parse(this.responseText);
-
- let j = jData["info"];
+ // Parse "info" section
if (j)
{
text += MakeCollapsibleBegin("Block Summary");
@@ -1295,7 +1383,10 @@
Deployed Smart Contracts
\n\
let jRow = jTbl[i];
text += "
";
- text += MakeCell(AddClassEx(jRow["id"], "kernelId", " title='" + jRow["id"] + "'"));
+ let idClass = "kernelId";
+ // Mark the kernel with a special class if it was the one queried
+ if (g_kernel && jRow["id"] == g_kernel) { idClass += " highlight" };
+ text += MakeCell(AddClass(jRow["id"], idClass, " title='" + jRow["id"] + "'"));
text += MakeCellRA(Obj2Html(jRow["fee"]));
let txtH = "";
@@ -1350,7 +1441,7 @@
Deployed Smart Contracts
\n\
text += MakeCollapsibleEnd();
}
else
- text += MakeCAs(g_CurrentID);
+ text += MakeCAs(g_id);
SetContent(text);
}
@@ -1449,7 +1540,7 @@
Deployed Smart Contracts
\n\
{
// Temporary hack:
// The node explorer does not provide (yet?) parameters to move pages forward.
- // Until then, we use the parameter given to go backwards and simply add twice nMax to it.
+ // Until then, we use the parameter given to go backwards and simply add twice g_nMax to it.
// This is imperfect, temporary, and -of course- only works for the Block Headers page...
const jMore = jTblObj["more"];
if (!jMore)
@@ -1460,7 +1551,7 @@
\n\
const jData = JSON.parse(this.responseText);
let text = "
\n";
- if (g_CurrentID) {
- text += "Confidential Assets at block height " + MakeBlock(g_CurrentID);
+ if (g_id) {
+ text += "Confidential Assets at block height " + MakeBlock(g_id);
text += " ";
- if (g_CurrentID > 1) {
- text += AddClassEx(MakeRef(UrlSelfWithID("assets", g_CurrentID - 1, ""), ""),"previousBlock","title='Previous block'");
+ if (g_id > 1) {
+ text += AddClass(MakeRef(UrlSelfWithID("assets", g_id - 1), ""),"previousBlock","title='Previous block'");
} else {
- text += AddClassEx("","previousBlock off","title=''");
+ text += AddClass("","previousBlock off","title=''");
}
- text += AddClassEx(MakeRef(UrlSelfWithID("assets", g_CurrentID - 1 + 2, ""), ""),"nextBlock","title='Next block'");
+ text += AddClass(MakeRef(UrlSelfWithID("assets", g_id - 1 + 2), ""),"nextBlock","title='Next block'");
} else {
text += "Current Confidential Assets";
}
@@ -1698,7 +1789,7 @@
Deployed Smart Contracts
\n\
{
block_list: [780219],
title: "Creation of the first Confidential Asset",
- description: "Two days after the second hard-fork, the first Confidential Asset (with id:1) was created. Although it has changed named since then, and seems to be inactive today, this first Confidential Asset was actually a draft for the subsequent Tico coin (id:9) which has been quite successful as the first confidential meme coin, with its fair, fun and feathered initial distribution! As can be seen in the block details of any block, the transactions of confidential assets are as private as Beam transactions (i.e. no amount and no addresses are visible), and are actually even undistinguishable from Beam transactions themselves (the id of the transacted asset is shown to be in a [0-63] range, with id:0 being Beam itself)! Since then, numerous confidential assets have been created (including the 'AMML' ones, created by the DEX smart contract to represent the shares of its Liquidity Pools). Confidential assets have an unlimited supply when created through CLI (the owner wallet or contract can mint as many as needed), or a hard-capped max supply when created through the Beam Asset Minter.",
+ description: "Two days after the second hard-fork, the first Confidential Asset (with id:1) was created. Although it has changed named since then, and seems to be inactive today, this first Confidential Asset was actually a draft for the subsequent Tico coin (id:9) which has been quite successful as the first confidential meme coin, with its fair, fun and feathered initial distribution! As can be seen in the block details of any block, the transactions of confidential assets are as private as Beam transactions (i.e. no amount and no addresses are visible), and are actually even undistinguishable from Beam transactions themselves (the id of the transacted asset is shown to be in a [0-63] range, with id:0 being Beam itself)! Since then, numerous confidential assets have been created (including the 'AMML' ones, created by the DEX smart contract to represent the shares of its Liquidity Pools). Confidential assets have an unlimited supply when created through CLI (the owner wallet or contract can mint as many as needed), or a hard-capped max supply when created through the Beam Asset Minter.",
links: [
//["Tico website","https://www.ticotip.me"],
["Tico's first anniversary","https://ticotipme.substack.com/p/tico-turns-1"],
@@ -1725,7 +1816,7 @@
Deployed Smart Contracts
\n\
{
block_list: [1464852],
title: "BeamX creation",
- description: "The BeamX confidential asset (with id:7) is the governance token of the 'BeamX DAO' towards which the Beam Foundation is now transitioning. Its 100,000,000 units were minted all at once by the DAO Core smart contract, which would later distribute these units over a period of 4 years according to a predefined schedule. Part of the BeamX supply is reserved for liquidity incentives towards Beam's DeFi ecosystem (staking campaigns, liquidity provider rewards, etc.).",
+ description: "The BeamX confidential asset (with id:7) is the governance token of the 'BeamX DAO' towards which the Beam Foundation is now transitioning. Its 100,000,000 units were minted all at once by the DAO Core smart contract, which would later distribute these units over a period of 4 years according to a predefined schedule. Part of the BeamX supply is reserved for liquidity incentives towards Beam's DeFi ecosystem (staking campaigns, liquidity provider rewards, etc.).",
links: [
["The BeamX DAO","https://www.beamxdao.org"],
["The BeamX token","https://medium.com/beam-mw/introducing-beamx-privacy-by-default-maximum-confidentiality-defi-ecosystem-629670b905ba"]
@@ -1773,30 +1864,26 @@
Deployed Smart Contracts
\n\
}
];
- // Current block, asset id or contract id
- let g_CurrentID = "";
-
// URL and explorer node parameters
const args = (new URL(document.location)).searchParams;
- const type = args.get("type");
- g_CurrentID = args.get("id");
-
+ let g_type = args.get("type"); // Remark: We've added two URL types not used by the explorer node ("historical" and "treasury")
+ let g_id = args.get("id"); // Serves for both 'id' (asset & contract) and 'height'
+ let g_kernel = args.get("kernel"); // Takes precedence over 'height' (blocks)
+ let g_hMin = args.get("hMin");
+ let g_hMax = args.get("hMax");
+ let g_nMax = args.get("nMax"); // Serves for nMax (hdrs), n (blocks), nMaxOps (asset) and nMaxTxs (contract)
+ let g_cols = args.get("cols"); // Only for hdrs
+ let g_dh = args.get("dh"); // Only for hdrs
+
+ // Address of the remote explorer node
//let urlPrefix = "http://localhost:17328/";
let urlPrefix = "http://116.203.118.51:8100/";
- // Request "nice" formatting from the node (timestamps will be expanded into YYYYY-MM-DD, amounts formatted as XXX,XXX,XXX.YYYYYYYY, etc.)
+ // Request 'nice' formatting from the node (timestamps as YYYYY-MM-DD, amounts as XXX,XXX,XXX.YYYYYYYY, etc.)
let urlSuffix = "?exp_am=1";
- const hMax = args.get("hMax");
- if (hMax)
- urlSuffix += "&hMax=" + hMax;
-
- let nMax = args.get("nMax");
- if (!nMax)
- nMax = 100;
-
// Static page
- if (type == "historical")
+ if (g_type == "historical") // Arguments for 'historical': (none)
{
window.onload = DisplayHistoricalBlocks;
}
@@ -1805,33 +1892,52 @@
Deployed Smart Contracts
\n\
{
const xmlhttp = new XMLHttpRequest();
- if (type == "aid")
+ // Build request
+ if (g_type == "asset") // Arguments for 'asset': id, hMin, hMax, nMaxOps
{
+ if (!g_nMax) { g_nMax = 100 }
+ urlSuffix += "&id=" + g_id + "&nMaxOps" + g_nMax;
+ if (g_hMin) { urlSuffix += "&hMin=" + g_hMin }
+ if (g_hMax) { urlSuffix += "&hMax=" + g_hMax }
xmlhttp.onload = DisplayAssetHistory;
- xmlhttp.open("GET", urlPrefix + "asset" + urlSuffix + "&id=" + g_CurrentID);
- xmlhttp.send();
}
- else if (type == "block")
+ else if (g_type == "block") // Arguments for 'block': height OR kernel
{
+ // We decide that 'kernel' argument takes precedence over 'height'
+ if (g_kernel) {
+ urlSuffix += "&kernel=" + g_kernel;
+ } else {
+ urlSuffix += "&height=" + g_id;
+ }
+ xmlhttp.onload = DisplayBlock;
+ }
+ else if (g_type == "treasury") // 'treasury' is 'block' with height=0
+ {
+ g_type = "block";
+ g_id = 0;
+ urlSuffix += "&height=" + g_id;
xmlhttp.onload = DisplayBlock;
- //if (kernel) {
- // console.log(urlPrefix + "block" + urlSuffix + "&kernel=" + kernel);
- // xmlhttp.open("GET", urlPrefix + "block" + urlSuffix + "&kernel=" + kernel);
- //} else {
- // console.log(urlPrefix + "block" + urlSuffix + "&height=" + g_CurrentID);
- xmlhttp.open("GET", urlPrefix + "block" + urlSuffix + "&height=" + g_CurrentID);
- //}
- xmlhttp.send();
}
- else if (type == "contract")
+ // Option to query and display the details of multiple blocks (not used here)
+ // else if (g_type == "blocks") // Arguments for 'blocks': height, n
+ // {
+ // if (!g_nMax) { g_nMax = 5 }
+ // urlSuffix += "&height=" + g_id + "&n=" + g_nMax;
+ // //xmlhttp.onload = DisplayBlocks; // Display function to be done...
+ // xmlhttp.onload = DisplayStatus; // Until then, use this as test
+ // }
+ else if (g_type == "contract") // Arguments for 'contract': id, hMin, hMax, nMaxTxs
{
+ if (!g_nMax) { g_nMax = 100 }
+ urlSuffix += "&id=" + g_id + "&nMaxTxs=" + g_nMax;
+ if (g_hMin) { urlSuffix += "&hMin=" + g_hMin }
+ if (g_hMax) { urlSuffix += "&hMax=" + g_hMax }
xmlhttp.onload = DisplayContractState;
- xmlhttp.open("GET", urlPrefix + "contract" + urlSuffix + "&id=" + g_CurrentID + "&nMaxTxs=" + nMax);
- xmlhttp.send();
}
- else if (type == "hdrs")
+ else if (g_type == "hdrs") // Arguments for 'hdrs': cols, hMax, nMax, dh
{
- // Data that can be requested (lowercase if relative, uppercase if absolute):
+ // Codes for 'cols' (lowercase if relative, uppercase if absolute):
+ // Remark: First column is always the block height
// H = Hash
// T = Time
// d = Difficulty
@@ -1846,34 +1952,48 @@