Browse Source

More data on block pages and rpc api versioning:

* New data in "Summary" on Block pages (supported for bitcoind v0.17.0+)
    * Fee percentiles
    * Min / Max fees
    * Input / Output counts
    * Outputs total value
    * UTXO count change
    * Min / Max tx sizes
* Start of RPC API versioning support
master
Dan Janosik 5 years ago
parent
commit
ae632abed5
No known key found for this signature in database GPG Key ID: C6F8CE9FFDB2CED2
  1. 9
      CHANGELOG.md
  2. 56
      app.js
  3. 9
      app/api/coreApi.js
  4. 16
      app/api/rpcApi.js
  5. 28
      npm-shrinkwrap.json
  6. 1
      package.json
  7. 6
      roadmap.md
  8. 80
      routes/baseActionsRouter.js
  9. 420
      views/includes/block-content.pug
  10. 5
      views/includes/debug-overrides.pug

9
CHANGELOG.md

@ -18,11 +18,20 @@
* Add total fees display
* Demote display of "block size" value to hover
* Show weight in kWu instead of Wu
* New data in "Summary" on Block pages (supported for bitcoind v0.17.0+)
* Fee percentiles
* Min / Max fees
* Input / Output counts
* Outputs total value
* UTXO count change
* Min / Max tx sizes
* New tool `/mining-summary` for viewing summarized mining data from recent blocks
* Zero-indexing for tx inputs/outputs (#173)
* Labels for transaction output types
* Configurable UI "sub-header" links
* Start of RPC API versioning support
* Tweaked styling
* Remove "Bitcoin Explorer" H1 from homepage (it's redundant)
* Updated miner configs
#### v1.1.9

56
app.js

@ -19,6 +19,7 @@ var debug = require("debug");
debug.enable(process.env.DEBUG || "btcexp:app,btcexp:error");
var debugLog = debug("btcexp:app");
var debugErrorLog = debug("btcexp:error");
var debugPerfLog = debug("btcexp:actionPerformace");
var express = require('express');
@ -203,7 +204,48 @@ function onRpcConnectionVerified(getnetworkinfo, getblockchaininfo) {
global.getnetworkinfo = getnetworkinfo;
debugLog(`RPC Connected: version=${getnetworkinfo.version} (${getnetworkinfo.subversion}), protocolversion=${getnetworkinfo.protocolversion}, chain=${getblockchaininfo.chain}, services=${services}`);
var bitcoinCoreVersionRegex = /^.*\/Satoshi\:(.*)\/.*$/;
var match = bitcoinCoreVersionRegex.exec(getnetworkinfo.subversion);
if (match) {
global.btcNodeVersion = match[1];
var semver4PartRegex = /^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$/;
var semver4PartMatch = semver4PartRegex.exec(global.btcNodeVersion);
if (semver4PartMatch) {
var p0 = semver4PartMatch[1];
var p1 = semver4PartMatch[2];
var p2 = semver4PartMatch[3];
var p3 = semver4PartMatch[4];
// drop last segment, which usually indicates a bug fix release which is (hopefully) irrelevant for RPC API versioning concerns
global.btcNodeSemver = `${p0}.${p1}.${p2}`;
} else {
var semver3PartRegex = /^([0-9]+)\.([0-9]+)\.([0-9]+)$/;
var semver3PartMatch = semver3PartRegex.exec(global.btcNodeVersion);
if (semver3PartMatch) {
var p0 = semver3PartMatch[1];
var p1 = semver3PartMatch[2];
var p2 = semver3PartMatch[3];
global.btcNodeSemver = `${p0}.${p1}.${p2}`;
} else {
// short-circuit: force all RPC calls to pass their version checks - this will likely lead to errors / instability / unexpected results
global.btcNodeSemver = "1000.1000.0"
}
}
} else {
// short-circuit: force all RPC calls to pass their version checks - this will likely lead to errors / instability / unexpected results
global.btcNodeSemver = "1000.1000.0"
debugErrorLog(`Unable to parse node version string: ${getnetworkinfo.subversion} - RPC versioning will likely be unreliable. Is your node a version of Bitcoin Core?`);
}
debugLog(`RPC Connected: version=${getnetworkinfo.version} subversion=${getnetworkinfo.subversion}, parsedVersion(used for RPC versioning)=${global.btcNodeSemver}, protocolversion=${getnetworkinfo.protocolversion}, chain=${getblockchaininfo.chain}, services=${services}`);
// load historical/fun items for this chain
loadHistoricalDataForChain(global.activeBlockchain);
@ -408,6 +450,18 @@ app.use(function(req, res, next) {
}
}
// blockPage.showTechSummary
if (!req.session.blockPageShowTechSummary) {
var cookieValue = req.cookies['user-setting-blockPageShowTechSummary'];
if (cookieValue) {
req.session.blockPageShowTechSummary = cookieValue;
} else {
req.session.blockPageShowTechSummary = "true";
}
}
// homepage banner
if (!req.session.hideHomepageBanner) {
var cookieValue = req.cookies['user-setting-hideHomepageBanner'];

9
app/api/coreApi.js

@ -179,6 +179,12 @@ function getNetworkHashrate(blockCount) {
});
}
function getBlockStats(hash) {
return tryCacheThenRpcApi(miscCache, "getBlockStats-" + hash, 1000 * 60 * 1000, function() {
return rpcApi.getBlockStats(hash);
});
}
function getUtxoSetSummary() {
return tryCacheThenRpcApi(miscCache, "getUtxoSetSummary", 15 * 60 * 1000, rpcApi.getUtxoSetSummary);
}
@ -992,5 +998,6 @@ module.exports = {
getSmartFeeEstimates: getSmartFeeEstimates,
getSmartFeeEstimate: getSmartFeeEstimate,
getUtxoSetSummary: getUtxoSetSummary,
getNetworkHashrate: getNetworkHashrate
getNetworkHashrate: getNetworkHashrate,
getBlockStats: getBlockStats,
};

16
app/api/rpcApi.js

@ -3,6 +3,7 @@ var debug = require('debug');
var debugLog = debug("btcexp:rpc");
var async = require("async");
var semver = require("semver");
var utils = require("../utils.js");
var config = require("../config.js");
@ -66,7 +67,13 @@ function getNetworkHashrate(blockCount=144) {
}
function getBlockStats(hash) {
return getRpcDataWithParams({method:"getblockstats", parameters:[hash]});
if (semver.gte(global.btcNodeSemver, "0.17.0")) {
return getRpcDataWithParams({method:"getblockstats", parameters:[hash]});
} else {
// unsupported
return nullPromise();
}
}
function getUtxoSetSummary() {
@ -341,6 +348,12 @@ function getRpcDataWithParams(request) {
});
}
function nullPromise() {
return new Promise(function(resolve, reject) {
resolve(null);
});
}
module.exports = {
getBlockchainInfo: getBlockchainInfo,
@ -364,4 +377,5 @@ module.exports = {
getSmartFeeEstimate: getSmartFeeEstimate,
getUtxoSetSummary: getUtxoSetSummary,
getNetworkHashrate: getNetworkHashrate,
getBlockStats: getBlockStats
};

28
npm-shrinkwrap.json

@ -304,6 +304,13 @@
"request": "^2.53.0",
"semver": "^5.1.0",
"standard-error": "^1.1.0"
},
"dependencies": {
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
}
}
},
"bitcoin-ops": {
@ -1885,6 +1892,13 @@
"is-builtin-module": "^1.0.0",
"semver": "2 || 3 || 4 || 5",
"validate-npm-package-license": "^3.0.1"
},
"dependencies": {
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
}
}
},
"npm-run-all": {
@ -1915,6 +1929,14 @@
"semver": "^5.5.0",
"shebang-command": "^1.2.0",
"which": "^1.2.9"
},
"dependencies": {
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
}
}
},
"load-json-file": {
@ -2501,9 +2523,9 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"semver": {
"version": "5.5.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz",
"integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw=="
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz",
"integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA=="
},
"send": {
"version": "0.17.1",

1
package.json

@ -46,6 +46,7 @@
"qrcode": "^1.4.4",
"redis": "^2.8.0",
"request": "^2.88.0",
"semver": "^7.1.3",
"serve-favicon": "^2.5.0",
"simple-git": "^1.129.0"
},

6
roadmap.md

@ -0,0 +1,6 @@
* Tool for analyzing block details
* fee graphs
* value graphs
* Homepage:
* Summary table item for 24hr volume (pulled via blockstats.total_output)
* New page for 24hr volume for last ~30days

80
routes/baseActionsRouter.js

@ -525,15 +525,49 @@ router.get("/block-height/:blockHeight", function(req, res, next) {
coreApi.getBlockByHeight(blockHeight).then(function(result) {
res.locals.result.getblockbyheight = result;
coreApi.getBlockByHashWithTransactions(result.hash, limit, offset).then(function(result) {
res.locals.result.getblock = result.getblock;
res.locals.result.transactions = result.transactions;
res.locals.result.txInputsByTransaction = result.txInputsByTransaction;
var promises = [];
promises.push(new Promise(function(resolve, reject) {
coreApi.getBlockByHashWithTransactions(result.hash, limit, offset).then(function(result) {
res.locals.result.getblock = result.getblock;
res.locals.result.transactions = result.transactions;
res.locals.result.txInputsByTransaction = result.txInputsByTransaction;
resolve();
});
}));
promises.push(new Promise(function(resolve, reject) {
coreApi.getBlockStats(result.hash).then(function(result) {
res.locals.result.blockstats = result;
resolve();
}).catch(function(err) {
res.locals.userMessage = "Error getting block stats";
reject(err);
});
}));
Promise.all(promises).then(function() {
res.render("block");
next();
}).catch(function(err) {
res.locals.pageErrors.push(utils.logError("3249y2ewgfee", err));
res.render("block");
next();
});
}).catch(function(err) {
res.locals.pageErrors.push(utils.logError("389wer07eghdd", err));
res.render("block");
next();
});
});
@ -565,18 +599,44 @@ router.get("/block/:blockHash", function(req, res, next) {
res.locals.limit = limit;
res.locals.offset = offset;
res.locals.paginationBaseUrl = "/block/" + blockHash;
coreApi.getBlockByHashWithTransactions(blockHash, limit, offset).then(function(result) {
res.locals.result.getblock = result.getblock;
res.locals.result.transactions = result.transactions;
res.locals.result.txInputsByTransaction = result.txInputsByTransaction;
var promises = [];
promises.push(new Promise(function(resolve, reject) {
coreApi.getBlockByHashWithTransactions(blockHash, limit, offset).then(function(result) {
res.locals.result.getblock = result.getblock;
res.locals.result.transactions = result.transactions;
res.locals.result.txInputsByTransaction = result.txInputsByTransaction;
resolve();
}).catch(function(err) {
res.locals.userMessage = "Error getting block data";
reject(err);
});
}));
promises.push(new Promise(function(resolve, reject) {
coreApi.getBlockStats(blockHash).then(function(result) {
res.locals.result.blockstats = result;
resolve();
}).catch(function(err) {
res.locals.userMessage = "Error getting block stats";
reject(err);
});
}));
Promise.all(promises).then(function() {
res.render("block");
next();
}).catch(function(err) {
res.locals.userMessage = "Error getting block data";
res.locals.pageErrors.push(utils.logError("3217wfeghy9sdgs", err));
res.render("block");

420
views/includes/block-content.pug

@ -1,3 +1,20 @@
div.mb-2
if (result.getblock.previousblockhash)
a.btn.btn-sm.btn-primary.mb-1(href=("/block/" + result.getblock.previousblockhash)) « Prev Block:
span.text-monospace ##{(result.getblock.height - 1).toLocaleString()}
else if (result.getblock.hash == genesisBlockHash)
span.btn.btn-sm.btn-secondary.disabled.mb-1 « Prev Block: none (genesis block)
span.mx-2
if (result.getblock.nextblockhash)
a.btn.btn-sm.btn-primary.mb-1(href=("/block/" + result.getblock.nextblockhash)) Next Block:
span.text-monospace ##{(result.getblock.height + 1).toLocaleString()} »
else
a.btn.btn-sm.btn-secondary.disabled.mb-1 Next Block: N/A
small (latest block)
ul.nav.nav-tabs.mb-3
li.nav-item
a.nav-link.active(data-toggle="tab", href="#tab-details", role="tab") Details
@ -32,170 +49,252 @@ div.tab-content
span
a(href=sbInfo.referenceUrl) Read more
div.card.shadow-sm.mb-3
div.card-body.px-2.px-md-3
h3.h6 Summary
hr
div.row
- var sumTableLabelClass = (result.blockstats != null ? "summary-split-table-label" : "summary-table-label");
- var sumTableValueClass = (result.blockstats != null ? "summary-split-table-content" : "summary-table-content");
div.row
div(class="col-md-6")
div.row
div.summary-split-table-label Previous Block
div.summary-split-table-content.text-monospace
if (result.getblock.previousblockhash)
a(class="word-wrap", href=("/block/" + result.getblock.previousblockhash)) #{result.getblock.previousblockhash}
br
span (##{(result.getblock.height - 1).toLocaleString()})
else if (result.getblock.hash == genesisBlockHash)
span None (genesis block)
div.row
div.summary-split-table-label Date
div.summary-split-table-content.text-monospace
- var timestampHuman = result.getblock.time;
include ./timestamp-human.pug
small.ml-1 utc
br
- var timeAgoTime = result.getblock.time;
span.text-muted (
include ./time-ago-text.pug
span )
div.row
div.summary-split-table-label Transactions
div.summary-split-table-content.text-monospace #{result.getblock.tx.length.toLocaleString()}
div.row
div.summary-split-table-label Total Fees
div.summary-split-table-content.text-monospace
- var currencyValue = new Decimal(result.getblock.totalFees);
include ./value-display.pug
if (result.getblock.totalFees > 0)
div.mb-3(class=(result.blockstats != null ? "col-md-6 pr-0" : "col-md-12"))
div.card.shadow-sm(style="height: 100%;")
div.card-body.px-2.px-md-3
h3.h6.mb-0 Summary
hr
div.clearfix
div.row
div.summary-split-table-label Average Fee
div.summary-split-table-content.text-monospace
- var currencyValue = new Decimal(result.getblock.totalFees).dividedBy(result.getblock.strippedsize).times(coinConfig.baseCurrencyUnit.multiplier).toDecimalPlaces(1);
span #{currencyValue} sat/vB
br
span.text-muted (
- var currencyValue = new Decimal(result.getblock.totalFees).dividedBy(result.getblock.tx.length);
include ./value-display.pug
div(class=sumTableLabelClass) Date
div.text-monospace(class=sumTableValueClass)
- var timestampHuman = result.getblock.time;
include ./timestamp-human.pug
small.ml-1 utc
- var timeAgoTime = result.getblock.time;
span.text-muted.ml-2 (
include ./time-ago-text.pug
span )
- var blockRewardMax = coinConfig.blockRewardFunction(result.getblock.height, global.activeBlockchain);
- var coinbaseTxTotalOutputValue = new Decimal(0);
each vout in result.getblock.coinbaseTx.vout
- coinbaseTxTotalOutputValue = coinbaseTxTotalOutputValue.plus(new Decimal(vout.value));
if (result.blockstats)
div.row
div(class=sumTableLabelClass)
span.border-dotted(title="Total value of all transaction outputs (excluding the coinbase transaction)", data-toggle="tooltip")
span Total Output
div.text-monospace(class=sumTableValueClass)
- var currencyValue = new Decimal(result.blockstats.total_out).dividedBy(coinConfig.baseCurrencyUnit.multiplier);
include ./value-display.pug
if (result.blockstats)
div.row
div(class=sumTableLabelClass)
span.border-dotted(title="Total number of inputs and outputs", data-toggle="tooltip")
span In #
span.text-muted.font-weight-normal.mx-1 /
span Out #
div.text-monospace(class=sumTableValueClass)
span #{result.blockstats.ins.toLocaleString()}
span.text-muted.mx-1 /
span #{result.blockstats.outs.toLocaleString()}
if (result.blockstats)
div.row
div(class=sumTableLabelClass)
span.border-dotted(title="Change in number (and size) of UTXO set.", data-toggle="tooltip")
span UTXO Δ
div.text-monospace(class=sumTableValueClass)
- var sizePlusMinus = (result.blockstats.utxo_size_inc > 0) ? "+" : "-";
- var sizeDeltaData = utils.formatLargeNumber(Math.abs(result.blockstats.utxo_size_inc), 1);
- var plusMinus = (result.blockstats.utxo_increase > 0) ? "+" : "";
span #{plusMinus}#{result.blockstats.utxo_increase.toLocaleString()}
span.text-muted (#{sizePlusMinus}#{sizeDeltaData[0]} #{sizeDeltaData[1].abbreviation}B)
if (result.blockstats)
div.row
div(class=sumTableLabelClass) Min - Max Tx Size
div.text-monospace(class=sumTableValueClass)
span #{result.blockstats.mintxsize.toLocaleString()}
span.text-muted.mx-1 -
span #{result.blockstats.maxtxsize.toLocaleString()} B
- var blockRewardMax = coinConfig.blockRewardFunction(result.getblock.height, global.activeBlockchain);
- var coinbaseTxTotalOutputValue = new Decimal(0);
each vout in result.getblock.coinbaseTx.vout
- coinbaseTxTotalOutputValue = coinbaseTxTotalOutputValue.plus(new Decimal(vout.value));
if (parseFloat(coinbaseTxTotalOutputValue) < blockRewardMax)
div.row
div(class=sumTableLabelClass)
span.border-dotted(data-toggle="tooltip" title="The miner of this block failed to collect this value. As a result, it is lost.") Fees Destroyed
div.text-monospace.text-danger(class=sumTableValueClass)
- var currencyValue = new Decimal(blockRewardMax).minus(coinbaseTxTotalOutputValue);
include ./value-display.pug
if (result.getblock.weight)
div.row
div(class=sumTableLabelClass) Weight
div.text-monospace(class=sumTableValueClass)
span(style="") #{result.getblock.weight.toLocaleString()} wu
span.text-muted (#{new Decimal(100 * result.getblock.weight / coinConfig.maxBlockWeight).toDecimalPlaces(2)}% full)
if (parseFloat(coinbaseTxTotalOutputValue) < blockRewardMax)
div.row
div.summary-split-table-label
span.border-dotted(data-toggle="tooltip" title="The miner of this block failed to collect this value. As a result, it is lost.") Fees Destroyed
div(class="summary-split-table-content text-monospace text-danger")
- var currencyValue = new Decimal(blockRewardMax).minus(coinbaseTxTotalOutputValue);
include ./value-display.pug
div(class=sumTableLabelClass) Size
div.text-monospace(class=sumTableValueClass) #{result.getblock.size.toLocaleString()} bytes
if (result.getblock.weight)
div.row
div.summary-split-table-label Weight
div.summary-split-table-content.text-monospace
span(style="") #{result.getblock.weight.toLocaleString()} wu
div(class=sumTableLabelClass) Confirmations
div.text-monospace(class=sumTableValueClass)
if (result.getblock.confirmations < 6)
span(class="font-weight-bold text-warning") #{result.getblock.confirmations.toLocaleString()}
a(data-toggle="tooltip", title="Fewer than 6 confirmations is generally considered 'unsettled' for high-value transactions. The applicability of this guidance may vary.")
i(class="fas fa-unlock-alt")
else
span(class="font-weight-bold text-success font-weight-bold") #{result.getblock.confirmations.toLocaleString()}
a(data-toggle="tooltip", title="6 confirmations is generally considered 'settled'. High-value transactions may require more; low-value transactions may require less.")
i.fas.fa-lock
if (result.blockstats)
div.col-md-6.mb-3
div.card.shadow-sm(style="height: 100%;")
div.card-body.px-2.px-md-3
h3.h6.mb-0 Fees Summary
hr
div.clearfix
div.row
div.summary-split-table-label Total
div.summary-split-table-content.text-monospace
- var currencyValue = new Decimal(result.getblock.totalFees);
include ./value-display.pug
if (result.blockstats)
div.row
div.summary-split-table-label Percentiles
br
small.font-weight-normal (sat/vB)
div.summary-split-table-content.text-monospace
div.clearfix
each item, itemIndex in [10, 25, 50, 75, 90]
div.float-left.mr-3
span.font-weight-bold #{item}%
br
span #{JSON.stringify(result.blockstats.feerate_percentiles[itemIndex])}
if (result.getblock.totalFees > 0)
div.row
div.summary-split-table-label Average Rate
div.summary-split-table-content.text-monospace
- var currencyValue = new Decimal(result.getblock.totalFees).dividedBy(result.getblock.strippedsize).times(coinConfig.baseCurrencyUnit.multiplier).toDecimalPlaces(1);
span #{currencyValue} sat/vB
br
span.text-muted (
- var currencyValue = new Decimal(result.getblock.totalFees).dividedBy(result.getblock.tx.length);
include ./value-display.pug
span )
if (result.blockstats)
div.row
div.summary-split-table-label Median Rate
div.summary-split-table-content.text-monospace
- var currencyValue = new Decimal(result.blockstats.medianfee).dividedBy(1000).toDecimalPlaces(1);
span #{currencyValue} sat/vB
if (result.blockstats)
div.row
div.summary-split-table-label Min, Max Rate
div.summary-split-table-content.text-monospace
- var currencyValue = new Decimal(result.blockstats.minfeerate).toDecimalPlaces(1);
span #{currencyValue}
span.text-muted.mx-1 -
span.text-muted (#{new Decimal(100 * result.getblock.weight / coinConfig.maxBlockWeight).toDecimalPlaces(2)}% full)
- var currencyValue = new Decimal(result.blockstats.maxfeerate).toDecimalPlaces(1);
span #{currencyValue}
- var fullPercent = new Decimal(100 * result.getblock.weight / coinConfig.maxBlockWeight).toDecimalPlaces(0);
small.ml-1 sat/vB
if (result.blockstats)
div.row
div(class="col-md-10 col-lg-8 col-12")
div(class="progress my-1 mr-2", style="height: 4px;")
div(class="progress-bar", role="progressbar", style=("width: " + fullPercent + "%;"), aria-valuenow=parseInt(100 * result.getblock.weight / coinConfig.maxBlockWeight), aria-valuemin="0" ,aria-valuemax="100")
div.summary-split-table-label
span.border-dotted(title="These are the min and max fees for individual transactions included in this block.", data-toggle="tooltip") Min, Max Total
div.summary-split-table-content.text-monospace
- var currencyValue = new Decimal(result.blockstats.minfee).dividedBy(coinConfig.baseCurrencyUnit.multiplier);
include ./value-display.pug
br
- var currencyValue = new Decimal(result.blockstats.maxfee).dividedBy(coinConfig.baseCurrencyUnit.multiplier);
include ./value-display.pug
div.card.shadow-sm.mb-3
div.card-body.px-2.px-md-3
div.clearfix
div.float-left.mr-2
h3.h6.mb-0 Technical Details
div.float-right
if (!session.blockPageShowTechSummary || session.blockPageShowTechSummary == "true")
a(href="/changeSetting?name=blockPageShowTechSummary&value=false") hide
else
a(href="/changeSetting?name=blockPageShowTechSummary&value=true") show
div(id="tech-details-wrapper", class=(session.blockPageShowTechSummary == "true" ? "" : "d-none"))
hr
div.row
div.col-md-6
div.row
div.summary-split-table-label Difficulty
div.summary-split-table-content.text-monospace
- var difficultyData = utils.formatLargeNumber(result.getblock.difficulty, 3);
span(title=parseFloat(result.getblock.difficulty).toLocaleString(), data-toggle="tooltip")
span #{difficultyData[0]}
span x 10
sup #{difficultyData[1].exponent}
div.row
div.summary-split-table-label Size
div.summary-split-table-content.text-monospace #{result.getblock.size.toLocaleString()} bytes
div.row
div.summary-split-table-label Confirmations
div.summary-split-table-content.text-monospace
if (result.getblock.confirmations < 6)
span(class="font-weight-bold text-warning") #{result.getblock.confirmations.toLocaleString()}
a(data-toggle="tooltip", title="Fewer than 6 confirmations is generally considered 'unsettled' for high-value transactions. The applicability of this guidance may vary.")
i(class="fas fa-unlock-alt")
else
span(class="font-weight-bold text-success font-weight-bold") #{result.getblock.confirmations.toLocaleString()}
a(data-toggle="tooltip", title="6 confirmations is generally considered 'settled'. High-value transactions may require more; low-value transactions may require less.")
i(class="fas fa-lock")
div(class="col-md-6")
div.row
div.summary-split-table-label Next Block
div.summary-split-table-content.text-monospace
if (result.getblock.nextblockhash)
a(href=("/block/" + result.getblock.nextblockhash)) #{result.getblock.nextblockhash}
br
span (##{(result.getblock.height + 1).toLocaleString()})
else
span None (latest block)
div.row
div.summary-split-table-label Difficulty
div.summary-split-table-content.text-monospace
- var difficultyData = utils.formatLargeNumber(result.getblock.difficulty, 3);
span(title=parseFloat(result.getblock.difficulty).toLocaleString(), data-toggle="tooltip")
span #{difficultyData[0]}
span x 10
sup #{difficultyData[1].exponent}
div.row
div.summary-split-table-label Version
div.summary-split-table-content.text-monospace 0x#{result.getblock.versionHex}
span.text-muted (decimal: #{result.getblock.version})
div.row
div.summary-split-table-label Nonce
div.summary-split-table-content.text-monospace #{result.getblock.nonce}
div.row
div.summary-split-table-label Bits
div.summary-split-table-content.text-monospace #{result.getblock.bits}
div.row
div.summary-split-table-label Merkle Root
div.summary-split-table-content.text-monospace #{result.getblock.merkleroot}
div.row
div.summary-split-table-label Chainwork
div.summary-split-table-content.text-monospace
- var chainworkData = utils.formatLargeNumber(parseInt("0x" + result.getblock.chainwork), 2);
span #{chainworkData[0]}
span x 10
sup #{chainworkData[1].exponent}
span hashes
span.text-muted (#{result.getblock.chainwork.replace(/^0+/, '')})
div.row
div.summary-split-table-label Version
div.summary-split-table-content.text-monospace 0x#{result.getblock.versionHex}
span.text-muted (decimal: #{result.getblock.version})
if (result.getblock.miner)
div.row
div.summary-split-table-label Miner
div.summary-split-table-content.text-monospace.mb-0
if (result.getblock.miner)
if (result.getblock.miner.identifiedBy)
small.data-tag.bg-primary(data-toggle="tooltip", title=("Identified by: " + result.getblock.miner.identifiedBy)) #{result.getblock.miner.name}
div.summary-split-table-label Nonce
div.summary-split-table-content.text-monospace #{result.getblock.nonce}
div.row
div.summary-split-table-label Bits
div.summary-split-table-content.text-monospace #{result.getblock.bits}
div.row
div.summary-split-table-label Merkle Root
div.summary-split-table-content.text-monospace #{result.getblock.merkleroot}
div.row
div.summary-split-table-label Chainwork
div.summary-split-table-content.text-monospace
- var chainworkData = utils.formatLargeNumber(parseInt("0x" + result.getblock.chainwork), 2);
span #{chainworkData[0]}
span x 10
sup #{chainworkData[1].exponent}
span hashes
span.text-muted (#{result.getblock.chainwork.replace(/^0+/, '')})
if (result.getblock.miner)
div.row
div.summary-split-table-label Miner
div.summary-split-table-content.text-monospace.mb-0
if (result.getblock.miner)
if (result.getblock.miner.identifiedBy)
small.data-tag.bg-primary(data-toggle="tooltip", title=("Identified by: " + result.getblock.miner.identifiedBy)) #{result.getblock.miner.name}
else
small.data-tag.bg-primary #{result.getblock.miner.name}
else
small.data-tag.bg-primary #{result.getblock.miner.name}
else
span ?
span(data-toggle="tooltip", title="Unable to identify miner")
i(class="fas fa-info-circle")
span ?
span(data-toggle="tooltip", title="Unable to identify miner")
i.fas.fa-info-circle
div.card.shadow-sm.mb-3
@ -230,7 +329,7 @@ div.tab-content
div
each tx, txIndex in result.transactions
//pre
// code(class="json bg-light") #{JSON.stringify(tx, null, 4)}
// code.json.bg-light #{JSON.stringify(tx, null, 4)}
div.card.shadow-sm(class=(" " + ((txIndex < (result.transactions.length - 1) || txCount > limit) ? "mb-3" : "")))
div.card-header.text-monospace
if (tx && tx.txid)
@ -245,7 +344,7 @@ div.tab-content
div.card-body.px-2.px-md-3
//pre
// code(class="json bg-light") #{JSON.stringify(result.txInputsByTransaction[tx.txid], null, 4)}
// code.json.bg-light #{JSON.stringify(result.txInputsByTransaction[tx.txid], null, 4)}
if (true)
- var txInputs = result.txInputsByTransaction[tx.txid];
- var blockHeight = result.getblock.height;
@ -253,7 +352,7 @@ div.tab-content
include ./transaction-io-details.pug
//pre
// code(class="json bg-light") #{JSON.stringify(tx, null, 4)}
// code.json.bg-light #{JSON.stringify(tx, null, 4)}
if (!crawlerBot && txCount > limit)
- var pageNumber = offset / limit + 1;
@ -273,18 +372,27 @@ div.tab-content
- var blockDetails = JSON.parse(JSON.stringify(result.getblock));
- blockDetails.tx = "See 'Transaction IDs'";
ul(class='nav nav-pills mb-3')
ul.nav.nav-pills.mb-3
li.nav-item
a(data-toggle="tab", href="#tab-json-block-summary", class="nav-link active", role="tab") Block Summary
a.nav-link.active(data-toggle="tab", href="#tab-json-block-summary", role="tab") Block Summary
li.nav-item
a(data-toggle="tab", href="#tab-json-tx-ids", class="nav-link", role="tab") Transaction IDs
a.nav-link(data-toggle="tab", href="#tab-json-tx-ids", role="tab") Transaction IDs
if (result.blockstats)
li.nav-item
a.nav-link(data-toggle="tab", href="#tab-json-blockstats", role="tab") Block Stats
div.tab-content
div(id="tab-json-block-summary", class="tab-pane active", role="tabpanel")
div.tab-pane.active(id="tab-json-block-summary", role="tabpanel")
pre
code(class="json bg-light") #{JSON.stringify(blockDetails, null, 4)}
code.json.bg-light #{JSON.stringify(blockDetails, null, 4)}
div(id="tab-json-tx-ids", class="tab-pane", role="tabpanel")
div.tab-pane(id="tab-json-tx-ids", role="tabpanel")
pre
code(class="json bg-light") #{JSON.stringify(result.getblock.tx, null, 4)}
code.json.bg-light #{JSON.stringify(result.getblock.tx, null, 4)}
if (result.blockstats)
div.tab-pane(id="tab-json-blockstats", role="tabpanel")
pre
code.json.bg-light #{JSON.stringify(result.blockstats, null, 4)}

5
views/includes/debug-overrides.pug

@ -3,4 +3,7 @@
// debug as if we're in performance protection mode (which means we don't calculate UTXO set details)
//- utxoSetSummary = null;
//- utxoSetSummaryPending = false;
//- utxoSetSummaryPending = false;
// debug as if we don't have result.blockstats (applies to block pages when node version < 0.17.0)
//- result.blockstats = null;
Loading…
Cancel
Save