You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
438 lines
14 KiB
438 lines
14 KiB
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
|
|
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)',
|
|
});
|
|
|
|
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);
|
|
});
|
|
|