extends layout block headContent title Block Block block content h1.h3 Block Stats hr if (rpcApiUnsupportedError) div.alert.alert-danger(style="margin-bottom: 200px;") span Your node version doesn't support the span.text-monospace #{rpcApiUnsupportedError.rpc} span RPC. To use this tool you need to upgrade to span.text-monospace v#{rpcApiUnsupportedError.version} span or later. else div.card.shadow-sm.mb-3 div.card-body h3.h6.mb-0 Block Range hr div.clearfix div.float-left.mr-4 div(id="time-range-buttons") h3.h6 Latest N Blocks div.btn-group a.btn.btn-primary.block-count-btn(href="javascript:void(0)", data-blockCount="144") 144 small.ml-2 (~1d) a.btn.btn-outline-primary.block-count-btn(href="javascript:void(0)", data-blockCount="432") 432 small.ml-2 (~3d) a.btn.btn-outline-primary.block-count-btn(href="javascript:void(0)", data-blockCount="1008") 1,008 small.ml-2 (~7d) a.btn.btn-outline-primary.block-count-btn(href="javascript:void(0)", data-blockCount="4320") 4,320 small.ml-2 (~30d) div.float-left.mr-4 div(id="block-selections-buttons") h3.h6 Preconfigured div.dropdown button.btn.btn-outline-primary.dropdown-toggle(type="button", id="preconfigured-dropdown", data-toggle="dropdown", aria-haspopup="true", aria-expanded="false") Selections div.dropdown-menu(aria-labelledby="preconfigured-dropdown") a.dropdown-item(href="javascript:void(0)", data-blocks="0-199") First 200 Blocks a.dropdown-item(href="javascript:void(0)", data-blocks="209900-210100") First Halving ±100 a.dropdown-item(href="javascript:void(0)", data-blocks="419900-420100") Second Halving ±100 a.dropdown-item(href="javascript:void(0)", data-blocks="481724-481924") SegWit Activation ±100 div.float-left div(id="time-range-buttons") h3.h6 Custom Range form.form-inline.mr-3(id="custom-range-form") div.input-group input.form-control(type="text", id="custom-range-start", placeholder="min height", style="width: 125px;") input.form-control(type="text", id="custom-range-end", placeholder="max height", style="width: 125px;") div.input-group-append button.btn.btn-primary(type="submit") Go div#progress-wrapper.mb-huge div.card.shadow-sm.mb-3 div.card-body h4.h6 Loading blocks: span(id="block-progress-text") div.progress(id="progress-bar", style="height: 7px;") div.progress-bar(id="data-progress", role="progressbar", aria-valuenow="0", aria-valuemin="0" ,aria-valuemax="100") - var graphIds = ["fee-rates", "max-fee-rates", "total-fee", "min-fees", "max-fees", "inputs-outputs", "tx-sizes", "max-tx-sizes", "volumes", "weights-sizes", "sw-txs", "subsidy", "mediantime", "txs", "utxo_increase"]; - var graphTitles = ["Fee Rates (avg, median, min)", "Maximum Fee Rate", "Total Fees", "Minimum Tx Fee", "Maximum Tx Fee", "Input / Output Count", "Tx Sizes (avg, median, min)", "Maximum Tx Size", "Output Volume", "Block Weight and Size", "SegWit Tx Count", "Subsidy", "Timestamp", "Tx Count", "UTXO Δ"]; div(id="main-content", style="display: none;") div.card.shadow-sm.mb-3 div.card-body div.clearfix each graphId, graphIndex in graphIds div.float-left.mr-3.mb-1 div.form-check input.form-check-input.graph-toggle-checkbox(type="checkbox", id=("checkbox-" + graphId) checked="checked", data-graphId=graphId, autocomplete="off") label.form-check-label(for=("checkbox-" + graphId)) #{graphTitles[graphIndex]} div.float-left.mr-3.mb-1 a.mr-3(id="link-show-all", href="javascript:void(0)") show all a(id="link-hide-all", href="javascript:void(0)") hide all div.row.clearfix each graphId, graphIndex in graphIds div.col-lg-6.float-left div.card.shadow-sm.mb-3.graph-wrapper(id=`graph-wrapper-${graphId}`) div.card-body h3.h6.mb-0 #{graphTitles[graphIndex]} hr canvas.mb-3(id=graphId) block endOfBody script(src="/js/chart.bundle.min.js", integrity="sha384-qgOtiGNaHh9fVWUnRjyHlV39rfbDcvPPkEzL1RHvsHKbuqUqM6uybNuVnghY2z4/") script(src='/js/decimal.js') script. var currentBlockHeight = !{currentBlockHeight}; $(document).ready(function() { $("#time-range-buttons .block-count-btn").on("click", function() { // highlight current selection $("#time-range-buttons .block-count-btn").removeClass("btn-primary").addClass("btn-outline-primary"); $(this).addClass("btn-primary").removeClass("btn-outline-primary"); $("#preconfigured-dropdown").removeClass("btn-primary").addClass("btn-outline-primary"); var blockCount = parseInt($(this).attr("data-blockCount")); $("#data-progress").css("width", "0%"); $("#block-progress-text").text(""); $("#main-content").hide(); $("#progress-wrapper").show(); getData(currentBlockHeight, blockCount, 15); }); $("#block-selections-buttons .dropdown-item").on("click", function() { // highlight current selection $("#time-range-buttons .block-count-btn").removeClass("btn-primary").addClass("btn-outline-primary"); $("#preconfigured-dropdown").removeClass("btn-outline-primary").addClass("btn-primary"); var blocks = $(this).attr("data-blocks"); var bStartEnd = blocks.split("-"); var bStart = parseInt(bStartEnd[0]); var bEnd = parseInt(bStartEnd[1]); $("#data-progress").css("width", "0%"); $("#block-progress-text").text(""); $("#main-content").hide(); $("#progress-wrapper").show(); getData(bEnd, bEnd - bStart + 1, 15); }); $("#custom-range-form").on("submit", function() { // highlight current selection $("#time-range-buttons .block-count-btn").removeClass("btn-primary").addClass("btn-outline-primary"); $("#preconfigured-dropdown").removeClass("btn-primary").addClass("btn-outline-primary"); var bStart = parseInt($("#custom-range-start").val()); var bEnd = parseInt($("#custom-range-end").val()); $("#data-progress").css("width", "0%"); $("#block-progress-text").text(""); $("#main-content").hide(); $("#progress-wrapper").show(); getData(bEnd, bEnd - bStart + 1, 15); return false; }); $(".graph-toggle-checkbox").change(function() { var graphId = $(this).attr("data-graphId"); $("#graph-wrapper-" + graphId).toggle(); }); $("#link-show-all").on("click", function() { $(".graph-toggle-checkbox").each(function() { $(this).prop("checked", true); }); $(".graph-wrapper").each(function() { $(this).show(); }); }); $("#link-hide-all").on("click", function() { $(".graph-toggle-checkbox").each(function() { $(this).prop("checked", false); }); $(".graph-wrapper").each(function() { $(this).hide(); }); }); }); function getData(blockStart, count, chunkSize) { $("#time-range-buttons .block-count-btn").addClass("disabled"); $("#block-selections-buttons .dropdown-item").addClass("disabled"); var chunks = []; var blockIndex = blockStart; while (blockIndex > blockStart - count) { var chunk = []; for (var i = blockIndex; (i > (blockIndex - chunkSize) && i > (blockStart - count)); i--) { chunk.push(i); } blockIndex -= chunkSize; chunks.push(chunk); } console.log(JSON.stringify(chunks)); //alert(JSON.stringify(chunks)); var results = []; var statusCallback = function(chunkIndexDone, chunkCount) { //console.log("Done: " + Math.min(((chunkIndexDone + 1) * chunkSize), count) + " of " + count); var wPercent = `${parseInt(100 * (chunkIndexDone + 1) / parseFloat(chunkCount))}%`; $("#data-progress").css("width", wPercent); $("#block-progress-text").text(`${Math.min(((chunkIndexDone + 1) * chunkSize), count).toLocaleString()} of ${count.toLocaleString()} (${wPercent})`); }; var finishedCallback = function() { $("#time-range-buttons .block-count-btn").removeClass("disabled"); $("#block-selections-buttons .dropdown-item").removeClass("disabled"); var summary = summarizeData(results); var red = "#dc3545"; var green = "#28a745"; var blue = "#007bff"; createGraph("fee-rates", ["avg-fee-rate", "median-fee-rate", "min-fee-rate"], [summary.avgfeerate, summary.medianfeerate, summary.minfeerate], [blue, red, green]); createGraph("max-fee-rates", ["max-fee-rate"], [summary.maxfeerate], [blue]); createGraph("total-fee", ["total-fee"], [summary.totalfee], [blue]); createGraph("min-fees", ["min-fee"], [summary.minfee], [blue]); createGraph("max-fees", ["max-fee"], [summary.maxfee], [blue]); createGraph("inputs-outputs", ["inputs", "outputs"], [summary.inputs, summary.outputs], [blue, green]); createGraph("tx-sizes", ["avg-tx-size", "median-tx-size", "min-tx-size"], [summary.avgtxsize, summary.mediantxsize, summary.mintxsize], [blue, red, green]); createGraph("max-tx-sizes", ["max-tx-size"], [summary.maxtxsize], [blue]); createGraph("volumes", ["volume"], [summary.totaloutput], [blue]); createGraph("weights-sizes", ["weight", "size"], [summary.weight, summary.size], [blue, green]); createGraph("sw-txs", ["swtxs"], [summary.swtxs], [blue]); createGraph("subsidy", ["subsidy"], [summary.subsidy], [blue]); createGraph("mediantime", ["mediantime"], [summary.mediantime], [blue]); createGraph("txs", ["txs"], [summary.txs], [blue]); createGraph("utxo_increase", ["utxo_increase"], [summary.utxo_increase], [blue]); $("#main-content").show(); $("#progress-wrapper").hide(); }; getBlockData(results, chunks, 0, statusCallback, finishedCallback); } function getBlockData(results, chunks, chunkIndex, statusCallback, finishedCallback) { if (chunkIndex > chunks.length - 1) { finishedCallback(); return; } var url = `/api/block-stats-by-height/${chunks[chunkIndex]}`; //console.log(url); $.ajax({ url: url }).done(function(result) { for (var i = 0; i < result.length; i++) { results.push(result[i]); } statusCallback(chunkIndex, chunks.length); getBlockData(results, chunks, chunkIndex + 1, statusCallback, finishedCallback); }); } function summarizeData(results) { var summary = {}; summary.avgfeerate = []; summary.medianfeerate = []; summary.minfeerate = []; summary.maxfeerate = []; summary.totalfee = []; summary.minfee = []; summary.maxfee = []; summary.inputs = []; summary.outputs = []; summary.avgtxsize = []; summary.mediantxsize = []; summary.mintxsize = []; summary.maxtxsize = []; summary.totaloutput = []; summary.weight = []; summary.size = []; summary.swtxs = []; summary.subsidy = []; summary.mediantime = []; summary.txs = []; summary.utxo_increase = []; for (var i = results.length - 1; i >= 0; i--) { summary.avgfeerate.push({x:results[i].height, y:results[i].avgfeerate}); summary.medianfeerate.push({x:results[i].height, y:results[i].feerate_percentiles[2]}); summary.minfeerate.push({x:results[i].height, y:results[i].minfeerate}); summary.maxfeerate.push({x:results[i].height, y:results[i].maxfeerate}); summary.totalfee.push({x:results[i].height, y:new Decimal(results[i].totalfee).dividedBy(100000000)}); summary.minfee.push({x:results[i].height, y:results[i].minfee}); summary.maxfee.push({x:results[i].height, y:results[i].maxfee}); summary.inputs.push({x:results[i].height, y:results[i].ins}); summary.outputs.push({x:results[i].height, y:results[i].outs}); summary.avgtxsize.push({x:results[i].height, y:results[i].avgtxsize}); summary.mediantxsize.push({x:results[i].height, y:results[i].mediantxsize}); summary.mintxsize.push({x:results[i].height, y:results[i].mintxsize}); summary.maxtxsize.push({x:results[i].height, y:results[i].maxtxsize}); summary.totaloutput.push({x:results[i].height, y:new Decimal(results[i].total_out).dividedBy(100000000)}); summary.weight.push({x:results[i].height, y:results[i].total_weight}); summary.size.push({x:results[i].height, y:results[i].total_size}); summary.swtxs.push({x:results[i].height, y:results[i].swtxs}); summary.subsidy.push({x:results[i].height, y:results[i].subsidy}); summary.mediantime.push({x:results[i].height, y:results[i].mediantime}); summary.txs.push({x:results[i].height, y:results[i].txs}); summary.utxo_increase.push({x:results[i].height, y:results[i].utxo_increase}); } return summary; } var graphsById = {}; function createGraph(chartid, names, datas, colors) { var datasets = []; var yaxes = []; for (var i = 0; i < names.length; i++) { datasets.push({ label: names[i], data: datas[i], borderWidth: 2, borderColor: colors[i], backgroundColor: 'rgba(0, 0, 0, 0)', pointRadius: 1 }); yaxes.push({ scaleLabel: { display: false, //labelString: names[i] } }); } // update data in graph if we already created, otherwise create anew if (graphsById[chartid]) { graph = graphsById[chartid]; graph.data = { datasets: datasets }; graph.update(); } else { var ctx = document.getElementById(chartid).getContext('2d'); var graph = new Chart(ctx, { type: 'line', data: { datasets: datasets }, options: { // disable all animations animation: { duration: 0 }, hover: { animationDuration: 0 }, responsiveAnimationDuration: 0, legend: { display: (datasets.length > 1) }, scales: { xAxes: [{ type: 'linear', position: 'bottom', scaleLabel: { display: true, labelString: 'Block' }, }], } } }); graphsById[chartid] = graph; } } if (rpcApiUnsupportedError == null) script. $(document).ready(function() { getData(currentBlockHeight, 144, 15); });