Browse Source

Working toward medium-size update v1.2.0:

Changelog additions:
* Optional querying of UTXO set summary
    * Note: this is disabled by default to protect slow nodes. Set 'BTCEXP_SLOW_DEVICE_MODE' to false in your `.env` file to enjoy this feature.
* More data in homepage "Network Summary":
    * Fee estimates (estimatesmartfee) for 1, 6, 144, 1008 blocks
    * Hashrate estimate for 1+7 days
    * New item for 'Chain Rewrite Days', using 7day hashrate
    * New data based on optional UTXO set summary (see note above):
        * UTXO set size
        * Total coins in circulation
        * Market cap
* Tweaks to data in blocks lists:
    * Simpler timestamp formatting for easy reading
    * Include "Time-to-Mine" (TTM) for each block (with green/red highlighting for "fast"/"slow" (<5min/>15min) blocks)
    * Display average fee in sat/vB
    * Add total fees display
    * Demote display of "block size" value to hover
    * Show weight in kWu instead of Wu
* Zero-indexing for tx inputs/outputs (#173)
* Labels for transaction output types
* Configurable UI "sub-header" links
* Tweaked styling

- Also lots of frontend code cleanup (moving to more consistent pug-style class designations)
master
Dan Janosik 5 years ago
parent
commit
44a29e8ed9
No known key found for this signature in database GPG Key ID: C6F8CE9FFDB2CED2
  1. 5
      .env-sample
  2. 18
      CHANGELOG.md
  3. 1
      app.js
  4. 31
      app/api/coreApi.js
  5. 15
      app/api/rpcApi.js
  6. 1
      app/coins/btc.js
  7. 30
      app/config.js
  8. 77
      app/utils.js
  9. 9477
      public/css/bootstrap-dark-2.css
  10. 9477
      public/css/bootstrap-dark.css
  11. 7
      public/css/bootstrap-dark.min.css
  12. 6
      public/css/bootstrap.min.css
  13. 10
      public/css/dark-touchups.css
  14. 70
      public/css/styling.css
  15. 44
      routes/baseActionsRouter.js
  16. 182
      views/address.pug
  17. 24
      views/browser.pug
  18. 198
      views/includes/block-content.pug
  19. 142
      views/includes/blocks-list.pug
  20. 6
      views/includes/debug-overrides.pug
  21. 201
      views/includes/index-network-summary.pug
  22. 3
      views/includes/time-ago-text.pug
  23. 10
      views/includes/timestamp-human.pug
  24. 6
      views/includes/tools-card-block.pug
  25. 97
      views/includes/tools-card.pug
  26. 208
      views/includes/transaction-io-details.pug
  27. 19
      views/includes/value-display.pug
  28. 167
      views/index.pug
  29. 148
      views/layout.pug
  30. 63
      views/mempool-summary.pug
  31. 130
      views/node-status.pug
  32. 22
      views/peers.pug
  33. 8
      views/terminal.pug
  34. 225
      views/transaction.pug
  35. 4
      views/tx-stats.pug
  36. 14
      views/unconfirmed-transactions.pug

5
.env-sample

@ -59,6 +59,11 @@
# Default: false
#BTCEXP_DEMO=true
# Set to false to enable resource-intensive features, including:
# UTXO set summary querying
# (default value is true, i.e. resource-intensive features are disabled)
#BTCEXP_SLOW_DEVICE_MODE=false
# Privacy mode disables:
# Exchange-rate queries, IP-geolocation queries
# Default: false

18
CHANGELOG.md

@ -3,7 +3,25 @@
* Optional querying of UTXO set summary
* Note: this is disabled by default to protect slow nodes. Set 'BTCEXP_SLOW_DEVICE_MODE' to false in your `.env` file to enjoy this feature.
* More data in homepage "Network Summary":
* Fee estimates (estimatesmartfee) for 1, 6, 144, 1008 blocks
* Hashrate estimate for 1+7 days
* New item for 'Chain Rewrite Days', using 7day hashrate
* New data based on optional UTXO set summary (see note above):
* UTXO set size
* Total coins in circulation
* Market cap
* Tweaks to data in blocks lists:
* Simpler timestamp formatting for easy reading
* Include "Time-to-Mine" (TTM) for each block (with green/red highlighting for "fast"/"slow" (<5min/>15min) blocks)
* Display average fee in sat/vB
* Add total fees display
* Demote display of "block size" value to hover
* Show weight in kWu instead of Wu
* New tool `/mining-summary` for viewing summarized mining data from recent blocks
* Zero-indexing for tx inputs/outputs (#173)
* Labels for transaction output types
* Tweaked styling
* Updated miner configs
#### v1.1.9

1
app.js

@ -367,6 +367,7 @@ app.use(function(req, res, next) {
res.locals.config = global.config;
res.locals.coinConfig = global.coinConfig;
res.locals.exchangeRates = global.exchangeRates;
res.locals.utxoSetSummary = global.utxoSetSummary;
res.locals.utxoSetSummaryPending = global.utxoSetSummaryPending;

31
app/api/coreApi.js

@ -173,6 +173,12 @@ function getChainTxStats(blockCount) {
});
}
function getNetworkHashrate(blockCount) {
return tryCacheThenRpcApi(miscCache, "getNetworkHashrate-" + blockCount, 20 * 60 * 1000, function() {
return rpcApi.getNetworkHashrate(blockCount);
});
}
function getUtxoSetSummary() {
return tryCacheThenRpcApi(miscCache, "getUtxoSetSummary", 15 * 60 * 1000, rpcApi.getUtxoSetSummary);
}
@ -245,6 +251,28 @@ function getTxCountStats(dataPtCount, blockStart, blockEnd) {
});
}
function getSmartFeeEstimates(mode, confTargetBlockCounts) {
return new Promise(function(resolve, reject) {
var promises = [];
for (var i = 0; i < confTargetBlockCounts.length; i++) {
promises.push(getSmartFeeEstimate(mode, confTargetBlockCounts[i]));
}
Promise.all(promises).then(function(results) {
resolve(results);
}).catch(function(err) {
reject(err);
});
});
}
function getSmartFeeEstimate(mode, confTargetBlockCount) {
return tryCacheThenRpcApi(miscCache, "getSmartFeeEstimate-" + mode + "-" + confTargetBlockCount, 10 * 60 * 1000, function() {
return rpcApi.getSmartFeeEstimate(mode, confTargetBlockCount);
});
}
function getPeerSummary() {
return new Promise(function(resolve, reject) {
tryCacheThenRpcApi(miscCache, "getpeerinfo", 1000, rpcApi.getPeerInfo).then(function(getpeerinfo) {
@ -961,5 +989,8 @@ module.exports = {
getChainTxStats: getChainTxStats,
getMempoolDetails: getMempoolDetails,
getTxCountStats: getTxCountStats,
getSmartFeeEstimates: getSmartFeeEstimates,
getSmartFeeEstimate: getSmartFeeEstimate,
getUtxoSetSummary: getUtxoSetSummary,
getNetworkHashrate: getNetworkHashrate
};

15
app/api/rpcApi.js

@ -57,6 +57,18 @@ function getMempoolTxids() {
return getRpcDataWithParams({method:"getrawmempool", parameters:[false]});
}
function getSmartFeeEstimate(mode="CONSERVATIVE", confTargetBlockCount) {
return getRpcDataWithParams({method:"estimatesmartfee", parameters:[confTargetBlockCount, mode]});
}
function getNetworkHashrate(blockCount=144) {
return getRpcDataWithParams({method:"getnetworkhashps", parameters:[blockCount]});
}
function getBlockStats(hash) {
return getRpcDataWithParams({method:"getblockstats", parameters:[hash]});
}
function getUtxoSetSummary() {
return getRpcData("gettxoutsetinfo");
}
@ -133,6 +145,7 @@ function getBlockByHash(blockHash) {
getRawTransaction(block.tx[0]).then(function(tx) {
block.coinbaseTx = tx;
block.totalFees = utils.getBlockTotalFeesFromCoinbaseTxAndBlockHeight(tx, block.height);
block.subsidy = coinConfig.blockRewardFunction(block.height, global.activeBlockchain);
block.miner = utils.getMinerFromCoinbaseTx(tx);
resolve(block);
@ -348,5 +361,7 @@ module.exports = {
getAddress: getAddress,
getPeerInfo: getPeerInfo,
getChainTxStats: getChainTxStats,
getSmartFeeEstimate: getSmartFeeEstimate,
getUtxoSetSummary: getUtxoSetSummary,
getNetworkHashrate: getNetworkHashrate,
};

1
app/coins/btc.js

@ -64,6 +64,7 @@ module.exports = {
],
maxBlockWeight: 4000000,
maxBlockSize: 1000000,
maxSupply: new Decimal(20999817.31308491), // ref: https://bitcoin.stackexchange.com/a/38998
targetBlockTimeSeconds: 600,
currencyUnits:currencyUnits,
currencyUnitsByName:{"BTC":currencyUnits[0], "mBTC":currencyUnits[1], "bits":currencyUnits[2], "sat":currencyUnits[3]},

30
app/config.js

@ -140,13 +140,17 @@ module.exports = {
redisUrl:process.env.BTCEXP_REDIS_URL,
site: {
homepage:{
recentBlocksCount:10
},
blockTxPageSize:20,
addressTxPageSize:10,
txMaxInput:15,
browseBlocksPageSize:20,
browseBlocksPageSize:50,
addressPage:{
txOutputMaxDefaultDisplay:10
},
valueDisplayMaxLargeDigits: 4,
header:{
showToolsSubheader:(process.env.BTCEXP_UI_SHOW_TOOLS_SUBHEADER == "true"),
dropdowns:[
@ -160,27 +164,29 @@ module.exports = {
]
}
]
}
},
subHeaderToolsList:[0, 1, 4, 9, 6, 7], // indexes in "siteTools" below that are shown in the site "sub menu" (visible on all pages except homepage)
toolsDropdownIndexList: [0, 1, 4, 9, 3, 2, 5, 6, 7, 8],
},
credentials: credentials,
siteTools:[
{name:"Node Status", url:"/node-status", desc:"Summary of this node: version, network, uptime, etc.", fontawesome:"fas fa-broadcast-tower"},
{name:"Peers", url:"/peers", desc:"Detailed info about the peers connected to this node.", fontawesome:"fas fa-sitemap"},
/* 0 */ {name:"Node Status", url:"/node-status", desc:"Summary of this node: version, network, uptime, etc.", fontawesome:"fas fa-broadcast-tower"},
/* 1 */ {name:"Peers", url:"/peers", desc:"Detailed info about the peers connected to this node.", fontawesome:"fas fa-sitemap"},
{name:"Browse Blocks", url:"/blocks", desc:"Browse all blocks in the blockchain.", fontawesome:"fas fa-cubes"},
{name:"Transaction Stats", url:"/tx-stats", desc:"See graphs of total transaction volume and transaction rates.", fontawesome:"fas fa-chart-bar"},
/* 2 */ {name:"Browse Blocks", url:"/blocks", desc:"Browse all blocks in the blockchain.", fontawesome:"fas fa-cubes"},
/* 3 */ {name:"Transaction Stats", url:"/tx-stats", desc:"See graphs of total transaction volume and transaction rates.", fontawesome:"fas fa-chart-bar"},
{name:"Mempool Summary", url:"/mempool-summary", desc:"Detailed summary of the current mempool for this node.", fontawesome:"fas fa-clipboard-list"},
{name:"Unconfirmed Transactions", url:"/unconfirmed-tx", desc:"Browse unconfirmed/pending transactions.", fontawesome:"fas fa-unlock-alt"},
/* 4 */ {name:"Mempool Summary", url:"/mempool-summary", desc:"Detailed summary of the current mempool for this node.", fontawesome:"fas fa-clipboard-list"},
/* 5 */ {name:"Pending Transactions", url:"/unconfirmed-tx", desc:"Browse unconfirmed/pending transactions.", fontawesome:"fas fa-unlock-alt"},
{name:"RPC Browser", url:"/rpc-browser", desc:"Browse the RPC functionality of this node. See docs and execute commands.", fontawesome:"fas fa-book"},
{name:"RPC Terminal", url:"/rpc-terminal", desc:"Directly execute RPCs against this node.", fontawesome:"fas fa-terminal"},
/* 6 */ {name:"RPC Browser", url:"/rpc-browser", desc:"Browse the RPC functionality of this node. See docs and execute commands.", fontawesome:"fas fa-book"},
/* 7 */ {name:"RPC Terminal", url:"/rpc-terminal", desc:"Directly execute RPCs against this node.", fontawesome:"fas fa-terminal"},
{name:(coins[currentCoin].name + " Fun"), url:"/fun", desc:"See fun/interesting historical blockchain data.", fontawesome:"fas fa-certificate"},
/* 8 */ {name:(coins[currentCoin].name + " Fun"), url:"/fun", desc:"See fun/interesting historical blockchain data.", fontawesome:"fas fa-certificate"},
{name:"Mining Summary", url:"/mining-summary", desc:"Summary of recent data about miners.", fontawesome:"fas fa-chart-pie"},
/* 9 */ {name:"Mining Summary", url:"/mining-summary", desc:"Summary of recent data about miners.", fontawesome:"fas fa-chart-pie"},
],
donations:{

77
app/utils.js

@ -21,11 +21,11 @@ var exponentScales = [
{val:1000000000000000000000000, name:"yotta", abbreviation:"Y", exponent:"24"},
{val:1000000000000000000000, name:"zetta", abbreviation:"Z", exponent:"21"},
{val:1000000000000000000, name:"exa", abbreviation:"E", exponent:"18"},
{val:1000000000000000, name:"peta", abbreviation:"P", exponent:"15"},
{val:1000000000000, name:"tera", abbreviation:"T", exponent:"12"},
{val:1000000000, name:"giga", abbreviation:"G", exponent:"9"},
{val:1000000, name:"mega", abbreviation:"M", exponent:"6"},
{val:1000, name:"kilo", abbreviation:"K", exponent:"3"}
{val:1000000000000000, name:"peta", abbreviation:"P", exponent:"15", textDesc:"Q"},
{val:1000000000000, name:"tera", abbreviation:"T", exponent:"12", textDesc:"T"},
{val:1000000000, name:"giga", abbreviation:"G", exponent:"9", textDesc:"B"},
{val:1000000, name:"mega", abbreviation:"M", exponent:"6", textDesc:"M"},
{val:1000, name:"kilo", abbreviation:"K", exponent:"3", textDesc:"thou"}
];
var ipMemoryCache = {};
@ -166,13 +166,43 @@ function formatCurrencyAmountWithForcedDecimalPlaces(amount, formatType, forcedD
if (formatInfo.type == "native") {
dec = dec.times(formatInfo.multiplier);
return addThousandsSeparators(dec.toDecimalPlaces(decimalPlaces)) + " " + formatInfo.name;
if (forcedDecimalPlaces >= 0) {
// toFixed will keep trailing zeroes
var baseStr = addThousandsSeparators(dec.toFixed(decimalPlaces));
return {val:baseStr, currencyUnit:formatInfo.name, simpleVal:baseStr};
} else {
// toDP will strip trailing zeroes
var baseStr = addThousandsSeparators(dec.toDP(decimalPlaces));
var returnVal = {currencyUnit:formatInfo.name, simpleVal:baseStr};
// max digits in "val"
var maxValDigits = config.site.valueDisplayMaxLargeDigits;
if (baseStr.indexOf(".") == -1) {
returnVal.val = baseStr;
} else {
if (baseStr.length - baseStr.indexOf(".") - 1 > maxValDigits) {
returnVal.val = baseStr.substring(0, baseStr.indexOf(".") + maxValDigits + 1);
returnVal.lessSignificantDigits = baseStr.substring(baseStr.indexOf(".") + maxValDigits + 1);
} else {
returnVal.val = baseStr;
}
}
return returnVal;
}
} else if (formatInfo.type == "exchanged") {
if (global.exchangeRates != null && global.exchangeRates[formatInfo.multiplier] != null) {
dec = dec.times(global.exchangeRates[formatInfo.multiplier]);
return addThousandsSeparators(dec.toDecimalPlaces(decimalPlaces)) + " " + formatInfo.name;
var baseStr = addThousandsSeparators(dec.toDecimalPlaces(decimalPlaces));
return {val:baseStr, currencyUnit:formatInfo.name, simpleVal:baseStr};
} else {
return formatCurrencyAmountWithForcedDecimalPlaces(amount, coinConfig.defaultCurrencyUnit.name, forcedDecimalPlaces);
@ -233,10 +263,10 @@ function satoshisPerUnitOfActiveCurrency() {
var exchangedAmt = parseInt(dec);
if (exchangeType == "eur") {
return addThousandsSeparators(exchangedAmt) + ` ${unitName}/€`;
return {amt:addThousandsSeparators(exchangedAmt), unit:`${unitName}/€`};
} else {
return addThousandsSeparators(exchangedAmt) + ` ${unitName}/$`;
return {amt:addThousandsSeparators(exchangedAmt), unit:`${unitName}/$`};
}
}
@ -279,15 +309,37 @@ function seededRandomIntBetween(seed, min, max) {
return (min + (max - min) * rand);
}
function ellipsize(str, length) {
function ellipsize(str, length, ending="…") {
if (str.length <= length) {
return str;
} else {
return str.substring(0, length - 3) + "...";
return str.substring(0, length - ending.length) + ending;
}
}
function shortenTimeDiff(str) {
str = str.replace(" years", "y");
str = str.replace(" year", "y");
str = str.replace(" months", "m");
str = str.replace(" month", "m");
str = str.replace(" weeks", "w");
str = str.replace(" week", "w");
str = str.replace(" days", "d");
str = str.replace(" day", "d");
str = str.replace(" hours", "hr");
str = str.replace(" hour", "hr");
str = str.replace(" minutes", "min");
str = str.replace(" minute", "min");
return str;
}
function logMemoryUsage() {
var mbUsed = process.memoryUsage().heapUsed / 1024 / 1024;
mbUsed = Math.round(mbUsed * 100) / 100;
@ -644,5 +696,6 @@ module.exports = {
colorHexToHsl: colorHexToHsl,
logError: logError,
buildQrCodeUrls: buildQrCodeUrls,
ellipsize: ellipsize
ellipsize: ellipsize,
shortenTimeDiff: shortenTimeDiff
};

9477
public/css/bootstrap-dark-2.css

File diff suppressed because it is too large

9477
public/css/bootstrap-dark.css

File diff suppressed because it is too large

7
public/css/bootstrap-dark.min.css

File diff suppressed because one or more lines are too long

6
public/css/bootstrap.min.css

File diff suppressed because one or more lines are too long

10
public/css/dark-touchups.css

@ -2,6 +2,16 @@ hr {
background-color: #555555;
}
.nav-tabs .nav-link.active {
border-color: #9a9b9c #9a9b9c #0c0c0c !important;
}
.data-tag {
color: #111 !important;
background-color: #aaa !important;
border-color: #cccccc !important;
}
.hljs-attr {
color: #eeeeee;
}

70
public/css/styling.css

@ -20,8 +20,8 @@ code, .text-monospace {
font-family: "Source Code Pro", monospace !important;
}
.card-body {
padding: 1.1em;
.text-small {
font-size: 80%;
}
.details-table td {
@ -41,7 +41,23 @@ code, .text-monospace {
}
.border-dotted {
border-bottom: 1px dotted #777;
border-bottom: 1px dotted #aaa;
}
.data-tag {
color: white;
border-radius: 0.25rem;
padding: 3px 6px;
}
.mb-tiny {
margin-bottom: 3px;
}
.nav-tabs .nav-link.active {
background-color: transparent;
border-color: #dee2e6 #dee2e6 #f8f9fa;
}
pre {
@ -81,7 +97,21 @@ strong {
font-weight: 500;
}
.summary-table-label, .summary-table-content, .summary-split-table-label, .summary-split-table-content, .tx-io-label, .tx-io-content, .tx-io-desc, .tx-io-value {
.xindex-summary table tbody th, .xindex-summary table tbody td {
padding: 0;
padding-top: 0.3rem;
padding-bottom: 0.3rem;
}
.index-summary .summary-icon {
width: 20px;
}
.summary-table-label, .summary-table-content, .summary-split-table-label, .summary-split-table-content {
position: relative;
width: 100%;
min-height: 1px;
@ -90,7 +120,17 @@ strong {
margin-bottom: 5px;
}
.summary-table-label, .summary-split-table-label, .tx-io-label {
.tx-io-label, .tx-io-content, .tx-io-desc, .tx-io-value {
position: relative;
width: 100%;
min-height: 1px;
padding-right: 0px;
padding-left: 0px;
margin-bottom: 2px;
float: left;
}
.summary-table-label, .summary-split-table-label {
font-weight: bold;
}
@ -144,8 +184,8 @@ strong {
.tx-io-label { max-width: 8%; }
.tx-io-content { max-width: 92%; }
.tx-io-desc { max-width: 60%; }
.tx-io-value { max-width: 40%; text-align: right; padding-right: 25px; }
.tx-io-desc { max-width: 65%; }
.tx-io-value { max-width: 35%; text-align: right; }
}
@media (min-width: 992px) {
@ -170,7 +210,7 @@ strong {
.tx-io-label { max-width: 11%; }
.tx-io-content { max-width: 89%; }
.tx-io-desc { max-width: 60%; }
.tx-io-value { max-width: 40%; text-align: right; padding-right: 25px; }
.tx-io-value { max-width: 40%; text-align: right; }
}
@media (min-width: 1200px) {
@ -196,10 +236,10 @@ strong {
margin-bottom: 5px;
}
.tx-io-label { max-width: 9.5%; }
.tx-io-content { max-width: 90.5%; }
.tx-io-desc { max-width: 61%; }
.tx-io-value { max-width: 39%; text-align: right; padding-right: 25px; }
.tx-io-label { max-width: 11%; }
.tx-io-content { max-width: 89%; }
.tx-io-desc { max-width: 69%; }
.tx-io-value { max-width: 31%; text-align: right; }
}
@media (min-width: 1920px) {
@ -225,10 +265,10 @@ strong {
margin-bottom: 5px;
}
.tx-io-label { max-width: 6.5%; }
.tx-io-content { max-width: 93.5%; }
.tx-io-label { max-width: 7%; }
.tx-io-content { max-width: 93%; }
.tx-io-desc { max-width: 70%; }
.tx-io-value { max-width: 30%; text-align: right; padding-right: 25px; }
.tx-io-value { max-width: 30%; text-align: right; }
}

44
routes/baseActionsRouter.js

@ -44,17 +44,37 @@ router.get("/", function(req, res, next) {
res.locals.homepage = true;
// variables used by blocks-list.pug
res.locals.offset = 0;
res.locals.sort = "desc";
var feeConfTargets = [1, 6, 144, 1008];
res.locals.feeConfTargets = feeConfTargets;
var promises = [];
// promiseResults[0]
promises.push(coreApi.getMempoolInfo());
// promiseResults[1]
promises.push(coreApi.getMiningInfo());
// promiseResults[2]
promises.push(coreApi.getSmartFeeEstimates("CONSERVATIVE", feeConfTargets));
// promiseResults[3] and [4]
promises.push(coreApi.getNetworkHashrate(144));
promises.push(coreApi.getNetworkHashrate(1008));
coreApi.getBlockchainInfo().then(function(getblockchaininfo) {
res.locals.getblockchaininfo = getblockchaininfo;
if (getblockchaininfo.chain !== 'regtest') {
var targetBlocksPerDay = 24 * 60 * 60 / global.coinConfig.targetBlockTimeSeconds;
// promiseResults[4] (if not regtest)
promises.push(coreApi.getTxCountStats(targetBlocksPerDay / 4, -targetBlocksPerDay, "latest"));
var chainTxStatsIntervals = [ targetBlocksPerDay, targetBlocksPerDay * 7, targetBlocksPerDay * 30, targetBlocksPerDay * 365 ]
@ -71,7 +91,8 @@ router.get("/", function(req, res, next) {
var blockHeights = [];
if (getblockchaininfo.blocks) {
for (var i = 0; i < 10; i++) {
// +1 to page size here so we have the next block to calculate T.T.M.
for (var i = 0; i < (config.site.homepage.recentBlocksCount + 1); i++) {
blockHeights.push(getblockchaininfo.blocks - i);
}
} else if (global.activeBlockchain == "regtest") {
@ -91,12 +112,25 @@ router.get("/", function(req, res, next) {
res.locals.mempoolInfo = promiseResults[0];
res.locals.miningInfo = promiseResults[1];
var rawSmartFeeEsimates = promiseResults[2];
var smartFeeEsimates = {};
for (var i = 0; i < feeConfTargets.length; i++) {
smartFeeEsimates[feeConfTargets[i]] = parseInt(new Decimal(rawSmartFeeEsimates[i].feerate).times(coinConfig.baseCurrencyUnit.multiplier).dividedBy(1000));
}
res.locals.smartFeeEsimates = smartFeeEsimates;
res.locals.hashrate1d = promiseResults[3];
res.locals.hashrate7d = promiseResults[4];
if (getblockchaininfo.chain !== 'regtest') {
res.locals.txStats = promiseResults[2];
res.locals.txStats = promiseResults[5];
var chainTxStats = [];
for (var i = 0; i < res.locals.chainTxStatsLabels.length; i++) {
chainTxStats.push(promiseResults[i + 3]);
chainTxStats.push(promiseResults[i + 6]);
}
res.locals.chainTxStats = chainTxStats;
@ -309,13 +343,13 @@ router.get("/blocks", function(req, res, next) {
var blockHeights = [];
if (sort == "desc") {
for (var i = (getblockchaininfo.blocks - offset); i > (getblockchaininfo.blocks - offset - limit); i--) {
for (var i = (getblockchaininfo.blocks - offset); i > (getblockchaininfo.blocks - offset - limit - 1); i--) {
if (i >= 0) {
blockHeights.push(i);
}
}
} else {
for (var i = offset; i < (offset + limit); i++) {
for (var i = offset - 1; i < (offset + limit); i++) {
if (i >= 0) {
blockHeights.push(i);
}

182
views/address.pug

@ -37,15 +37,6 @@ block content
span (see the configuration
a(href=coinConfig.miningPoolsConfigUrls[0]) here
span )
else if (global.specialAddresses[address] && global.specialAddresses[address].type == "donation")
div(class="alert alert-primary shadow-sm", style="padding-bottom: 0;")
div(class="float-left", style="width: 50px; height: 50px; font-size: 18px;")
i(class="fas fa-certificate fa-2x", style="margin-top: 10px;")
h4(class="alert-heading h6 font-weight-bold") #{coinConfig.name} Fun
p
span This is the #{coinConfig.ticker} donation address to support development of this tool. All support is appreciated!
else if (global.specialAddresses[address] && global.specialAddresses[address].type == "fun")
div(class="alert alert-primary shadow-sm", style="padding-bottom: 0;")
@ -77,14 +68,14 @@ block content
pre
code.json.bg-light #{JSON.stringify(addressObj, null, 4)}
ul(class='nav nav-tabs mb-3')
li(class="nav-item")
a(data-toggle="tab", href="#tab-details", class="nav-link active", role="tab") Details
li(class="nav-item")
a(data-toggle="tab", href="#tab-json", class="nav-link", role="tab") JSON
ul.nav.nav-tabs.mb-3
li.nav-item
a.nav-link.active(data-toggle="tab", href="#tab-details", role="tab") Details
li.nav-item
a.nav-link(data-toggle="tab", href="#tab-json", role="tab") JSON
div(class="tab-content")
div.tab-content
div(id="tab-details", class="tab-pane active", role="tabpanel")
if (false && config.electrumXServers && config.electrumXServers.length > 0)
@ -95,44 +86,45 @@ block content
a(href="/changeSetting?name=hideElectrumTrustWarnings&value=true", class="close", aria-label="Close", style="text-decoration: none;")
span(aria-hidden="true") &times;
div(class="card mb-3 shadow-sm")
div(class="card-header")
span(class="h6") Summary
div.card.shadow-sm.mb-3
div.card-body
h3.h6 Summary
if (config.addressApi)
small.text-muted.border-dotted.ml-2(title=`Some details for this address were queried from ${config.addressApi}` data-toggle="tooltip") Trust Note
div(class="card-body")
div(class="row")
div(class="col-md-6")
hr
div.row
div.col-md-6
if (addressObj.hash)
div(class="row")
div(class="summary-split-table-label") Hash 160
div(class="summary-split-table-content text-monospace") #{addressObj.hash.toString("hex")}
div.row
div.summary-split-table-label Hash 160
div.summary-split-table-content.text-monospace #{addressObj.hash.toString("hex")}
if (result.validateaddress.scriptPubKey)
div(class="row")
div(class="summary-split-table-label") Script Public Key
div(class="summary-split-table-content text-monospace") #{result.validateaddress.scriptPubKey}
div.row
div.summary-split-table-label Script Public Key
div.summary-split-table-content.text-monospace #{result.validateaddress.scriptPubKey}
if (addressObj.hasOwnProperty("version"))
div(class="row")
div(class="summary-split-table-label") Version
div(class="summary-split-table-content text-monospace") #{addressObj.version}
div.row
div.summary-split-table-label Version
div.summary-split-table-content.text-monospace #{addressObj.version}
if (result.validateaddress.hasOwnProperty("witness_version"))
div(class="row")
div(class="summary-split-table-label") Witness Version
div(class="summary-split-table-content text-monospace") #{result.validateaddress.witness_version}
div.row
div.summary-split-table-label Witness Version
div.summary-split-table-content.text-monospace #{result.validateaddress.witness_version}
if (result.validateaddress.witness_program)
div(class="row")
div(class="summary-split-table-label") Witness Program
div(class="summary-split-table-content text-monospace") #{result.validateaddress.witness_program}
div.row
div.summary-split-table-label Witness Program
div.summary-split-table-content.text-monospace #{result.validateaddress.witness_program}
if (firstSeenTransaction && firstSeenTransaction.confirmations > 0)
div(class="row")
div(class="summary-split-table-label") First Seen
div(class="summary-split-table-content text-monospace")
div.row
div.summary-split-table-label First Seen
div.summary-split-table-content.text-monospace
if (getblockchaininfo)
span Block ##{(getblockchaininfo.blocks - firstSeenTransaction.confirmations).toLocaleString()}
else
@ -150,9 +142,9 @@ block content
if (balance)
if (balance.conflictedResults)
div(class="row")
div(class="summary-split-table-label") Balance
div(class="summary-split-table-content text-monospace")
div.row
div.summary-split-table-label Balance
div.summary-split-table-content.text-monospace
span(class="text-warning") Conflicted ElectrumX Results
each item in balance.conflictedResults
@ -161,50 +153,50 @@ block content
else
div(class="row")
div(class="summary-split-table-label") Balance
div(class="summary-split-table-content text-monospace")
div.row
div.summary-split-table-label Balance
div.summary-split-table-content.text-monospace
- var currencyValue = balance.result.confirmed / coinConfig.baseCurrencyUnit.multiplier;
include includes/value-display.pug
if (balance.unconfirmed)
div(class="row")
div(class="summary-split-table-label") Unconfirmed
div(class="summary-split-table-content text-monospace")
div.row
div.summary-split-table-label Unconfirmed
div.summary-split-table-content.text-monospace
- var currencyValue = balance.unconfirmed / coinConfig.baseCurrencyUnit.multiplier;
include includes/value-display.pug
if (addressDetails && addressDetails.balanceSat)
div(class="row")
div(class="summary-split-table-label") Balance
div(class="summary-split-table-content text-monospace")
div.row
div.summary-split-table-label Balance
div.summary-split-table-content.text-monospace
- var currencyValue = new Decimal(addressDetails.balanceSat).dividedBy(coinConfig.baseCurrencyUnit.multiplier);
include includes/value-display.pug
if (addressDetails && addressDetails.txCount)
div(class="row")
div(class="summary-split-table-label") Transactions
div(class="summary-split-table-content text-monospace") #{addressDetails.txCount.toLocaleString()}
div.row
div.summary-split-table-label Transactions
div.summary-split-table-content.text-monospace #{addressDetails.txCount.toLocaleString()}
div(class="row")
div(class="summary-split-table-label") QR Code
div(class="summary-split-table-content text-monospace")
div.row
div.summary-split-table-label QR Code
div.summary-split-table-content.text-monospace
img(src=addressQrCodeUrl, alt=address, style="border: solid 1px #ccc;")
div(class="col-md-6")
div.col-md-6
if (electrumScripthash)
div(class="row")
div(class="summary-split-table-label") Scripthash
div(class="summary-split-table-content text-monospace") #{electrumScripthash}
div.row
div.summary-split-table-label Scripthash
div.summary-split-table-content.text-monospace #{electrumScripthash}
- var x = result.validateaddress;
- var flagNames = ["Is Valid?", "Is Script?", "Is Witness?", "Is Mine?", "Is Watch-Only?"];
- var flags = [x.isvalid, x.isscript, x.iswitness, x.ismine, x.iswatchonly];
each flagName, index in flagNames
div(class="row")
div(class="summary-split-table-label") #{flagName}
div(class="summary-split-table-content text-monospace")
div.row
div.summary-split-table-label #{flagName}
div.summary-split-table-content.text-monospace
if (flags[index])
i(class="fas fa-check text-success")
else
@ -212,9 +204,9 @@ block content
if (false)
div(class="card mb-3 shadow-sm")
div(class="card-header")
div.card-header
span(class="h6") Flags
div(class="card-body")
div.card-body
div(class="table-responsive")
table(class="table text-center")
thead
@ -236,10 +228,11 @@ block content
else
i(class="fas fa-times text-warning")
div(class="card mb-3 shadow-sm")
div(class="card-header clearfix")
div(class="float-left")
span(class="h6")
div.card.shadow-sm.mb-3
div.card-body
div.clearfix
div.float-left
h3.h6
if (addressDetails && addressDetails.txCount)
if (addressDetails.txCount == 1)
span 1 Transaction
@ -254,7 +247,6 @@ block content
else
small.text-muted.border-dotted.ml-2(title=`The list of transaction IDs for this address was queried from ${config.addressApi}` data-toggle="tooltip") Trust Note
if (!crawlerBot && txids && txids.length > 1 && addressApiSupport.sortDesc && addressApiSupport.sortAsc)
div(class="float-right")
a(href="#", class="pull-right dropdown-toggle", data-toggle="dropdown", aria-haspopup="true", aria-expanded="false")
@ -277,7 +269,8 @@ block content
div.float-right
span.text-muted Newest First
div(class="card-body")
hr
if (conflictedTxidResults)
div(class="alert alert-warning", style="padding-bottom: 0;")
div(class="float-left", style="width: 55px; height: 50px; font-size: 18px;")
@ -316,36 +309,27 @@ block content
each tx, txIndex in transactions
//pre
// code.json.bg-light #{JSON.stringify(tx, null, 4)}
div(class=("xcard bg-light rounded shadow-sm " + ((txIndex < (transactions.length - 1) || txids.length > limit) ? "mb-4" : "")))
div(class="card-header text-monospace clearfix")
div(class="float-left", style="margin-right: 0px;")
div.card.shadow-sm(class=((txIndex < (transactions.length - 1) || txids.length > limit) ? "mb-3" : ""))
div.card-header.text-monospace.clearfix
div.float-left.mr-0
if (sort == "desc")
span ##{(addressDetails.txCount - offset - txIndex).toLocaleString()}
else
span ##{(offset + txIndex + 1).toLocaleString()}
span &ndash;
div(class="row")
div(class="col-md-6")
div.row
div.col-md-8
if (tx && tx.txid)
a(href=("/tx/" + tx.txid)) #{tx.txid}
if (global.specialTransactions && global.specialTransactions[tx.txid])
span
a(data-toggle="tooltip", title=(coinConfig.name + " Fun! See transaction for details"))
i(class="fas fa-certificate text-primary")
i.fas.fa-certificate.text-primary
div(class="col-md-6")
div(class="text-md-right")
if (tx.time)
span #{moment.utc(new Date(tx["time"] * 1000)).format("Y-MM-DD HH:mm:ss")} utc
- var timeAgoTime = tx.time;
include includes/time-ago.pug
else
span(class="text-danger") Unconfirmed
br
div(class="col")
if (addrGainsByTx[tx.txid])
- var currencyValue = addrGainsByTx[tx.txid];
span(class="text-success") +
@ -359,7 +343,23 @@ block content
span(class="text-danger") -
include includes/value-display.pug
div(class="card-body")
div.col-md-4
div.text-md-right
if (tx.time)
- var timestampHuman = tx.time;
include includes/timestamp-human.pug
br
- var timeAgoTime = tx.time;
small.text-muted (
include includes/time-ago-text.pug
span )
else
span.text-danger Unconfirmed
div.card-body
if (true)
- var txInputs = txInputsByTransaction[tx.txid];
- var blockHeight = blockHeightsByTxid[tx.txid];
@ -394,10 +394,10 @@ block content
div(id="tab-json", class="tab-pane", role="tabpanel")
div(class="highlight")
div.highlight
h4 validateaddress
pre
code(class="json bg-light", data-lang="json") #{JSON.stringify(result.validateaddress, null, 4)}
code.json.bg-light(data-lang="json") #{JSON.stringify(result.validateaddress, null, 4)}
if (addressDetails)
h4 addressDetails

24
views/browser.pug

@ -14,31 +14,31 @@ block content
hr
if (gethelp)
div(class="row")
div.row
div(class="col-md-9")
if (methodhelp)
div(class="row")
div(class="col")
div.row
div.col
h4(style="display: inline-block;")
span Command:
span.font-weight-light.text-monospace #{method}
div(class="col")
div.col
a(href=("https://bitcoin.org/en/developer-reference#" + method), class="btn btn-primary float-md-right") See developer docs &raquo;
hr
ul(class='nav nav-tabs mb-3')
li(class="nav-item")
a(data-toggle="tab", href="#tab-execute", class="nav-link active", role="tab") Execute
li(class="nav-item")
a(data-toggle="tab", href="#tab-help-content", class="nav-link", role="tab") Help Content
ul.nav.nav-tabs.mb-3
li.nav-item
a.nav-link.active(data-toggle="tab", href="#tab-execute", role="tab") Execute
li.nav-item
a.nav-link(data-toggle="tab", href="#tab-help-content", role="tab") Help Content
if (methodhelp.args && methodhelp.args.length > 0)
li(class="nav-item")
li.nav-item
a(data-toggle="tab", href="#tab-parsed-args", class="nav-link", role="tab") Arguments Details
div(class="tab-content")
div.tab-content
div(id="tab-execute", class="tab-pane active pb-3", role="tabpanel")
if (methodResult)
div(class="mt-4")
@ -88,7 +88,7 @@ block content
input(id=("arg_" + argX.name), type="text", name=("args[" + index + "]"), placeholder=argX.name, class="form-control", value=(valX ? valX : ""))
if (!methodhelp.args || methodhelp.args.length == 0)
span(class="text-muted") None
span.text-muted None
hr

198
views/includes/block-content.pug

@ -1,12 +1,12 @@
ul(class='nav nav-tabs mb-3')
li(class="nav-item")
a(data-toggle="tab", href="#tab-details", class="nav-link active", role="tab") Details
li(class="nav-item")
a(data-toggle="tab", href="#tab-json", class="nav-link", role="tab") JSON
ul.nav.nav-tabs.mb-3
li.nav-item
a.nav-link.active(data-toggle="tab", href="#tab-details", role="tab") Details
li.nav-item
a.nav-link(data-toggle="tab", href="#tab-json", role="tab") JSON
- var txCount = result.getblock.tx.length;
div(class="tab-content")
div.tab-content
div(id="tab-details", class="tab-pane active", role="tabpanel")
if (global.specialBlocks && global.specialBlocks[result.getblock.hash])
div(class="alert alert-primary shadow-sm", style="padding-bottom: 0;")
@ -32,15 +32,16 @@ div(class="tab-content")
span
a(href=sbInfo.referenceUrl) Read more
div(class="card mb-3 shadow-sm")
div(class="card-header")
span(class="h6") Summary
div(class="card-body")
div(class="row")
div.card.shadow-sm.mb-3
div.card-body.px-2.px-md-3
h3.h6 Summary
hr
div.row
div(class="col-md-6")
div(class="row")
div(class="summary-split-table-label") Previous Block
div(class="summary-split-table-content monospace")
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
@ -49,29 +50,41 @@ div(class="tab-content")
else if (result.getblock.hash == genesisBlockHash)
span None (genesis block)
div(class="row")
div(class="summary-split-table-label") Timestamp
div(class="summary-split-table-content monospace")
span #{moment.utc(new Date(result.getblock.time * 1000)).format("Y-MM-DD HH:mm:ss")} utc
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;
include ./time-ago.pug
span.text-muted (
include ./time-ago-text.pug
span )
div(class="row")
div(class="summary-split-table-label") Transactions
div(class="summary-split-table-content monospace") #{result.getblock.tx.length.toLocaleString()}
div.row
div.summary-split-table-label Transactions
div.summary-split-table-content.text-monospace #{result.getblock.tx.length.toLocaleString()}
div(class="row")
div(class="summary-split-table-label") Total Fees
div(class="summary-split-table-content monospace")
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(class="row")
div(class="summary-split-table-label") Average Fee
div(class="summary-split-table-content monospace")
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
span )
- var blockRewardMax = coinConfig.blockRewardFunction(result.getblock.height, global.activeBlockchain);
- var coinbaseTxTotalOutputValue = new Decimal(0);
@ -79,36 +92,36 @@ div(class="tab-content")
- coinbaseTxTotalOutputValue = coinbaseTxTotalOutputValue.plus(new Decimal(vout.value));
if (parseFloat(coinbaseTxTotalOutputValue) < blockRewardMax)
div(class="row")
div(class="summary-split-table-label")
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 monospace text-danger")
div(class="summary-split-table-content text-monospace text-danger")
- var currencyValue = new Decimal(blockRewardMax).minus(coinbaseTxTotalOutputValue);
include ./value-display.pug
if (result.getblock.weight)
div(class="row")
div(class="summary-split-table-label") Weight
div(class="summary-split-table-content monospace")
div.row
div.summary-split-table-label Weight
div.summary-split-table-content.text-monospace
span(style="") #{result.getblock.weight.toLocaleString()} wu
span(class="text-muted") (#{new Decimal(100 * result.getblock.weight / coinConfig.maxBlockWeight).toDecimalPlaces(2)}% full)
span.text-muted (#{new Decimal(100 * result.getblock.weight / coinConfig.maxBlockWeight).toDecimalPlaces(2)}% full)
- var fullPercent = new Decimal(100 * result.getblock.weight / coinConfig.maxBlockWeight).toDecimalPlaces(0);
div(class="row")
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(class="row")
div(class="summary-split-table-label") Size
div(class="summary-split-table-content monospace") #{result.getblock.size.toLocaleString()} bytes
div.row
div.summary-split-table-label Size
div.summary-split-table-content.text-monospace #{result.getblock.size.toLocaleString()} bytes
div(class="row")
div(class="summary-split-table-label") Confirmations
div(class="summary-split-table-content monospace")
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.")
@ -120,9 +133,9 @@ div(class="tab-content")
div(class="col-md-6")
div(class="row")
div(class="summary-split-table-label") Next Block
div(class="summary-split-table-content monospace")
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
@ -130,9 +143,9 @@ div(class="tab-content")
else
span None (latest block)
div(class="row")
div(class="summary-split-table-label") Difficulty
div(class="summary-split-table-content monospace")
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")
@ -140,26 +153,26 @@ div(class="tab-content")
span x 10
sup #{difficultyData[1].exponent}
div(class="row")
div(class="summary-split-table-label") Version
div(class="summary-split-table-content monospace") 0x#{result.getblock.versionHex}
span(class="text-muted") (decimal: #{result.getblock.version})
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(class="row")
div(class="summary-split-table-label") Nonce
div(class="summary-split-table-content monospace") #{result.getblock.nonce}
div.row
div.summary-split-table-label Nonce
div.summary-split-table-content.text-monospace #{result.getblock.nonce}
div(class="row")
div(class="summary-split-table-label") Bits
div(class="summary-split-table-content monospace") #{result.getblock.bits}
div.row
div.summary-split-table-label Bits
div.summary-split-table-content.text-monospace #{result.getblock.bits}
div(class="row")
div(class="summary-split-table-label") Merkle Root
div(class="summary-split-table-content monospace") #{result.getblock.merkleroot}
div.row
div.summary-split-table-label Merkle Root
div.summary-split-table-content.text-monospace #{result.getblock.merkleroot}
div(class="row")
div(class="summary-split-table-label") Chainwork
div(class="summary-split-table-content monospace")
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]}
@ -167,56 +180,59 @@ div(class="tab-content")
sup #{chainworkData[1].exponent}
span hashes
span(class="text-muted") (#{result.getblock.chainwork.replace(/^0+/, '')})
span.text-muted (#{result.getblock.chainwork.replace(/^0+/, '')})
if (result.getblock.miner)
div(class="row")
div(class="summary-split-table-label") Miner
div(class="summary-split-table-content monospace")
div.row
div.summary-split-table-label Miner
div.summary-split-table-content.text-monospace.mb-0
if (result.getblock.miner)
span #{result.getblock.miner.name}
if (result.getblock.miner.identifiedBy)
span(data-toggle="tooltip", title=("Identified by: " + result.getblock.miner.identifiedBy))
i(class="fas fa-info-circle")
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
span ?
span(data-toggle="tooltip", title="Unable to identify miner")
i(class="fas fa-info-circle")
div(class="card mb-3 shadow-sm")
div(class="card-header")
div(class="row")
div(class="col-md-4")
h2(class="h6 mb-0") #{txCount.toLocaleString()}
div.card.shadow-sm.mb-3
div.card-body.px-2.px-md-3
div.row
div.col-md-4
h2.h6.mb-0 #{txCount.toLocaleString()}
if (txCount == 1)
span Transaction
else
span Transactions
if (!config.demoSite && !crawlerBot && txCount > 20)
if (false || (!config.demoSite && !crawlerBot && txCount > 20))
div(class="col-md-8 text-right")
span(class="mr-2") Show
div(class="btn-group", role="group")
a(href=(paginationBaseUrl + "?limit=20"), class="btn btn-sm btn-primary px-2", class=((limit == 20 && txCount > limit) ? "active" : false)) 20
small.mr-1.text-muted Show
div.btn-group(role="group")
a.btn.btn-sm.btn-primary.px-2.py-0(href=(paginationBaseUrl + "?limit=20"), class=((limit == 20 && txCount > limit) ? "active" : false)) 20
if (txCount > 50)
a(href=(paginationBaseUrl + "?limit=50"), class="btn btn-sm btn-primary px-2", class=(limit == 50 ? "active" : false)) 50
a.btn.btn-sm.btn-primary.px-2.py-0(href=(paginationBaseUrl + "?limit=50") class=(limit == 50 ? "active" : false)) 50
if (txCount > 100)
a(href=(paginationBaseUrl + "?limit=100"), class="btn btn-sm btn-primary px-2", class=(limit == 100 ? "active" : false)) 100
a.btn.btn-sm.btn-primary.px-2.py-0(href=(paginationBaseUrl + "?limit=100"), class=(limit == 100 ? "active" : false)) 100
a(href=(paginationBaseUrl + "?limit=3000"), class="btn btn-sm btn-primary px-2", class=(limit >= txCount ? "active" : false)) all
a.btn.btn-sm.btn-primary.px-2.py-0(href=(paginationBaseUrl + "?limit=3000"), class=(limit >= txCount ? "active" : false)) all
hr
- var fontawesomeInputName = "sign-in-alt";
- var fontawesomeOutputName = "sign-out-alt";
div(class="card-body")
div
each tx, txIndex in result.transactions
//pre
// code(class="json bg-light") #{JSON.stringify(tx, null, 4)}
div(class=("xcard bg-light rounded shadow-sm " + ((txIndex < (result.transactions.length - 1) || txCount > limit) ? "mb-4" : "")))
div(class="card-header monospace")
div.card.shadow-sm(class=(" " + ((txIndex < (result.transactions.length - 1) || txCount > limit) ? "mb-3" : "")))
div.card-header.text-monospace
if (tx && tx.txid)
span ##{(txIndex + offset + 1).toLocaleString()}
span &ndash;
@ -225,9 +241,9 @@ div(class="tab-content")
if (global.specialTransactions && global.specialTransactions[tx.txid])
span
a(data-toggle="tooltip", title=(coinConfig.name + " Fun! See transaction for details"))
i(class="fas fa-certificate text-primary")
i.fas.fa-certificate.text-primary
div(class="card-body px-lg-3 px-2")
div.card-body.px-2.px-md-3
//pre
// code(class="json bg-light") #{JSON.stringify(result.txInputsByTransaction[tx.txid], null, 4)}
if (true)
@ -258,12 +274,12 @@ div(class="tab-content")
- blockDetails.tx = "See 'Transaction IDs'";
ul(class='nav nav-pills mb-3')
li(class="nav-item")
li.nav-item
a(data-toggle="tab", href="#tab-json-block-summary", class="nav-link active", role="tab") Block Summary
li(class="nav-item")
li.nav-item
a(data-toggle="tab", href="#tab-json-tx-ids", class="nav-link", role="tab") Transaction IDs
div(class="tab-content")
div.tab-content
div(id="tab-json-block-summary", class="tab-pane active", role="tabpanel")
pre
code(class="json bg-light") #{JSON.stringify(blockDetails, null, 4)}

142
views/includes/blocks-list.pug

@ -1,54 +1,142 @@
div(class="table-responsive")
table(class="table table-striped mb-0")
div.table-responsive
table.table.table-striped.mb-0
thead
tr
//th
th(class="data-header") Height
th(class="data-header") Timestamp (utc)
th(class="data-header text-right") Age
th(class="data-header") Miner
th(class="data-header text-right") Transactions
th(class="data-header text-right") Average Fee
th(class="data-header text-right") Size (bytes)
th
th.data-header.text-right Height
th.data-header.text-right Date
small (utc)
th.data-header.text-right Age
th.data-header.text-right
span.border-dotted(title="Time To Mine - The time it took to mine this block after the previous block. 'Fast' blocks (mined in < 5min) are shown in green; 'slow' blocks (mined in > 15min) are shown in red.", data-toggle="tooltip") T.T.M.
th.data-header.text-right Miner
th.data-header.text-right Transactions
th.data-header.text-right Avg Fee
small (sat/vB)
th.data-header.text-right Total Fees
//th.data-header.text-right Size (kB)
if (blocks && blocks.length > 0 && blocks[0].weight)
th(class="data-header text-right") Weight (wu)
th.data-header.text-right Weight
small (kWu)
else
th.data-header.text-right Size
small (kB)
tbody
each block, blockIndex in blocks
if (block)
if (block && ((sort == "desc" && blockIndex < blocks.length - 1) || (sort == "asc" && (block.height == 0 || blockIndex > 0))))
tr
td(class="data-cell monospace")
a(href=("/block-height/" + block.height)) #{block.height.toLocaleString()}
td
if (sort == "desc")
small.text-muted #{(blockIndex + offset + 1).toLocaleString()}
else
small.text-muted #{(blockIndex + offset).toLocaleString()}
td.data-cell.text-monospace.text-right
if (global.specialBlocks && global.specialBlocks[block.hash])
span
a(data-toggle="tooltip", title=(coinConfig.name + " Fun! See block for details"))
i(class="fas fa-certificate text-primary")
td(class="data-cell monospace") #{moment.utc(new Date(parseInt(block.time) * 1000)).format("Y-MM-DD HH:mm:ss")}
i.fas.fa-certificate.text-primary.mr-1
a(href=("/block-height/" + block.height)) #{block.height.toLocaleString()}
- var timeAgoTime = moment.utc(new Date()).diff(moment.utc(new Date(parseInt(block.time) * 1000)));
- var timeAgo = moment.duration(timeAgoTime);
- var timeDiff = null;
if (sort == "asc")
if (blockIndex > 0)
- var timeDiff = moment.duration(moment.utc(new Date(parseInt(block.time) * 1000)).diff(moment.utc(new Date(parseInt(blocks[blockIndex - 1].time) * 1000))));
else
if (blockIndex < blocks.length - 1)
- var timeDiff = moment.duration(moment.utc(new Date(parseInt(block.time) * 1000)).diff(moment.utc(new Date(parseInt(blocks[blockIndex + 1].time) * 1000))));
td.data-cell.text-monospace.text-right
- var timestampHuman = block.time;
include timestamp-human.pug
td.data-cell.text-monospace.text-right
if (sort != "asc" && blockIndex == 0 && offset == 0 && timeAgoTime > (15 * 60 * 1000))
span.text-danger.border-dotted(title="It's been > 15 min since this latest block.", data-toggle="tooltip") #{utils.shortenTimeDiff(timeAgo.format())}
else
span #{utils.shortenTimeDiff(timeAgo.format())}
- var timeAgo = moment.duration(moment.utc(new Date()).diff(moment.utc(new Date(parseInt(block.time) * 1000))));
td(class="data-cell monospace text-right") #{timeAgo.format()}
td(class="data-cell monospace")
td.data-cell.text-monospace.text-right
if (timeDiff)
- var colorClass = "text-muted";
if (timeDiff < 300000)
- var colorClass = "text-success";
if (timeDiff > 900000)
- var colorClass = "text-danger";
span.font-weight-light(class=colorClass) #{utils.shortenTimeDiff(timeDiff.format())}
else
if (block.height == 0)
small.border-dotted.text-muted(title="Not applicable: genesis block has no previous block to compare to.", data-toggle="tooltip") N/A (genesis)
else
span.font-weight-light.text-muted -
td.data-cell.text-monospace.text-right
if (block.miner && block.miner.name)
span(data-toggle="tooltip", title=("Identified by: " + block.miner.identifiedBy), class="rounded bg-primary text-white px-2 py-1") #{block.miner.name}
small.rounded.bg-primary.text-white.px-2.py-1(data-toggle="tooltip", title=("Identified by: " + block.miner.identifiedBy)) #{utils.ellipsize(block.miner.name, 10)}
else
span ?
td(class="data-cell monospace text-right") #{block.tx.length.toLocaleString()}
td.data-cell.text-monospace.text-right #{block.tx.length.toLocaleString()}
td.data-cell.text-monospace.text-right
- var currencyValue = new Decimal(block.totalFees).dividedBy(block.strippedsize).times(coinConfig.baseCurrencyUnit.multiplier).toDecimalPlaces(1);
span #{currencyValue}
td(class="data-cell monospace text-right")
- var currencyValue = new Decimal(block.totalFees).dividedBy(block.tx.length);
// idea: show typical tx fee, maybe also optimized fee if not segwit
if (false)
- var feeEstimateVal = currencyValue.times(166).dividedBy(coinConfig.baseCurrencyUnit.multiplier);
span.border-dotted(title=`Value: ${feeEstimateVal}`, data-toggle="tooltip") #{currencyValue}
td.data-cell.text-monospace.text-right
- var currencyValue = new Decimal(block.totalFees);
- var currencyValueDecimals = 3;
include ./value-display.pug
td(class="data-cell monospace text-right") #{block.size.toLocaleString()}
if (false)
td.data-cell.text-monospace.text-right
- var bSizeK = parseInt(block.size / 1000);
span #{bSizeK.toLocaleString()}
if (blocks && blocks.length > 0 && blocks[0].weight)
td(class="data-cell monospace text-right")
td.data-cell.text-monospace.text-right
- var bWeightK = parseInt(block.weight / 1000);
- var fullPercent = new Decimal(100 * block.weight / coinConfig.maxBlockWeight).toDecimalPlaces(1);
span #{block.weight.toLocaleString()}
small(class="font-weight-light text-muted") (#{fullPercent}%)
span #{bWeightK.toLocaleString()}
small.font-weight-light.text-muted (#{fullPercent}%)
- var bSizeK = parseInt(block.size / 1000);
span.ml-1(data-toggle="tooltip", title=`Size: ${bSizeK.toLocaleString()} kB`)
i.fas.fa-ellipsis-h.text-muted
div(class="progress", style="height: 4px;")
div(class="progress-bar", role="progressbar", style=("width: " + fullPercent + "%;"), aria-valuenow=parseInt(100 * block.weight / coinConfig.maxBlockWeight), aria-valuemin="0" ,aria-valuemax="100")
else
td.data-cell.text-monospace.text-right
- var bSizeK = parseInt(block.size / 1000);
- var fullPercent = new Decimal(100 * block.size / coinConfig.maxBlockSize).toDecimalPlaces(1);
span #{bSizeK.toLocaleString()}
small.font-weight-light.text-muted (#{fullPercent}%)
div(class="progress", style="height: 4px;")
div(class="progress-bar", role="progressbar", style=("width: " + fullPercent + "%;"), aria-valuenow=parseInt(100 * block.size / coinConfig.maxBlockSize), aria-valuemin="0" ,aria-valuemax="100")
- var lastBlock = block;

6
views/includes/debug-overrides.pug

@ -0,0 +1,6 @@
// debug as if we're in privacy mode (which means we don't have exchange rate data)
//- exchangeRates = null;
// debug as if we're in performance protection mode (which means we don't calculate UTXO set details)
//- utxoSetSummary = null;
//- utxoSetSummaryPending = false;

201
views/includes/index-network-summary.pug

@ -0,0 +1,201 @@
- var colClass = "col-lg-6 px-3";
if (exchangeRates)
- colClass = "col-lg-4 px-3";
- var utxoCalculatingDesc = "At startup the app pulls a summary of the UTXO set. Until this summary is retrieved this data can't be displayed. Wait for the summary request to your node to return, then refresh this page.";
div.row.index-summary
div(class=colClass)
h5.h6 Mining
- var hashrate1dayData0 = utils.formatLargeNumber(hashrate1d, 0);
- var hashrate7dayData0 = utils.formatLargeNumber(hashrate7d, 0);
- var hashrate1dayData1 = utils.formatLargeNumber(hashrate1d, 1);
- var hashrate7dayData1 = utils.formatLargeNumber(hashrate7d, 1);
table.table.table-borderless.table-sm.table-hover
tbody
tr
th.px-2.px-lg-0.px-xl-2
i.fas.fa-tachometer-alt.mr-1.summary-icon
span.border-dotted(title="Estimates for global network hashrate for 1 day / 7 days.", data-toggle="tooltip") Hashrate
small.ml-1 (d/w)
td.text-right.text-monospace
span.d-xxl-none #{hashrate1dayData0[0]}
span.d-none.d-xxl-inline #{hashrate1dayData1[0]}
small.text-muted /
span.d-xxl-none #{hashrate7dayData0[0]}
span.d-none.d-xxl-inline #{hashrate7dayData1[0]}
small.border-dotted(title=`${hashrate1dayData0[1].abbreviation}H = ${hashrate1dayData0[1].name}-hash (x10^${hashrate1dayData0[1].exponent})`, data-toggle="tooltip") #{hashrate1dayData0[1].abbreviation}H/s
tr
th.px-2.px-lg-0.px-xl-2
i.fas.fa-edit.mr-1.summary-icon
span.border-dotted(title="Estimate of the number of days the current global hashrate would require to produce all the hashes needed to re-write the entire blockchain.", data-toggle="tooltip") Chain Rewrite Days
td.text-right.text-monospace
- var globalHashCount = parseInt("0x" + getblockchaininfo.chainwork);
- var rewriteDays = globalHashCount / hashrate7d / 60 / 60 / 24;
span #{new Decimal(rewriteDays).toDecimalPlaces(1)}
tr
th.px-2.px-lg-0.px-xl-2
i.fas.fa-dumbbell.mr-1.summary-icon
span Difficulty
td.text-right.text-monospace
- var difficultyData = utils.formatLargeNumber(getblockchaininfo.difficulty, 2);
span.border-dotted(data-toggle="tooltip", title=parseFloat(getblockchaininfo.difficulty).toLocaleString())
span #{difficultyData[0]}
small.px-2.px-lg-0.px-xl-2 x
span 10
sup #{difficultyData[1].exponent}
tr
th.px-2.px-lg-0.px-xl-2
i.fas.fa-unlock-alt.mr-1.summary-icon
span Unconfirmed Tx
td.text-right.text-monospace
- var colorClass = "text-success";
if (mempoolInfo.size > 7000)
- colorClass = "text-warning";
if (mempoolInfo.size > 11000)
- colorClass = "text-danger";
span(class=colorClass) #{mempoolInfo.size.toLocaleString()}
tr
th.px-2.px-lg-0.px-xl-2
i.fas.fa-poll.mr-1.summary-icon
span.border-dotted(title="Current fee estimates (using 'estimatesmartfee') for getting a transaction included in 1 block, 6 blocks (1 hr), 144 blocks (1 day), or 1,008 blocks (1 week).", data-toggle="tooltip") Fee Targets
if (false)
small.ml-1 (1/h/d/w)
td.text-right.text-monospace #{smartFeeEsimates[1]}
small.d-md-none
small.text-muted /
span #{smartFeeEsimates[6]}
small.text-muted /
span #{smartFeeEsimates[144]}
small.text-muted /
span #{smartFeeEsimates[1008]}
small sat/vB
div(class=colClass)
h5.h6 Blockchain
table.table.table-borderless.table-sm.table-hover
tbody
tr
th.px-2.px-lg-0.px-xl-2
i.fas.fa-sign-out-alt.mr-1.summary-icon
//span Total Transactions
span Total Txs
td.text-right.text-monospace
- var totalTxData = utils.formatLargeNumber(txStats.totalTxCount, 2);
span.border-dotted(title=`${txStats.totalTxCount.toLocaleString()}`, data-toggle="tooltip") #{totalTxData[0]} #{totalTxData[1].abbreviation}
if (getblockchaininfo.size_on_disk)
- var sizeData = utils.formatLargeNumber(getblockchaininfo.size_on_disk, 2);
tr
th.px-2.px-lg-0.px-xl-2
i.fas.fa-database.mr-1.summary-icon
span Data Size
td.text-right.text-monospace #{sizeData[0]} #{sizeData[1].abbreviation}B
tr
th.px-2.px-lg-0.px-xl-2
i.fas.fa-bolt.mr-1.summary-icon
span.border-dotted(title="The total amount of work necessary to produce the active chain, approximated in 'hashes'.", data-toggle="tooltip") Chain Work
td.text-right.text-monospace
- var chainworkData = utils.formatLargeNumber(parseInt("0x" + getblockchaininfo.chainwork), 2);
span.border-dotted(data-toggle="tooltip", title=`hex: ${getblockchaininfo.chainwork.replace(/^0+/, '')}`)
span #{chainworkData[0]}
small.px-2.px-lg-0.px-xl-2 x
span 10
sup #{chainworkData[1].exponent}
if (false)
tr
th.px-2.px-lg-0.px-xl-2
i.fas.fa-arrow-circle-up.mr-1.summary-icon
span.border-dotted(title="The active 'soft' forks on the network.", data-toggle="tooltip") Soft-Forks
td.text-right.text-monospace.word-wrap
ul.list-inline.mb-0
each softforkData, softforkName in getblockchaininfo.softforks
li.list-inline-item
small.border-dotted(title=`${JSON.stringify(softforkData)}`, data-toggle="tooltip") #{softforkName}
if (utxoSetSummary || utxoSetSummaryPending)
tr
th.px-2.px-lg-0.px-xl-2
i.fas.fa-grip-horizontal.mr-1.summary-icon
span.border-dotted(title="The number / data size of 'unspent transaction outputs' (UTXOs) in the blockchain.", data-toggle="tooltip") UTXO Set
td.text-right.text-monospace
if (utxoSetSummary)
- var utxoCount = utils.formatLargeNumber(utxoSetSummary.txouts, 2);
- var utxoDataSize = utils.formatLargeNumber(utxoSetSummary.disk_size, 2);
span #{utxoCount[0]} #{utxoCount[1].abbreviation}
small.text-muted /
span #{utxoDataSize[0]} #{utxoDataSize[1].abbreviation}B
else
small.text-muted.border-dotted(title=utxoCalculatingDesc, data-toggle="tooltip") calculating...
if (utxoSetSummary || utxoSetSummaryPending)
tr
th.px-2.px-lg-0.px-xl-2
i.fab.fa-bitcoin.mr-1.summary-icon
span Total Supply
td.text-right.text-monospace
if (utxoSetSummary)
span.border-dotted(title=`${new Decimal(utxoSetSummary.total_amount).dividedBy(coinConfig.maxSupply).times(100).toDP(4)}% produced`, data-toggle="tooltip") #{parseFloat(utxoSetSummary.total_amount).toLocaleString()}
else
small.text-muted.border-dotted(title=utxoCalculatingDesc, data-toggle="tooltip") calculating...
if (exchangeRates)
div(class=colClass)
h5.h6 Financials
table.table.table-borderless.table-sm.table-hover
tbody
tr
th.px-2.px-lg-0.px-xl-2
i.fas.fa-money-bill-wave-alt.mr-1.summary-icon
span.border-dotted(data-toggle="tooltip", title=("Exchange-rate data from: " + coinConfig.exchangeRateData.jsonUrl)) Exchange Rate
td.text-right.text-monospace
span #{utils.formatValueInActiveCurrency(1.0)}
tr
th.px-2.px-lg-0.px-xl-2
i.fab.fa-btc.mr-1.summary-icon
span Sats Rate
td.text-right.text-monospace
- var satsRateData = utils.satoshisPerUnitOfActiveCurrency();
span #{satsRateData.amt}
small #{satsRateData.unit}
if (utxoSetSummary || utxoSetSummaryPending)
tr
th.px-2.px-lg-0.px-xl-2
i.fas.fa-globe.mr-1.summary-icon
span Market Cap
td.text-right.text-monospace
if (utxoSetSummary)
- var activeCurrency = global.currencyFormatType.length > 0 ? global.currencyFormatType : "usd";
- var xxx = utils.formatLargeNumber(parseFloat(utxoSetSummary.total_amount) * exchangeRates[activeCurrency], 1);
if (activeCurrency == "eur")
span €
else
span $
span #{xxx[0]}
if (xxx[1].textDesc)
span #{xxx[1].textDesc}
else
span x 10
sup #{xxx[1].exponent}
// ["154.9",{"val":1000000000,"name":"giga","abbreviation":"G","exponent":"9"}]
else
small.text-muted.border-dotted(title=utxoCalculatingDesc, data-toggle="tooltip") calculating...

3
views/includes/time-ago-text.pug

@ -0,0 +1,3 @@
- var timeAgo = moment.duration(moment.utc(new Date()).diff(moment.utc(new Date(parseInt(timeAgoTime) * 1000))));
span #{utils.shortenTimeDiff(timeAgo.format())} ago

10
views/includes/timestamp-human.pug

@ -0,0 +1,10 @@
span #{moment.utc(new Date(parseInt(timestampHuman) * 1000)).format("M/D")}
- var yearStr = moment.utc(new Date(parseInt(timestampHuman) * 1000)).format("Y");
- var nowYearStr = moment.utc(new Date()).format("Y");
if (yearStr != nowYearStr)
span , #{yearStr}
span
span.border-dotted(title=`${moment.utc(new Date(parseInt(timestampHuman) * 1000)).format("Y-MM-DD HH:mm:ss")}`, data-toggle="tooltip") #{moment.utc(new Date(parseInt(timestampHuman) * 1000)).format("HH:mm")}

6
views/includes/tools-card-block.pug

@ -0,0 +1,6 @@
- var siteTool = config.siteTools[toolsItemIndex];
li.mb-2
span(title=siteTool.desc, data-toggle="tooltip")
i.mr-1.fa-sm(class=siteTool.fontawesome, style="width: 24px;")
a(href=siteTool.url)
span #{siteTool.name}

97
views/includes/tools-card.pug

@ -1,19 +1,100 @@
div(class="card mb-4 shadow-sm")
div(class="card-header")
h2(class="h6 mb-0") Tools
div(class="card-body")
div(class="row")
if (false)
div.card.mb-4.shadow-sm
div.card-header
h2.h6.mb-0 Tools
div.card-body
div.row
each item, index in [[0, 1, 2], [4, 3, 5], [6, 7, 8]]
div(class="col-md-4")
ul(style="list-style-type: none;", class="pl-0")
div.col-md-4
ul.pl-0(style="list-style-type: none;")
each toolIndex, toolIndexIndex in item
- var siteTool = config.siteTools[toolIndex];
li
div(class="float-left", style="height: 50px; width: 40px; margin-right: 10px;")
div.float-left(style="height: 50px; width: 40px; margin-right: 10px;")
span
i(class=siteTool.fontawesome, class="fa-2x mr-2", style="margin-top: 6px;")
a(href=siteTool.url) #{siteTool.name}
br
p #{siteTool.desc}
if (false)
div.row
each list, listIndex in [[0, 1], [2, 3], [4, 9], [5, 6], [7, 8]]
div.col
ul.list-unstyled
each listItem in list
- var siteTool = config.siteTools[listItem];
li.mb-2
span(title=siteTool.desc, data-toggle="tooltip")
i.mr-2.fa-lg(class=siteTool.fontawesome, style="width: 30px;")
a(href=siteTool.url)
span #{siteTool.name}
if (false)
div.row
each list, listIndex in [[0, 1, 2, 3, 4], [9, 5, 6, 7, 8]]
div.col
ul.list-unstyled
each listItem in list
- var siteTool = config.siteTools[listItem];
li.mb-2
span(title=siteTool.desc, data-toggle="tooltip")
i.mr-2.fa-sm(class=siteTool.fontawesome, style="width: 30px;")
a(href=siteTool.url)
span #{siteTool.name}
if (true)
div.row
// split into 4 segments:
// xxl: 2 columns (2 col because on xxl the tools card is in the same row as the "Network Summary" card)
// md: 3 columns (requires separate layout implementation...see below)
// lg, xl: 4 columns
// xm: 2 columns
- var indexLists = [[0, 1, 2], [3, 4, 9], [5, 6], [7, 8]];
- var indexListsMediumWidth = [[0, 1, 2, 3], [4, 9, 5], [6, 7, 8]];
// special case for medium-width layout
div.col.d-none.d-md-block.d-lg-none
div.row
div.col-md-4
ul.list-unstyled.mb-0
each toolsItemIndex in indexListsMediumWidth[0]
include tools-card-block.pug
div.col-md-4
ul.list-unstyled.mb-0
each toolsItemIndex in indexListsMediumWidth[1]
include tools-card-block.pug
div.col-md-4
ul.list-unstyled.mb-0
each toolsItemIndex in indexListsMediumWidth[2]
include tools-card-block.pug
// the below 2 div.col's are the default layout, used everywhere except medium-width layout
div.col.d-sm-block.d-md-none.d-lg-block
div.row
div.col-md-6.col-xxl-12
ul.list-unstyled.mb-0
each toolsItemIndex in indexLists[0]
include tools-card-block.pug
div.col-md-6.col-xxl-12
ul.list-unstyled.mb-0
each toolsItemIndex in indexLists[1]
include tools-card-block.pug
div.col.d-md-none.d-lg-block
div.row
div.col-md-6.col-xxl-12
ul.list-unstyled.mb-0
each toolsItemIndex in indexLists[2]
include tools-card-block.pug
div.col-md-6.col-xxl-12
ul.list-unstyled.mb-0
each toolsItemIndex in indexLists[3]
include tools-card-block.pug

208
views/includes/transaction-io-details.pug

@ -13,8 +13,8 @@ script.
link.classList.add("d-none");
}
div(class="row text-monospace")
div(class="col-lg-6")
div.row.text-monospace
div.col-lg-6
if (txInputs)
- var extraInputCount = 0;
each txVin, txVinIndex in tx.vin
@ -26,40 +26,53 @@ div(class="row text-monospace")
- var vout = txInput.vout[txVin.vout];
if (txVin.coinbase || vout)
div(class="row")
div(class="tx-io-label")
a(data-toggle="tooltip", title=("Input #" + (txVinIndex + 1).toLocaleString()), style="white-space: nowrap;")
i(class=("fas fa-" + fontawesomeInputName + " mr-2"))
span(class="d-inline d-md-none") Input #
span #{(txVinIndex + 1).toLocaleString()}
div.clearfix
div.tx-io-label
a(data-toggle="tooltip", title=("Input #" + txVinIndex.toLocaleString()), style="white-space: nowrap;")
span.font-weight-bold &gt;
small.d-inline.d-md-none Input
span ##{txVinIndex.toLocaleString()}
if (tx.vin.length > 0)
span(class="d-inline d-md-none font-weight-light text-muted") / #{tx.vin.length.toLocaleString()}
small.d-inline.d-md-none.font-weight-light.text-muted / #{(tx.vin.length - 1).toLocaleString()}
div(class="tx-io-content")
div(class="row pr-3")
div(class="tx-io-desc")
div.tx-io-content
div.clearfix
div.tx-io-desc
if (txVin.coinbase)
span(class="rounded bg-primary text-white px-2 py-1 mr-2") coinbase
small.data-tag.bg-primary.mr-2 coinbase
span Newly minted coins
else
div.word-wrap
small.data-tag.bg-dark.mr-2 txo
a(href=("/tx/" + txInput.txid + "#output-" + txVin.vout)) #{utils.ellipsize(txInput.txid, 26)}
span ##{txVin.vout}
if (vout && vout.scriptPubKey && vout.scriptPubKey.addresses)
div(class="word-wrap")
div.word-wrap
small.text-muted
span.mr-1 (addr:
a(href=("/address/" + vout.scriptPubKey.addresses[0]), class=(txIOHighlightAddress == vout.scriptPubKey.addresses[0] ? "font-weight-bold" : null)) #{vout.scriptPubKey.addresses[0]}
if (global.specialAddresses[vout.scriptPubKey.addresses[0]])
- var specialAddressInfo = global.specialAddresses[vout.scriptPubKey.addresses[0]];
if (specialAddressInfo.type == "minerPayout")
span
a(data-toggle="tooltip", title=("Miner payout address: " + specialAddressInfo.minerInfo.name))
i(class="fas fa-certificate text-primary")
else if (specialAddressInfo.type == "donation")
span
a(data-toggle="tooltip", title=("Development donation address. All support is appreciated!"))
i(class="fas fa-certificate text-primary")
i.fas.fa-certificate.text-primary
span )
span(class="small") via
a(href=("/tx/" + txInput.txid + "#output-" + txVin.vout)) #{txInput.txid.substring(0, 20)}...[#{txVin.vout}]
if (false)
small
span.border-dotted(title="Transaction output", data-toggle="tooltip") txo
span.mr-1 :
a(href=("/tx/" + txInput.txid + "#output-" + txVin.vout)) #{utils.ellipsize(txInput.txid, 20)}[#{txVin.vout}]
else
div(class="tx-io-value")
div.tx-io-value
if (txVin.coinbase)
- var currencyValue = coinConfig.blockRewardFunction(blockHeight, global.activeBlockchain);
include ./value-display.pug
@ -74,37 +87,40 @@ div(class="row text-monospace")
- extraInputCount = extraInputCount + 1;
if (extraInputCount > 0)
div(class="row")
div(class="tx-io-label")
div.clearfix
div.tx-io-label
a(data-toggle="tooltip", title=("Input #" + (tx.vin.length - extraInputCount + 1).toLocaleString() + " - " + tx.vin.length.toLocaleString()), style="white-space: nowrap;")
i(class=("fas fa-" + fontawesomeInputName + " mr-2"))
span(class="d-inline d-md-none") Input #
i(class=("fas fa-ellipsis-h mr-2"))
div(class="tx-io-content")
div(class="row pr-3")
div(class="tx-io-desc")
span #{extraInputCount.toLocaleString()} more input(s)
span.font-weight-bold &gt;
span.d-inline.d-md-none Input
span #…
div.tx-io-content
div.clearfix
div.tx-io-desc
span.text-reset #{extraInputCount.toLocaleString()} more input
if (extraInputCount > 1)
span s
br
span(class="small") see
a(href=("/tx/" + tx.txid)) transactions details
small.text-muted (see
a(href=("/tx/" + tx.txid)) transaction page
span for details)
div(class="tx-io-value")
div.tx-io-value
- var currencyValue = new Decimal(totalIOValues.output).minus(new Decimal(totalIOValues.input));
include ./value-display.pug
hr
div(class="row mb-5 mb-lg-2 pr-3")
div(class="col")
div(class="font-weight-bold text-left text-md-right")
span(class="d-block d-md-none") Total Input:
div.row.mb-5.mb-lg-0
div.col
div.font-weight-bold.text-left.text-md-right
span.d-block.d-md-none Total Input:
- var currencyValue = totalIOValues.input;
include ./value-display.pug
div(class="col-lg-6")
div.col-lg-6
- var maxRegularRowCount = (txIOHighlightAddress != null ? config.site.addressPage.txOutputMaxDefaultDisplay : 10000000);
- var regularRowCount = 0;
- var hiddenRowCount = 0;
@ -121,74 +137,88 @@ div(class="row text-monospace")
- hiddenRow = true;
- hiddenRowCount++;
div(data-txid=tx.txid, class=("pr-0 " + (hiddenRow ? "d-none" : "")))
div(class="row")
div(class="tx-io-label")
a(data-toggle="tooltip", title=("Output #" + (voutIndex + 1).toLocaleString()), style="white-space: nowrap;")
i(class=("fas fa-" + fontawesomeOutputName + " mr-2"))
span(class="d-inline d-md-none") Output #
span #{(voutIndex + 1).toLocaleString()}
div(data-txid=tx.txid, class=(hiddenRow ? "d-none" : ""))
div.clearfix
div.tx-io-label
a(data-toggle="tooltip", title=("Output #" + voutIndex.toLocaleString()), style="white-space: nowrap;")
span.font-weight-bold &lt;
small.d-inline.d-md-none Output
span ##{voutIndex.toLocaleString()}
if (tx.vout.length > 0)
span(class="d-inline d-md-none font-weight-light text-muted") / #{tx.vout.length.toLocaleString()}
small.d-inline.d-md-none.font-weight-light.text-muted / #{(tx.vout.length - 1).toLocaleString()}
div(class="tx-io-content")
div(class=("row pr-3"))
div(class="tx-io-desc")
div.tx-io-content
div.clearfix
div.tx-io-desc
if (vout.scriptPubKey)
if (vout.scriptPubKey.addresses)
if (false)
if (true)
div.mb-tiny
small.data-tag.bg-dark
if (vout.scriptPubKey.type == "pubkey")
span(class="border border-secondary rounded bg-light px-2 py-1 mr-2") P2PK
span(title="Output Type: Pay to Public Key", data-toggle="tooltip") p2pk
else if (vout.scriptPubKey.type == "pubkeyhash")
span(class="border border-secondary rounded bg-light px-2 py-1 mr-2") P2PKH
span(title="Output Type: Pay to Public Key Hash", data-toggle="tooltip") p2pkh
else if (vout.scriptPubKey.type == "scripthash")
span(class="border border-secondary rounded bg-light px-2 py-1 mr-2") P2SH
span(title="Output Type: Pay to Script Hash", data-toggle="tooltip") p2sh
else if (vout.scriptPubKey.type == "witness_v0_keyhash")
span(class="border border-secondary rounded bg-light px-2 py-1 mr-2") V0_P2WPKH
span(title="Output Type: Witness, v0 Key Hash", data-toggle="tooltip") v0_p2wpkh
else if (vout.scriptPubKey.type == "witness_v0_scripthash")
span(title="Output Type: Witness, v0 Script Hash", data-toggle="tooltip") v0_p2wsh
else
span ???
a(id=("output-" + voutIndex), href=("/address/" + vout.scriptPubKey.addresses[0]))
span(class=("monospace word-wrap " + (highlightRow ? "font-weight-bold" : ""))) #{vout.scriptPubKey.addresses[0]}
each addr in vout.scriptPubKey.addresses
a(id=("output-" + voutIndex), href=("/address/" + addr))
span.text-monospace.word-wrap(class=(highlightRow ? "font-weight-bold" : "")) #{addr}
if (global.specialAddresses[vout.scriptPubKey.addresses[0]])
- var specialAddressInfo = global.specialAddresses[vout.scriptPubKey.addresses[0]];
if (global.specialAddresses[addr])
- var specialAddressInfo = global.specialAddresses[addr];
if (specialAddressInfo.type == "minerPayout")
span
a(data-toggle="tooltip", title=("Miner payout address: " + specialAddressInfo.minerInfo.name))
i(class="fas fa-certificate text-primary")
else if (specialAddressInfo.type == "donation")
span
a(data-toggle="tooltip", title=("Development donation address. All support is appreciated!"))
i(class="fas fa-certificate text-primary")
i.fas.fa-certificate.text-primary
br
else if (vout.scriptPubKey.hex && vout.scriptPubKey.hex.startsWith('6a24aa21a9ed'))
span(class="monospace")
span(class="rounded bg-primary text-white px-2 py-1 mr-2") OP_RETURN
span(title="Segregated Witness", data-toggle="tooltip") SegWit
span committment
div.mb-tiny
small.data-tag.bg-primary OP_RETURN
a(href="https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#commitment-structure", data-toggle="tooltip", title="View developer docs", target="_blank")
i(class="fas fa-info-circle")
small.text-muted (SegWit committment -
a(href="https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#commitment-structure", data-toggle="tooltip", title="View developer docs", target="_blank") about
span )
else if (vout.scriptPubKey.asm && vout.scriptPubKey.asm.startsWith('OP_RETURN '))
span(class="monospace")
span(class="rounded bg-primary text-white px-2 py-1 mr-2") OP_RETURN
span #{utils.hex2ascii(vout.scriptPubKey.asm.substring("OP_RETURN ".length))}
div.mb-tiny
small.data-tag.bg-primary OP_RETURN
small #{utils.hex2ascii(vout.scriptPubKey.asm.substring("OP_RETURN ".length))}
else
span(class="monospace")
span(class="text-warning font-weight-bold") Unable to decode output:
br
span(class="font-weight-bold") type:
span #{vout.scriptPubKey.type}
br
span(class="font-weight-bold") asm:
span #{vout.scriptPubKey.asm}
div.mb-tiny
small.data-tag.bg-dark
if (vout.scriptPubKey.type == "pubkey")
span(title="Output Type: Pay to Public Key", data-toggle="tooltip") p2pk
else if (vout.scriptPubKey.type == "pubkeyhash")
span(title="Output Type: Pay to Public Key Hash", data-toggle="tooltip") p2pkh
else if (vout.scriptPubKey.type == "scripthash")
span(title="Output Type: Pay to Script Hash", data-toggle="tooltip") p2sh
else if (vout.scriptPubKey.type == "witness_v0_keyhash")
span(title="Output Type: Witness, v0 Key Hash", data-toggle="tooltip") v0_p2wpkh
else if (vout.scriptPubKey.type == "witness_v0_scripthash")
span(title="Output Type: Witness, v0 Script Hash", data-toggle="tooltip") v0_p2wsh
else
span ???
span
small.font-weight-bold asm:
small #{vout.scriptPubKey.asm}
br
span(class="font-weight-bold") decodedHex:
span #{utils.hex2ascii(vout.scriptPubKey.hex)}
small.font-weight-bold hex:
small #{vout.scriptPubKey.hex}
div(class="tx-io-value")
div.tx-io-value
if (utxos)
if (utxos[voutIndex])
i.fas.fa-lock.text-success.mr-2(title="Unspent output." data-toggle="tooltip")
@ -205,10 +235,10 @@ div(class="row text-monospace")
if (hiddenRowCount > 0)
a(href="javascript:void(0)", onclick=("showAllTxOutputs(this, '" + tx.txid + "');")) Show all #{tx.vout.length.toLocaleString()} outputs
div(class="row mb-2 pr-3")
div(class="col")
div(class="font-weight-bold text-left text-md-right")
span(class="d-block d-md-none") Total Output:
div.row.mb-0.mb-lg-0
div.col
div.font-weight-bold.text-left.text-md-right
span.d-block.d-md-none Total Output:
- var currencyValue = totalIOValues.output;
include ./value-display.pug

19
views/includes/value-display.pug

@ -1,18 +1,25 @@
- var currencyFormatInfo = utils.getCurrencyFormatInfo(currencyFormatType);
if (currencyValue > 0)
- var parts = utils.formatCurrencyAmount(currencyValue, currencyFormatType).split(" ");
if (currencyValueDecimals)
- var parts = utils.formatCurrencyAmountWithForcedDecimalPlaces(currencyValue, currencyFormatType, currencyValueDecimals);
else
- var parts = utils.formatCurrencyAmount(currencyValue, currencyFormatType);
//span #{JSON.stringify(currencyFormatInfo)}
span.text-monospace #{parts.val}#{(currencyValueDecimals && currencyFormatInfo.type == "native" && currencyFormatInfo.multiplier <= 1000) ? "…" : ""}
if (parts.lessSignificantDigits)
span.text-monospace.text-small(style="margin-left: 2px;") #{parts.lessSignificantDigits}
span.monospace #{parts[0]}
if (currencyFormatInfo.type == "native")
if (global.exchangeRates)
small.border-dotted.ml-1(data-toggle="tooltip", title=utils.formatExchangedCurrency(currencyValue, "usd")) #{parts[1]}
if (exchangeRates)
small.border-dotted.ml-1(data-toggle="tooltip", title=utils.formatExchangedCurrency(currencyValue, "usd")) #{parts.currencyUnit}
else
small.ml-1 #{parts[1]}
small.ml-1 #{parts.currencyUnit}
else if (currencyFormatInfo.type == "exchanged")
small.border-dotted.ml-1(data-toggle="tooltip", title=utils.formatCurrencyAmount(currencyValue, coinConfig.defaultCurrencyUnit.name)) #{parts[1]}
small.border-dotted.ml-1(data-toggle="tooltip", title=utils.formatCurrencyAmount(currencyValue, coinConfig.defaultCurrencyUnit.name)) #{parts.currencyUnit}
else
span.text-monospace 0

167
views/index.pug

@ -9,7 +9,7 @@ block content
if (getblockchaininfo == null)
div(class="alert alert-warning")
p(class="font-weight-bold") Unable to get basic blockchain data
p.font-weight-bold Unable to get basic blockchain data
ul
li If you just started your node, it may still be initializing.
li If your node is already initialized, check your RPC connection info.
@ -62,7 +62,7 @@ block content
if (getblockchaininfo.size_on_disk)
- networkSummaryItemCount++;
if (global.exchangeRates)
if (exchangeRates)
- networkSummaryItemCount++;
if (txStats)
@ -72,117 +72,63 @@ block content
if (networkSummaryItemCount > 6)
- networkSummaryColumnClass = "col-md-3";
div(class="card mb-4 shadow-sm")
div(class="card-header")
h2(class="h6 mb-0") Network Summary
div(class="card-body")
div(class="row")
div(class=networkSummaryColumnClass)
div(class="float-left", style="height: 40px; width: 40px;")
span
i(class="fas fa-tachometer-alt fa-2x mr-2", style="margin-top: 6px;")
- var hashrateData = utils.formatLargeNumber(miningInfo.networkhashps, 3);
span(class="font-weight-bold") Hashrate
p(class="lead")
span #{hashrateData[0]}
span.border-dotted(title=`${hashrateData[1].abbreviation}H = ${hashrateData[1].name}-hash (x10^${hashrateData[1].exponent})`, data-toggle="tooltip") #{hashrateData[1].abbreviation}H/s
if (txStats)
div(class=networkSummaryColumnClass)
div(class="float-left", style="height: 40px; width: 40px;")
span
i(class="fas fa-sign-out-alt fa-2x mr-2", style="margin-top: 6px; margin-left: 3px;")
span(class="font-weight-bold") Total Transactions
p(class="lead") #{txStats.totalTxCount.toLocaleString()}
div(class=networkSummaryColumnClass)
div(class="float-left", style="height: 40px; width: 40px;")
span
i(class="fas fa-unlock-alt fa-2x mr-2", style="margin-top: 6px; margin-left: 3px;")
span(class="font-weight-bold") Unconfirmed Transactions
p(class="lead") #{mempoolInfo.size.toLocaleString()} tx
- var mempoolBytesData = utils.formatLargeNumber(mempoolInfo.usage, 2);
small(class="text-muted font-weight-light") (#{mempoolBytesData[0]} #{mempoolBytesData[1].abbreviation}B)
div(class=networkSummaryColumnClass)
div(class="float-left", style="height: 40px; width: 40px;")
span
i(class="fas fa-bolt fa-2x mr-2", style="margin-top: 6px; margin-left: 6px;")
- var chainworkData = utils.formatLargeNumber(parseInt("0x" + getblockchaininfo.chainwork), 2);
span(class="font-weight-bold") Chainwork
div.row
- var summaryColCount = 8;
if (exchangeRates)
- summaryColCount = 9;
p(class="lead")
span.mr-2.border-dotted(data-toggle="tooltip", title=getblockchaininfo.chainwork.replace(/^0+/, ''))
span #{chainworkData[0]}
span x 10
sup #{chainworkData[1].exponent}
span hashes
div(class=networkSummaryColumnClass)
div(class="float-left", style="height: 40px; width: 40px; font-size: 12px;")
span
i(class="fas fa-dumbbell fa-2x mr-2", style="margin-top: 6px;")
- var difficultyData = utils.formatLargeNumber(getblockchaininfo.difficulty, 3);
span(class="font-weight-bold") Difficulty
div.mb-3.pr-xxl-0(class=`col-xxl-${summaryColCount}`)
div.card.shadow-sm(style="height: 100%;")
div.card-body.px-2.px-sm-3
h3.h6 Network Summary
hr
p(class="lead")
span.mr-2.border-dotted(data-toggle="tooltip", title=parseFloat(getblockchaininfo.difficulty).toLocaleString())
span #{difficultyData[0]}
span x 10
sup #{difficultyData[1].exponent}
include includes/index-network-summary.pug
if (getblockchaininfo.size_on_disk)
div(class=networkSummaryColumnClass)
div(class="float-left", style="height: 40px; width: 40px;")
span
i(class="fas fa-database fa-2x mr-2", style="margin-top: 6px; margin-left: 3px;")
span(class="font-weight-bold") Blockchain Size
div.mb-3(class=`col-xxl-${12 - summaryColCount}`)
div.card.shadow-sm(style="height: 100%;")
div.card-body.px-2.px-sm-3
h3.h6 Tools
hr
- var sizeData = utils.formatLargeNumber(getblockchaininfo.size_on_disk, 2);
p(class="lead") #{sizeData[0]} #{sizeData[1].abbreviation}B
include includes/tools-card.pug
if (global.exchangeRates)
div(class=networkSummaryColumnClass)
div(class="float-left", style="height: 40px; width: 40px; font-size: 12px;")
span
i(class="fas fa-money-bill-wave-alt fa-2x mr-2", style="margin-top: 7px;")
span.font-weight-bold.border-dotted(data-toggle="tooltip", title=("Exchange-rate data from: " + coinConfig.exchangeRateData.jsonUrl)) Exchange Rate
if (latestBlocks)
div.row.mb-3
div.col
div.card.shadow-sm
div.card-body.px-2.px-sm-3
div.row
div.col
h3.h6 Latest Blocks
if (global.exchangeRates)
p(class="lead") #{utils.formatValueInActiveCurrency(1.0)}
small.text-muted.font-weight-light (#{utils.satoshisPerUnitOfActiveCurrency()})
div.col.text-right
a(href="/blocks") Browse blocks &raquo;
else
p(class="lead") -
hr
- var blocks = latestBlocks;
- var blockOffset = 0;
include includes/tools-card.pug
include includes/blocks-list.pug
if (latestBlocks)
if (false)
div(class="card mb-4 shadow-sm")
div(class="card-header")
div(class="row")
div(class="col")
div.card-header
div.row
div.col
h2(class="h6 mb-0") Latest Blocks
if (getblockchaininfo.initialblockdownload)
small (#{(getblockchaininfo.headers - getblockchaininfo.blocks).toLocaleString()} behind)
div(class="col")
div.col
span(style="float: right;")
a(href="/blocks")
span Browse Blocks &raquo;
div(class="card-body")
div.card-body
- var blocks = latestBlocks;
- var blockOffset = 0;
@ -191,23 +137,23 @@ block content
if (txStats)
div(class="card mb-4 shadow-sm")
div(class="card-header")
div(class="row")
div(class="col")
h2(class="h6 mb-0") Transaction Stats Summary
div.row.mb-3
div.col
div.card.shadow-sm
div.card-body.px-2.px-sm-3
div.row
div.col
h3.h6 Transaction Stats
div(class="col")
span(style="float: right;")
a(href="/tx-stats")
span Transaction Stats &raquo;
div(class="card-body")
if (true)
div(class="row")
div(class="col-lg-6")
div(class="table-responsive")
table(class="table text-right mb-4 mb-lg-0")
div.col.text-right
a(href="/tx-stats") See more &raquo;
hr
div.row
div.col-lg-6
div.table-responsive
table.table.text-right.mb-4.mb-lg-0
thead
tr
th Period
@ -215,13 +161,13 @@ block content
th Transactions Per Sec
tbody
each item, index in chainTxStats
tr(class="text-monospace")
tr.text-monospace
td #{chainTxStatsLabels[index]}
td #{item.window_tx_count.toLocaleString()}
td #{new Decimal(item.txrate).toDecimalPlaces(4)}
div(class="col-lg-6")
div.col-lg-6
script var txRateDataDay = [];
each item, index in txStats.txCountStats.txRates
script txRateDataDay.push({x:#{item.x}, y:#{item.y}});
@ -229,3 +175,4 @@ block content
- var graphData = {id:"graphRateDay", dataVar:"txRateDataDay", labels:txStats.txCountStats.txLabels, title:"Tx Rate, 24hr", xaxisTitle:"Block", xaxisStep:5, yaxisTitle:"Tx Per Sec"};
include ./includes/line-graph.pug

148
views/layout.pug

@ -10,38 +10,40 @@ html(lang="en")
if (session.uiTheme && session.uiTheme == "dark")
link(rel="stylesheet", href="/css/bootstrap-dark.css", integrity="")
link(rel="stylesheet", href="/css/dark-touchups.css", integrity="")
link(rel="stylesheet", href="/css/bootstrap-dark.min.css", integrity="sha384-oqee0tym2HUHJW9STPyrefrgfODvg8w9lfqk+s/IbxHhuJpoPbAbPqj8ylGM3TUK")
link(rel="stylesheet", href="/css/dark-touchups.css", integrity="sha384-nUVK/u/fwyNP4THfHo2CtWSoyZaxa65KenBmn/46NaEgGD5uaUFz+N6cqgg8n5t0")
else
link(rel="stylesheet", href="/css/bootstrap.min.css", integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh")
link(rel="stylesheet", href="/css/bootstrap.min.css", integrity="sha384-Jo2q7h5Bg8GPJ+O6zM58Rvi6URN9ux4TSqZ4TlNnJzRWwVmEgDrqniWV//1yqqiR")
link(rel='stylesheet', href='/css/styling.css')
link(rel="icon", type="image/png", href=("/img/logo/" + config.coin.toLowerCase() + ".png"))
include includes/debug-overrides.pug
block headContent
title Explorer
body(class="bg-dark")
nav(class="navbar navbar-expand-lg navbar-dark bg-dark")
div(class="container")
a(class="navbar-brand", href="/")
body.bg-dark
nav.navbar.navbar-expand-lg.navbar-dark.bg-dark
div.container
a.navbar-brand(href="/")
span
if (coinConfig.logoUrl)
img(src=coinConfig.logoUrl, class="header-image", alt="logo")
img.header-image(src=coinConfig.logoUrl, alt="logo")
span #{coinConfig.siteTitle}
button(type="button", class="navbar-toggler navbar-toggler-right", data-toggle="collapse", data-target="#navbarNav", aria-label="collapse navigation")
span(class="navbar-toggler-icon")
button.navbar-toggler.navbar-toggler-right(type="button", data-toggle="collapse", data-target="#navbarNav", aria-label="collapse navigation")
span.navbar-toggler-icon
div(class="collapse navbar-collapse", id="navbarNav")
div.collapse.navbar-collapse(id="navbarNav")
if (rpcClient)
ul(class="navbar-nav mr-auto")
ul.navbar-nav.mr-auto
if (false)
li(class="nav-item")
a(href="/about", class="nav-link")
li.nav-item
a.nav-link(href="/about")
span About
if (global.activeBlockchain != "main")
@ -51,75 +53,79 @@ html(lang="en")
a.nav-link.text-warning(title=`Current node's chain: ${global.activeBlockchain}` data-toggle="tooltip") [#{chainName}]
if (config.siteTools)
li(class="nav-item dropdown")
a(class="nav-link dropdown-toggle", href="javascript:void(0)", role="button", data-toggle="dropdown", aria-haspopup="true", aria-expanded="false")
li.nav-item.dropdown
a.nav-link.dropdown-toggle(href="javascript:void(0)", role="button", data-toggle="dropdown", aria-haspopup="true", aria-expanded="false")
span Tools
div(class="dropdown-menu shadow", aria-label="Tools Items")
each item in config.siteTools
a(class="dropdown-item", href=item.url)
div.dropdown-menu.shadow(aria-label="Tools Items")
each itemIndex in config.site.toolsDropdownIndexList
- var item = config.siteTools[itemIndex];
a.dropdown-item(href=item.url)
i(class=item.fontawesome, style="width: 20px; margin-right: 10px;")
span #{item.name}
if (config.site.header.dropdowns)
each dropdown, ddIndex in config.site.header.dropdowns
li(class="nav-item dropdown")
a(class="nav-link dropdown-toggle", href="javascript:void(0)", role="button", data-toggle="dropdown", aria-haspopup="true", aria-expanded="false")
li.nav-item.dropdown
a.nav-link.dropdown-toggle(href="javascript:void(0)", role="button", data-toggle="dropdown", aria-haspopup="true", aria-expanded="false")
span #{dropdown.title}
div(class="dropdown-menu shadow", aria-label=(dropdown.title + " Items"))
div.dropdown-menu.shadow(aria-label=(dropdown.title + " Items"))
each dropdownLink in dropdown.links
a(class="dropdown-item", href=dropdownLink.url)
a.dropdown-item(href=dropdownLink.url)
if (dropdownLink.imgUrl)
img(src=dropdownLink.imgUrl, style="width: 24px; height: 24px; margin-right: 8px;", alt=dropdownLink.name)
span #{dropdownLink.name}
form(method="post", action="/search", class="form-inline mr-3")
form.form-inline.mr-3(method="post", action="/search")
input(type="hidden", name="_csrf", value=csrfToken)
div(class="input-group input-group-sm")
input(type="text", class="form-control form-control-sm", name="query", placeholder="block height/hash, txid, address", value=(query))
div(class="input-group-append")
button(type="submit", class="btn btn-primary")
i(class="fas fa-search")
ul(class="navbar-nav")
li(class="nav-item dropdown")
a(class="nav-link dropdown-toggle", href="javascript:void(0)", id="navbarDropdown", role="button", data-toggle="dropdown", aria-haspopup="true", aria-expanded="false")
i(class="fas fa-cog mr-1")
div(class="dropdown-menu dropdown-menu-right shadow", aria-labelledby="navbarDropdown")
div.input-group.input-group-sm
input.form-control.form-control-sm(type="text", name="query", placeholder="block height/hash, txid, address", value=(query))
div.input-group-append
button.btn.btn-primary(type="submit")
i.fas.fa-search
ul.navbar-nav
li.nav-item.dropdown
a.nav-link.dropdown-toggle(href="javascript:void(0)", id="navbarDropdown", role="button", data-toggle="dropdown", aria-haspopup="true", aria-expanded="false")
i.fas.fa-cog.mr-1
div.dropdown-menu.dropdown-menu-right.shadow(aria-labelledby="navbarDropdown")
if (coinConfig.currencyUnits)
span(class="dropdown-header") Currency Units
span.dropdown-header Currency Units
each item in coinConfig.currencyUnits
if (item.type == "native" || (config.queryExchangeRates && !config.privacyMode))
a(class="dropdown-item", href=("/changeSetting?name=currencyFormatType&value=" + item.values[0]))
a.dropdown-item(href=("/changeSetting?name=currencyFormatType&value=" + item.values[0]))
each valueName in item.values
if (currencyFormatType == valueName)
i(class="fas fa-check")
i.fas.fa-check
span #{item.name}
span(class="dropdown-header") Theme
a(class="dropdown-item", href="/changeSetting?name=uiTheme&value=light")
span.dropdown-header Theme
a.dropdown-item(href="/changeSetting?name=uiTheme&value=light")
if (session.uiTheme == "light" || session.uiTheme == "")
i(class="fas fa-check")
i.fas.fa-check
span Light
a(class="dropdown-item", href="/changeSetting?name=uiTheme&value=dark")
a.dropdown-item(href="/changeSetting?name=uiTheme&value=dark")
if (session.uiTheme == "dark")
i(class="fas fa-check")
i.fas.fa-check
span Dark
if (host && port && !homepage && config.site.header.showToolsSubheader)
div(id="sub-menu", class="container mb-2 pt-2 d-lg-block d-none border-top", style="")
ul(class="nav")
each item, index in config.siteTools
li(class="nav-item")
a(href=item.url, class="nav-link text-white px-2 text-decoration-underline")
div.container.mb-2.pt-2.d-lg-block.d-none.border-top(id="sub-menu", style="")
ul.nav
each itemIndex in config.site.subHeaderToolsList
- var item = config.siteTools[itemIndex];
li.nav-item
a.nav-link.text-white.px-2.text-decoration-underline.mr-3(href=item.url)
i.mr-1(class=item.fontawesome)
span #{item.name}
- var bodyBgColor = "#ffffff;";
- var bodyBgColor = "#f8f9fa;";
if (session.uiTheme && session.uiTheme == "dark")
- bodyBgColor = "#0c0c0c;";
div(class="pb-4 pt-3 pt-md-4", style=("background-color: " + bodyBgColor))
div(class="container px-2 px-sm-3")
div.pb-4.pt-3.pt-md-4(style=("background-color: " + bodyBgColor))
div.container.px-2.px-sm-3
if (pageErrors && pageErrors.length > 0)
include includes/page-errors-modal.pug
@ -129,7 +135,7 @@ html(lang="en")
span.badge.badge-danger.ml-2 #{pageErrors.length.toLocaleString()}
if (userMessage)
div(class="alert", class=(userMessageType ? ("alert-" + userMessageType) : "alert-warning"), role="alert")
div.alert(class=(userMessageType ? ("alert-" + userMessageType) : "alert-warning"), role="alert")
span #{userMessage}
block content
@ -139,26 +145,26 @@ html(lang="en")
if (config.demoSite && !crawlerBot)
include ./includes/donation-modal.pug
footer(class="footer border-top border-primary bg-dark pt-3 pb-1 px-3 text-white", style="border-width: 5px !important;")
div(class="container")
div(class="row")
div(class="col-md-5")
footer.footer.border-top.border-primary.bg-dark.pt-3.pb-1.px-3.text-white(style="border-width: 5px !important;")
div.container
div.row
div.col-md-5
dl
dt Source
dd
a(href="https://github.com/janoside/btc-rpc-explorer") github.com/janoside/btc-rpc-explorer
if (global.sourcecodeProjectMetadata)
div(class="mt-2")
a(href="https://github.com/janoside/btc-rpc-explorer", class="btn btn-primary btn-sm mr-3 mb-1 text-decoration-none")
i(class="fas fa-star mr-2")
span(class="mr-2") Star
span(class="badge bg-white text-dark") #{global.sourcecodeProjectMetadata.stargazers_count}
div.mt-2
a.btn.btn-primary.btn-sm.mr-3.mb-1.text-decoration-none(href="https://github.com/janoside/btc-rpc-explorer")
i.fas.fa-star.mr-2
span.mr-2 Star
span.badge.bg-white.text-dark #{global.sourcecodeProjectMetadata.stargazers_count}
a(href="https://github.com/janoside/btc-rpc-explorer/fork", class="btn btn-primary btn-sm mr-3 mb-1 text-decoration-none")
i(class="fas fa-code-branch mr-2")
span(class="mr-2") Fork
span(class="badge bg-white text-dark") #{global.sourcecodeProjectMetadata.forks_count}
a.btn.btn-primary.btn-sm.mr-3.mb-1.text-decoration-none(href="https://github.com/janoside/btc-rpc-explorer/fork")
i.fas.fa-code-branch.mr-2
span.mr-2 Fork
span.badge.bg-white.text-dark #{global.sourcecodeProjectMetadata.forks_count}
@ -184,20 +190,20 @@ html(lang="en")
else
a(href="https://explorer.btc21.org") https://explorer.btc21.org
div(class="mt-2")
div.mt-2
- var demoSiteCoins = ["BTC"];
each demoSiteCoin in demoSiteCoins
a(href=coinConfigs[demoSiteCoin].demoSiteUrl, class="mr-2", title=coinConfigs[demoSiteCoin].siteTitle)
a.mr-2(href=coinConfigs[demoSiteCoin].demoSiteUrl, title=coinConfigs[demoSiteCoin].siteTitle)
img(src=("/img/logo/" + demoSiteCoin.toLowerCase() + ".svg"), alt=demoSiteCoin.toLowerCase())
a(href="https://lnd-admin.chaintools.io", class="mr-2", title="LND Admin")
a.mr-2(href="https://lnd-admin.chaintools.io", title="LND Admin")
img(src=("/img/logo/lnd-admin.png"), style="width: 32px; height: 32px;", alt="LND Admin")
div(class="col-md-7 text-md-right")
div.col-md-7.text-md-right
dl
dd
button.btn.btn-primary(type="button", class="btn btn-primary", data-toggle="modal", data-target="#exampleModalCenter")
i(class="fas fa-heart mr-2")
button.btn.btn-primary(type="button", data-toggle="modal", data-target="#exampleModalCenter")
i.fas.fa-heart.mr-2
span Support Project
script(src="/js/jquery.min.js", integrity="sha384-vk5WoKIaW/vJyUAd9n/wmopsmNhiy+L2Z+SBxGYnUkunIxVxAv/UtMOhba/xskxh")

63
views/mempool-summary.pug

@ -12,47 +12,49 @@ block content
code.json.bg-light #{JSON.stringify(mempoolstats, null, 4)}
if (true)
div(class="card mb-3 shadow-sm")
div(class="card-header")
span(class="h6") Summary
div(class="card-body")
table(class="table details-table mb-0")
div.card.shadow-sm.mb-3
div.card-body.px-2.px-md-3
h3.h6 Summary
hr
table.table.details-table.mb-0
tr
td(class="properties-header") Transaction Count
td.properties-header Transaction Count
td.text-monospace #{getmempoolinfo.size.toLocaleString()}
tr
- var mem1Data = utils.formatLargeNumber(getmempoolinfo.usage, 2);
- var mem2Data = utils.formatLargeNumber(getmempoolinfo.bytes, 2);
td(class="properties-header") Memory Usage
td.properties-header Memory Usage
td.text-monospace
span #{mem1Data[0]} #{mem1Data[1].abbreviation}B
span(class="text-muted") (#{mem2Data[0]} v#{mem2Data[1].abbreviation}B)
span.text-muted (#{mem2Data[0]} v#{mem2Data[1].abbreviation}B)
tr
td(class="properties-header") Total Fees
td.properties-header Total Fees
td.text-monospace
- var currencyValue = mempoolstats["totalFees"];
include includes/value-display.pug
if (getmempoolinfo.size > 0)
tr
td(class="properties-header") Avg Fee
td.properties-header Avg Fee
td.text-monospace
- var currencyValue = mempoolstats["averageFee"];
include ./includes/value-display.pug
tr
td(class="properties-header") Avg Fee per
span.border-dotted(title="Virtual bytes", data-toggle="tooltip") vByte
td.text-monospace #{utils.formatCurrencyAmountInSmallestUnits(mempoolstats["averageFeePerByte"], 2)}/vB
td.properties-header Avg Fee per vB
td.text-monospace
- var feeRateData = utils.formatCurrencyAmountInSmallestUnits(mempoolstats["averageFeePerByte"], 2);
span #{feeRateData.val} #{feeRateData.currencyUnit}/vB
if (getmempoolinfo.size > 0)
div.card.mb-3.shadow-sm
div.card-header
span.h6 Transactions by fee rate
div.card-body
div.card.shadow-sm.mb-3
div.card-body.px-2.px-md-3
h3.h6 Transactions by fee rate
hr
if (false)
#{JSON.stringify(mempoolstats)}
@ -68,7 +70,7 @@ block content
- var feeBucketTxCounts = mempoolstats["satoshiPerByteBucketCounts"];
- var totalfeeBuckets = mempoolstats["satoshiPerByteBucketTotalFees"];
canvas(id="mempoolBarChart", height="100", class="mb-4")
canvas.mb-4(id="mempoolBarChart", height="100")
script(src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.min.js")
@ -132,17 +134,19 @@ block content
- var currencyValue = avgFee;
include ./includes/value-display.pug
td.text-monospace.text-right #{utils.formatCurrencyAmountInSmallestUnits(avgFeeRate, 2)}/vB
td.text-monospace.text-right
- var feeRateData = utils.formatCurrencyAmountInSmallestUnits(avgFeeRate, 2);
span #{feeRateData.val} #{feeRateData.currencyUnit}/vB
else
td.text-monospace.text-right -
td.text-monospace.text-right -
div.card.mb-3.shadow-sm
div.card-header
span.h6 Transactions by size
div.card-body
div.card.shadow-sm.mb-3
div.card-body.px-2.px-md-3
h3.h6 Transactions by size
hr
canvas(id="txSizesBarChart", height="100", class="mb-4")
canvas.mb-4(id="txSizesBarChart", height="100")
script var sizeBucketLabels = [];
script var bgColors = [];
@ -179,12 +183,13 @@ block content
});
div.card.mb-3.shadow-sm
div.card-header
span.h6 Transactions by age
div.card-body
canvas(id="txAgesBarChart", height="100", class="mb-4")
div.card.shadow-sm.mb-3
div.card-body.px-2.px-md-3
h3.h6 Transactions by age
hr
canvas.mb-4(id="txAgesBarChart", height="100")
script var ageBucketLabels = [];
script var bgColors = [];

130
views/node-status.pug

@ -20,51 +20,70 @@ block content
pre
code.json.bg-light #{JSON.stringify(getblockchaininfo, null, 4)}
ul(class='nav nav-tabs mb-3')
li(class="nav-item")
a(data-toggle="tab", href="#tab-details", class="nav-link active", role="tab") Details
li(class="nav-item")
a(data-toggle="tab", href="#tab-json", class="nav-link", role="tab") JSON
div(class="tab-content")
div(id="tab-details", class="tab-pane active", role="tabpanel")
div(class="card mb-3 shadow-sm")
div(class="card-header")
span(class="h6") Summary
div(class="card-body")
ul.nav.nav-tabs.mb-3
li.nav-item
a.nav-link.active(data-toggle="tab", href="#tab-details", role="tab") Details
li.nav-item
a.nav-link(data-toggle="tab", href="#tab-json", role="tab") JSON
div.tab-content
div.tab-pane.active(id="tab-details", role="tabpanel")
div.card.shadow-sm.mb-3
div.card-body.px-2.px-sm-3
h3.h6 Summary
hr
table(class="table details-table mb-0")
tr
td(class="properties-header") Host : Port
td(class="monospace") #{global.rpcClient.host + " : " + global.rpcClient.port}
td.properties-header Host : Port
td.text-monospace #{global.rpcClient.host + " : " + global.rpcClient.port}
tr
td(class="properties-header") Chain
td(class="monospace") #{getblockchaininfo.chain}
td.properties-header Chain
td.text-monospace #{getblockchaininfo.chain}
tr
td(class="properties-header") Version
td(class="monospace") #{getnetworkinfo.version}
span(class="monospace") (#{getnetworkinfo.subversion})
td.properties-header Version
td.text-monospace #{getnetworkinfo.version}
span(class="text-monospace") (#{getnetworkinfo.subversion})
tr
td(class="properties-header") Protocol Version
td(class="monospace") #{getnetworkinfo.protocolversion}
td.properties-header Protocol Version
td.text-monospace #{getnetworkinfo.protocolversion}
if (getblockchaininfo.size_on_disk)
- var sizeData = utils.formatLargeNumber(getblockchaininfo.size_on_disk, 2);
tr
td(class="properties-header") Blockchain Size
td(class="monospace") #{sizeData[0]} #{sizeData[1].abbreviation}B
td.properties-header Blockchain Size
td.text-monospace #{sizeData[0]} #{sizeData[1].abbreviation}B
br
span(class="text-muted") (pruned: #{getblockchaininfo.pruned})
span.text-muted (pruned: #{getblockchaininfo.pruned})
if (getblockchaininfo.softforks)
tr
td.properties-header Soft Forks
td.text-monospace
ul.list-unstyled
each item, itemName in getblockchaininfo.softforks
li
div.d-inline-block.text-right(style="width: 75px;")
span #{itemName}:
span.mr-2.ml-2 type=#{item.type},
if (item.active)
span.mr-2
span status=
span.text-success active
small.text-muted.ml-2 (height: #{item.height.toLocaleString()})
tr
td(class="properties-header") Block Count
td(class="monospace") #{getblockchaininfo.blocks.toLocaleString()}
td.properties-header Block Count
td.text-monospace #{getblockchaininfo.blocks.toLocaleString()}
br
span(class="text-muted") (headers: #{getblockchaininfo.headers.toLocaleString()})
span.text-muted (headers: #{getblockchaininfo.headers.toLocaleString()})
tr
td(class="properties-header") Difficulty
td(class="monospace")
td.properties-header Difficulty
td.text-monospace
- var difficultyData = utils.formatLargeNumber(getblockchaininfo.difficulty, 3);
span(title=parseFloat(getblockchaininfo.difficulty).toLocaleString(), data-toggle="tooltip")
@ -73,8 +92,8 @@ block content
sup #{difficultyData[1].exponent}
tr
td(class="properties-header") Status
td(class="monospace")
td.properties-header Status
td.text-monospace
if (getblockchaininfo.initialblockdownload || getblockchaininfo.headers > getblockchaininfo.blocks)
span Initial block download progress #{(100 * getblockchaininfo.verificationprogress).toLocaleString()}%
else
@ -82,44 +101,45 @@ block content
tr
- var startTimeAgo = moment.duration(uptimeSeconds * 1000);
td(class="properties-header") Uptime
td(class="monospace") #{startTimeAgo.format()}
td.properties-header Uptime
td.text-monospace #{startTimeAgo.format()}
tr
td(class="properties-header") Warnings
td(class="monospace")
td.properties-header Warnings
td.text-monospace
if (getblockchaininfo.warnings && getblockchaininfo.warnings.trim().length > 0)
span #{getblockchaininfo.warnings}
else
span None
div(class="card mb-3 shadow-sm")
div(class="card-header")
span(class="h6") Network Info
div(class="card-body")
table(class="table details-table mb-0")
div.card.shadow-sm.mb-3
div.card-body.px-2.px-sm-3
h3.h6 Network Info
hr
table.table.details-table.mb-0
tr
td(class="properties-header") Peers
td(class="monospace") #{getnetworkinfo.connections.toLocaleString()}
td.properties-header Peers
td.text-monospace #{getnetworkinfo.connections.toLocaleString()}
tr
td(class="properties-header") Network Traffic
td(class="monospace")
td.properties-header Network Traffic
td.text-monospace
- var downData = utils.formatLargeNumber(getnettotals.totalbytesrecv, 2);
- var downRateData = utils.formatLargeNumber(getnettotals.totalbytesrecv / uptimeSeconds, 2);
- var upData = utils.formatLargeNumber(getnettotals.totalbytessent, 2);
- var upRateData = utils.formatLargeNumber(getnettotals.totalbytessent / uptimeSeconds, 2);
span Total Download: #{downData[0]} #{downData[1].abbreviation}B
span(class="text-muted") (avg #{downRateData[0]} #{downRateData[1].abbreviation}B/s)
span.text-muted (avg #{downRateData[0]} #{downRateData[1].abbreviation}B/s)
br
span Total Upload: #{upData[0]} #{upData[1].abbreviation}B
span(class="text-muted") (avg #{upRateData[0]} #{upRateData[1].abbreviation}B/s)
span.text-muted (avg #{upRateData[0]} #{upRateData[1].abbreviation}B/s)
if (global.getnetworkinfo.networks)
tr
td(class="properties-header") Interfaces
td(class="monospace")
td.properties-header Interfaces
td.text-monospace
each item, index in global.getnetworkinfo.networks
div
span.font-weight-bold #{item.name}:
@ -134,8 +154,8 @@ block content
if (item.proxy)
span.text-muted.ml-3 (proxy: #{item.proxy})
td(class="properties-header") Local Addresses
td(class="monospace")
td.properties-header Local Addresses
td.text-monospace
if (global.getnetworkinfo.localaddresses)
each item, index in global.getnetworkinfo.localaddresses
div #{item.address}:#{item.port}
@ -146,14 +166,14 @@ block content
div(id="tab-json", class="tab-pane", role="tabpanel")
ul(class='nav nav-pills mb-3')
li(class="nav-item")
li.nav-item
a(data-toggle="tab", href="#tab-getblockchaininfo", class="nav-link active", role="tab") getblockchaininfo
li(class="nav-item")
li.nav-item
a(data-toggle="tab", href="#tab-getnettotals", class="nav-link", role="tab") getnettotals
li(class="nav-item")
li.nav-item
a(data-toggle="tab", href="#tab-getnetworkinfo", class="nav-link", role="tab") getnetworkinfo
div(class="tab-content")
div.tab-content
div(id="tab-getblockchaininfo", class="tab-pane active", role="tabpanel")
pre
code.json.bg-light(data-lang="json") #{JSON.stringify(getblockchaininfo, null, 4)}

22
views/peers.pug

@ -24,13 +24,13 @@ block content
span Peers
hr
ul(class='nav nav-tabs mb-3')
li(class="nav-item")
a(data-toggle="tab", href="#tab-summary", class="nav-link active", role="tab") Summary
li(class="nav-item")
a(data-toggle="tab", href="#tab-json", class="nav-link", role="tab") JSON
ul.nav.nav-tabs.mb-3
li.nav-item
a.nav-link.active(data-toggle="tab", href="#tab-summary", role="tab") Summary
li.nav-item
a.nav-link(data-toggle="tab", href="#tab-json", role="tab") JSON
div(class="tab-content")
div.tab-content
div(id="tab-summary", class="tab-pane active", role="tabpanel")
@ -38,10 +38,10 @@ block content
div(id="map", class="mb-4")
div(class="card mb-4 shadow-sm")
div(class="card-header")
div.card-header
h2(class="h6 mb-0") Summary
div(class="card-body")
div(class="row")
div.card-body
div.row
div(class="col-md-6")
span(class="font-weight-bold") Top Versions
hr
@ -77,14 +77,14 @@ block content
td(class="data-cell text-monospace") #{item[1].toLocaleString()}
div(class="card mb-4 shadow-sm")
div(class="card-header")
div.card-header
h2(class="h6 mb-0") #{peerSummary.getpeerinfo.length}
if (peerSummary.getpeerinfo.length == 1)
span Peer
else
span Peers
div(class="card-body")
div.card-body
table(class="table table-striped table-responsive-sm data-table mt-4")
thead
tr

8
views/terminal.pug

@ -4,11 +4,11 @@ block headContent
title RPC Terminal
block content
div(class="row")
div(class="col")
div.row
div.col
h1.h3 RPC Terminal
div(class="col")
div.col
if (!config.demoSite && (!config.credentials.rpc || !config.credentials.rpc.rpc))
span(style="float: right;")
a(href="/disconnect", class="btn btn-secondary") Disconnect from node
@ -19,7 +19,7 @@ block content
Use this interactive terminal to send RPC commands to your node. Results will be shown inline. To browse all available RPC commands you can use the [RPC Browser](/rpc-browser).
div(class="card mb-3 shadow-sm")
div(class="card-body")
div.card-body
form(id="terminal-form")
div(class="form-group")
label(for="input-cmd") Command

225
views/transaction.pug

@ -10,18 +10,18 @@ block headContent
block content
if (result && result.getrawtransaction)
h1(class="h3 word-wrap") Transaction
h1.h3.word-wrap Transaction
br
small(class="text-monospace") #{txid}
small.text-monospace #{txid}
hr
ul(class='nav nav-tabs mb-3')
li(class="nav-item")
a(data-toggle="tab", href="#tab-details", class="nav-link active", role="tab") Details
li(class="nav-item")
a(data-toggle="tab", href="#tab-scripts", class="nav-link", role="tab") Scripts
li(class="nav-item")
a(data-toggle="tab", href="#tab-json", class="nav-link", role="tab") JSON
ul.nav.nav-tabs.mb-3
li.nav-item
a.nav-link.active(data-toggle="tab", href="#tab-details", role="tab") Details
li.nav-item
a.nav-link(data-toggle="tab", href="#tab-scripts", role="tab") Scripts
li.nav-item
a.nav-link(data-toggle="tab", href="#tab-json", role="tab") JSON
- DecimalRounded = Decimal.clone({ precision: 4, rounding: 2 })
@ -38,7 +38,7 @@ block content
each vout, voutIndex in result.getrawtransaction.vout
- totalOutputValue = totalOutputValue.plus(new Decimal(vout.value));
div(class="tab-content")
div.tab-content
div(id="tab-details", class="tab-pane active", role="tabpanel")
if (global.specialTransactions && global.specialTransactions[txid])
div(class="alert alert-primary shadow-sm", style="padding-bottom: 0;")
@ -68,20 +68,22 @@ block content
if (!result.getrawtransaction.confirmations || result.getrawtransaction.confirmations == 0)
- isTxConfirmed = false;
div(class="card mb-3 shadow-sm")
div(class="card-header")
span(class="h6") Summary
div(class="card-body")
div.card.shadow-sm.mb-3
div.card-body.px-2.px-md-3
h2.h6.mb-0 Summary
hr
div.clearfix
if (!isTxConfirmed)
div(class="row")
div(class="summary-table-label") Status
div(class="summary-table-content monospace")
div.row
div.summary-table-label Status
div.summary-table-content.text-monospace
span(class="text-warning") Unconfirmed
div(class="row")
div(class="summary-table-label")
div.row
div.summary-table-label
span.border-dotted(title="Whether this unconfirmed transaction is replaceable using replace-by-fee (RBF)", data-toggle="tooltip") RBF
div(class="summary-table-content monospace")
div.summary-table-content.text-monospace
- var replaceable = false;
each vin, vinIndex in result.getrawtransaction.vin
// ref: https://github.com/bitcoin/bips/blob/master/bip-0125.mediawiki#summary
@ -99,43 +101,51 @@ block content
if (isTxConfirmed)
div(class="row")
div(class="summary-table-label") Block
div(class="summary-table-content monospace")
div.row
div.summary-table-label Block
div.summary-table-content.text-monospace
a(href=("/block/" + result.getrawtransaction.blockhash)) #{result.getrawtransaction.blockhash}
if (result.getblock.height)
br
span (##{result.getblock.height.toLocaleString()})
if (isTxConfirmed)
div(class="row")
div(class="summary-table-label") Timestamp
div(class="summary-table-content monospace")
div.row
div.summary-table-label Date
div.summary-table-content.text-monospace
if (result.getrawtransaction.time)
td(class="monospace") #{moment.utc(new Date(result.getrawtransaction["time"] * 1000)).format("Y-MM-DD HH:mm:ss")} utc
- var timeAgoTime = result.getrawtransaction["time"];
include includes/time-ago.pug
td.text-monospace
- var timestampHuman = result.getrawtransaction.time;
include includes/timestamp-human.pug
small.ml-1 utc
br
- var timeAgoTime = result.getrawtransaction.time;
span.text-muted (
include includes/time-ago-text.pug
span )
div(class="row")
div(class="summary-table-label") Version
div(class="summary-table-content monospace") #{result.getrawtransaction.version}
div.row
div.summary-table-label Version
div.summary-table-content.text-monospace #{result.getrawtransaction.version}
div(class="row")
div(class="summary-table-label") Size
div(class="summary-table-content monospace")
div.row
div.summary-table-label Size
div.summary-table-content.text-monospace
if (result.getrawtransaction.vsize != result.getrawtransaction.size)
span #{result.getrawtransaction.vsize.toLocaleString()}
span.border-dotted(title="Virtual bytes", data-toggle="tooltip") vB
br
span(class="text-muted") (#{result.getrawtransaction.size.toLocaleString()} B)
span.text-muted (#{result.getrawtransaction.size.toLocaleString()} B)
else
span #{result.getrawtransaction.size.toLocaleString()} B
if (result.getrawtransaction.locktime > 0)
div(class="row")
div(class="summary-table-label") Locktime
div(class="summary-table-content monospace")
div.row
div.summary-table-label Locktime
div.summary-table-content.text-monospace
if (result.getrawtransaction.locktime < 500000000)
span Spendable in block
a(href=("/block-height/" + result.getrawtransaction.locktime)) #{result.getrawtransaction.locktime.toLocaleString()}
@ -148,9 +158,9 @@ block content
i(class="fas fa-info-circle")
if (isTxConfirmed)
div(class="row")
div(class="summary-table-label") Confirmations
div(class="summary-table-content monospace")
div.row
div.summary-table-label Confirmations
div.summary-table-content.text-monospace
if (!result.getrawtransaction.confirmations || result.getrawtransaction.confirmations == 0)
strong(class="text-warning") 0 (unconfirmed)
@ -162,85 +172,88 @@ block content
if (result.getrawtransaction.vin[0].coinbase)
div(class="row")
div(class="summary-table-label") Fees Collected
div(class="summary-table-content monospace")
div.row
div.summary-table-label Fees Collected
div.summary-table-content.text-monospace
- var currencyValue = new Decimal(totalOutputValue).minus(totalInputValue);
include includes/value-display.pug
- var blockRewardMax = coinConfig.blockRewardFunction(result.getblock.height, global.activeBlockchain);
if (parseFloat(totalOutputValue) < parseFloat(blockRewardMax))
div(class="row")
div(class="summary-table-label")
div.row
div.summary-table-label
span.border-dotted(data-toggle="tooltip" title="The miner of this transaction's block failed to collect this value. As a result, it is lost.") Fees Destroyed
div(class="summary-table-content monospace text-danger")
div.summary-table-content.text-monospace.text-danger
- var currencyValue = new Decimal(blockRewardMax).minus(totalOutputValue);
include includes/value-display.pug
if (minerInfo)
- var minerInfo = utils.getMinerFromCoinbaseTx(result.getrawtransaction);
div(class="row")
div(class="summary-table-label") Miner
div(class="summary-table-content monospace")
div.row
div.summary-table-label Miner
div.summary-table-content.text-monospace
if (minerInfo)
span #{minerInfo.name}
if (minerInfo.identifiedBy)
span(data-toggle="tooltip", title=("Identified by: " + minerInfo.identifiedBy))
i(class="fas fa-info-circle")
i.fas.fa-info-circle
else
span ?
span(data-toggle="tooltip", title="Unable to identify miner")
i(class="fas fa-info-circle")
i.fas.fa-info-circle
else
- var feePaid = new Decimal(totalInputValue).minus(totalOutputValue);
div(class="row")
div(class="summary-table-label") Fee Paid
div(class="summary-table-content monospace")
div.row
div.summary-table-label Fee Paid
div.summary-table-content.text-monospace
- var currencyValue = feePaid;
include includes/value-display.pug
if (feePaid > 0)
span.ml-2(title=(utils.formatCurrencyAmount(totalInputValue, currencyFormatType) + " - " + utils.formatCurrencyAmount(totalOutputValue, currencyFormatType)), data-toggle="tooltip")
- var item1 = utils.formatCurrencyAmount(totalInputValue, currencyFormatType);
- var item2 = utils.formatCurrencyAmount(totalOutputValue, currencyFormatType);
span.ml-2(title=(item1.simpleVal + " - " + item2.simpleVal), data-toggle="tooltip")
i(class="fas fa-ellipsis-h")
if (feePaid > 0)
div(class="row")
div(class="summary-table-label") Fee Rate
div(class="summary-table-content monospace")
div.row
div.summary-table-label Fee Rate
div.summary-table-content.text-monospace
if (result.getrawtransaction.vsize != result.getrawtransaction.size)
span #{utils.addThousandsSeparators(new DecimalRounded(totalInputValue).minus(totalOutputValue).dividedBy(result.getrawtransaction.vsize).times(100000000))} sat/
span.border-dotted(title="Virtual bytes" data-toggle="tooltip") vB
br
span(class="text-muted") (#{utils.addThousandsSeparators(new DecimalRounded(totalInputValue).minus(totalOutputValue).dividedBy(result.getrawtransaction.size).times(100000000))} sat/B)
span.text-muted (#{utils.addThousandsSeparators(new DecimalRounded(totalInputValue).minus(totalOutputValue).dividedBy(result.getrawtransaction.size).times(100000000))} sat/B)
if (result.getrawtransaction.vin[0].coinbase)
div(class="card mb-3 shadow-sm")
div(class="card-header")
h2(class="h6 mb-0") Coinbase
div(class="card-body")
div.card.shadow-sm.mb-3
div.card-body.px-2.px-md-3
h3.h6.mb-0 Coinbase
hr
h6 Hex
div(class="bg-light px-4 py-3 mb-3")
span(class="monospace word-wrap") #{result.getrawtransaction.vin[0].coinbase}
div.bg-light.px-2.py-2.mb-3.border
span.text-monospace.word-wrap #{result.getrawtransaction.vin[0].coinbase}
h6 Decoded
div(class="bg-light px-4 py-3 mb-3")
span(class="monospace word-wrap") #{utils.hex2ascii(result.getrawtransaction.vin[0].coinbase)}
div.bg-light.px-2.py-2.border
span.text-monospace.word-wrap #{utils.hex2ascii(result.getrawtransaction.vin[0].coinbase)}
div(class="card mb-3 shadow-sm")
div(class="card-header")
h2(class="h6 mb-0")
div.card.shadow-sm.mb-3
div.card-body.px-2.px-md-3
h2.h6.mb-0
span #{result.getrawtransaction.vin.length.toLocaleString()}
if (result.getrawtransaction.vin.length == 1)
span Input
else
span Inputs
span ,
span.mx-2.text-muted.text-small /
span #{result.getrawtransaction.vout.length.toLocaleString()}
if (result.getrawtransaction.vout.length == 1)
@ -248,8 +261,10 @@ block content
else
span Outputs
hr
div(class="card-body")
- var tx = result.getrawtransaction;
- var txInputs = result.txInputs;
- var blockHeight = -1;
@ -259,48 +274,47 @@ block content
if (mempoolDetails)
if (mempoolDetails.ancestors.length > 0)
div(class="card mb-3 shadow-sm")
div(class="card-header")
h2(class="h6 mb-0")
div.card.shadow-sm.mb-3
div.card-body.px-2.px-md-3
h3.h6.mb-0
span #{mempoolDetails.ancestors.length.toLocaleString()}
if (mempoolDetails.ancestors.length == 1)
span Ancestor
else
span Ancestors
hr
div(class="card-body")
ol.mb-0
each ancestorTxid, ancestorIndex in mempoolDetails.ancestors
li
a.monospace(href=("/tx/" + ancestorTxid)) #{ancestorTxid}
a.text-monospace(href=("/tx/" + ancestorTxid)) #{ancestorTxid}
if (mempoolDetails.descendants.length > 0)
div(class="card mb-3 shadow-sm")
div(class="card-header")
h2(class="h6 mb-0")
div.card.shadow-sm.mb-3
div.card-body.px-2.px-md-3
h3.h6.mb-0
span #{mempoolDetails.descendants.length.toLocaleString()}
if (mempoolDetails.descendants.length == 1)
span Descendant
else
span Descendants
hr
div(class="card-body")
ol.mb-0
each descendantTxid, descendantIndex in mempoolDetails.descendants
li
a.monospace(href=("/tx/" + descendantTxid)) #{descendantTxid}
a.text-monospace(href=("/tx/" + descendantTxid)) #{descendantTxid}
- var fontawesomeInputName = "sign-in-alt";
- var fontawesomeOutputName = "sign-out-alt";
div(id="tab-scripts", class="tab-pane", role="tabpanel")
div(class="card mb-3 shadow-sm")
div(class="card-header")
span(class="h6") Input Scripts
div(class="card-body")
table(class="table table-striped mb-5")
div.card.shadow-sm.mb-3
div.card-body.px-2.px-md-3
h3.h6.mb-0 Input Scripts
hr
table.table.table-striped
thead
tr
th(style="width: 50px;")
@ -309,27 +323,28 @@ block content
each vin, vinIndex in result.getrawtransaction.vin
tr
th(class="pl-0")
a(data-toggle="tooltip", title=("Input #" + (vinIndex + 1)), style="white-space: nowrap;")
a(data-toggle="tooltip", title=("Input #" + vinIndex), style="white-space: nowrap;")
i(class=("fas fa-" + fontawesomeInputName + " mr-2"))
span #{(vinIndex + 1)}
small ##{vinIndex.toLocaleString()}
td(class="word-wrap text-break monospace")
td.word-wrap.text-break.text-monospace
if (vin.scriptSig && vin.scriptSig.asm)
span #{vin.scriptSig.asm}
else if (vin.coinbase)
div(class="monospace", style="line-height: 1.75em;")
span(class="rounded bg-primary text-white px-2 py-1 mr-2") coinbase
div.text-monospace(style="line-height: 1.75em;")
small.rounded.bg-primary.text-white.px-2.py-1.mr-2 coinbase
br
span #{vin.coinbase}
br
span(class="text-muted") (decoded) #{utils.hex2ascii(vin.coinbase)}
span.text-muted (decoded) #{utils.hex2ascii(vin.coinbase)}
div.card.shadow-sm.mb-3
div.card-body.px-2.px-md-3
h3.h6.mb-0 Output Scripts
hr
div(class="card mb-3 shadow-sm")
div(class="card-header")
span(class="h6") Output Scripts
div(class="card-body")
table(class="table table-striped")
table.table.table-striped
thead
tr
th(style="width: 50px;")
@ -340,14 +355,14 @@ block content
th(class="pl-0")
a(data-toggle="tooltip", title=("Output #" + (voutIndex + 1)), style="white-space: nowrap;")
i(class=("fas fa-" + fontawesomeOutputName + " mr-2"))
span #{(voutIndex + 1)}
small ##{voutIndex.toLocaleString()}
td(class="word-wrap monospace")
td(class="word-wrap text-monospace")
if (vout.scriptPubKey && vout.scriptPubKey.asm)
span #{vout.scriptPubKey.asm}
if (vout.scriptPubKey.asm.startsWith("OP_RETURN"))
br
span(class="text-muted") (decoded) #{utils.hex2ascii(vout.scriptPubKey.asm)}
span.text-muted (decoded) #{utils.hex2ascii(vout.scriptPubKey.asm)}
div(id="tab-json", class="tab-pane", role="tabpanel")
h3.h5 Transaction

4
views/tx-stats.pug

@ -16,7 +16,7 @@ block content
#{JSON.stringify(txStats.txCounts.length)}
if (true)
div(class="row")
div.row
div(class="col-lg-6")
script var txCountDataDay = [];
each item, index in txStatsDay.txCounts
@ -50,7 +50,7 @@ block content
include ./includes/line-graph.pug
div(class="row")
div.row
div(class="col-lg-6")
script var txRateDataDay = [];
each item, index in txStatsDay.txRates

14
views/unconfirmed-transactions.pug

@ -14,24 +14,24 @@ block content
if (mempoolDetails)
- var txCount = mempoolDetails.txCount;
div(class="card mb-3 shadow-sm")
div(class="card-header")
span(class="h6")
div.card.shadow-sm.mb-3
div.card-header
h3.h6
if (txCount == 1)
span 1 Transaction
else
span #{txCount.toLocaleString()} Transactions
div(class="card-body")
div.card-body
each tx, txIndex in mempoolDetails.transactions
div(class="xcard bg-light rounded shadow-sm mb-4")
div(class="card-header text-monospace")
div.card.shadow-sm.mb-3
div.card-header.text-monospace
if (tx && tx.txid)
strong ##{(txIndex + offset).toLocaleString()}
span &ndash;
a(href=("/tx/" + tx.txid)) #{tx.txid}
div(class="card-body")
div.card-body
- var txInputs = mempoolDetails.txInputsByTransaction[tx.txid];
- var blockHeight = -1;

Loading…
Cancel
Save