From e952d9e56d8d28232482106dfeed5ee3934b3c4a Mon Sep 17 00:00:00 2001 From: Dan Janosik Date: Thu, 31 May 2018 23:23:23 -0400 Subject: [PATCH] Improvements to mempool summary. Working on #28 --- app/rpcApi.js | 119 +++++++++++++++++------------- views/mempool-summary.pug | 149 ++++++++++++++++++++------------------ 2 files changed, 148 insertions(+), 120 deletions(-) diff --git a/app/rpcApi.js b/app/rpcApi.js index 78ac81a..236175d 100644 --- a/app/rpcApi.js +++ b/app/rpcApi.js @@ -104,73 +104,90 @@ function getMempoolStats() { return; } - var compiledResult = {}; - - compiledResult.count = 0; - compiledResult.fee_0_5 = 0; - compiledResult.fee_6_10 = 0; - compiledResult.fee_11_25 = 0; - compiledResult.fee_26_50 = 0; - compiledResult.fee_51_75 = 0; - compiledResult.fee_76_100 = 0; - compiledResult.fee_101_150 = 0; - compiledResult.fee_151_max = 0; - - compiledResult.totalfee_0_5 = 0; - compiledResult.totalfee_6_10 = 0; - compiledResult.totalfee_11_25 = 0; - compiledResult.totalfee_26_50 = 0; - compiledResult.totalfee_51_75 = 0; - compiledResult.totalfee_76_100 = 0; - compiledResult.totalfee_101_150 = 0; - compiledResult.totalfee_151_max = 0; - - var totalFee = 0; + var maxFee = 0; + var maxFeePerByte = 0; for (var txid in result) { var txMempoolInfo = result[txid]; - totalFee += txMempoolInfo.modifiedfee; + var fee = txMempoolInfo.modifiedfee; + var feePerByte = txMempoolInfo.modifiedfee / txMempoolInfo.size; - var feeRate = Math.round(txMempoolInfo.modifiedfee * 100000000 / txMempoolInfo.size); + if (fee > maxFee) { + maxFee = txMempoolInfo.modifiedfee; + } + + if (feePerByte > maxFeePerByte) { + maxFeePerByte = txMempoolInfo.modifiedfee / txMempoolInfo.size; + } + } - if (feeRate <= 5) { - compiledResult.fee_0_5++; - compiledResult.totalfee_0_5 += txMempoolInfo.modifiedfee; + var satoshiPerByteBucketMaxima = [1, 5, 10, 15, 20, 25, 50, 75, 100, 150]; + var bucketCount = satoshiPerByteBucketMaxima.length + 1; + + var satoshiPerByteBuckets = []; + var satoshiPerByteBucketLabels = []; + + satoshiPerByteBucketLabels[0] = ("[0 - " + satoshiPerByteBucketMaxima[0] + ")"); + for (var i = 0; i < bucketCount; i++) { + satoshiPerByteBuckets[i] = {"count":0, "totalFees":0, "totalBytes":0}; + + if (i > 0 && i < bucketCount - 1) { + satoshiPerByteBucketLabels[i] = ("[" + satoshiPerByteBucketMaxima[i - 1] + " - " + satoshiPerByteBucketMaxima[i] + ")"); + } + } - } else if (feeRate <= 10) { - compiledResult.fee_6_10++; - compiledResult.totalfee_6_10 += txMempoolInfo.modifiedfee; + satoshiPerByteBucketLabels[bucketCount - 1] = (satoshiPerByteBucketMaxima[satoshiPerByteBucketMaxima.length - 1] + "+"); - } else if (feeRate <= 25) { - compiledResult.fee_11_25++; - compiledResult.totalfee_11_25 += txMempoolInfo.modifiedfee; + var summary = { + "count":0, + "totalFees":0, + "totalBytes":0, + "satoshiPerByteBuckets":satoshiPerByteBuckets, + "satoshiPerByteBucketLabels":satoshiPerByteBucketLabels + }; - } else if (feeRate <= 50) { - compiledResult.fee_26_50++; - compiledResult.totalfee_26_50 += txMempoolInfo.modifiedfee; + for (var txid in result) { + var txMempoolInfo = result[txid]; + var fee = txMempoolInfo.modifiedfee; + var feePerByte = txMempoolInfo.modifiedfee / txMempoolInfo.size; + var satoshiPerByte = feePerByte * 100000000; - } else if (feeRate <= 75) { - compiledResult.fee_51_75++; - compiledResult.totalfee_51_75 += txMempoolInfo.modifiedfee; + var addedToBucket = false; + for (var i = 0; i < satoshiPerByteBucketMaxima.length; i++) { + if (satoshiPerByteBucketMaxima[i] > satoshiPerByte) { + satoshiPerByteBuckets[i]["count"]++; + satoshiPerByteBuckets[i]["totalFees"] += fee; + satoshiPerByteBuckets[i]["totalBytes"] += txMempoolInfo.size; - } else if (feeRate <= 100) { - compiledResult.fee_76_100++; - compiledResult.totalfee_76_100 += txMempoolInfo.modifiedfee; + addedToBucket = true; - } else if (feeRate <= 150) { - compiledResult.fee_101_150++; - compiledResult.totalfee_101_150 += txMempoolInfo.modifiedfee; + break; + } + } - } else { - compiledResult.fee_151_max++; - compiledResult.totalfee_151_max += txMempoolInfo.modifiedfee; + if (!addedToBucket) { + satoshiPerByteBuckets[bucketCount - 1]["count"]++; + satoshiPerByteBuckets[bucketCount - 1]["totalFees"] += fee; + satoshiPerByteBuckets[bucketCount - 1]["totalBytes"] += txMempoolInfo.size; } - compiledResult.count++; + summary["count"]++; + summary["totalFees"] += txMempoolInfo.modifiedfee; + summary["totalBytes"] += txMempoolInfo.size; } - compiledResult.totalFee = totalFee; + summary["averageFee"] = summary["totalFees"] / summary["count"]; + summary["averageFeePerByte"] = summary["totalFees"] / summary["totalBytes"] / summary["count"]; + + summary["satoshiPerByteBucketMaxima"] = satoshiPerByteBucketMaxima; + summary["satoshiPerByteBucketCounts"] = []; + summary["satoshiPerByteBucketTotalFees"] = []; + + for (var i = 0; i < bucketCount; i++) { + summary["satoshiPerByteBucketCounts"].push(summary["satoshiPerByteBuckets"][i]["count"]); + summary["satoshiPerByteBucketTotalFees"].push(summary["satoshiPerByteBuckets"][i]["totalFees"]); + } - resolve(compiledResult); + resolve(summary); }); }); } diff --git a/views/mempool-summary.pug b/views/mempool-summary.pug index 81aba17..b94b82f 100644 --- a/views/mempool-summary.pug +++ b/views/mempool-summary.pug @@ -7,10 +7,11 @@ block content h1(class="h2") Mempool Summary hr - if (getmempoolinfo) - p Data from RPC command - a(href="/rpc-browser?method=getmempoolinfo") getmempoolinfo - + if (false) + pre + code #{JSON.stringify(mempoolstats, null, 4)} + + if (true) table(class="table") tr th(class="table-active properties-header") Transaction Count @@ -20,85 +21,95 @@ block content td(class="monospace") span #{utils.formatBytes(getmempoolinfo.usage)} span(class="text-muted") (virtual size: #{utils.formatBytes(getmempoolinfo.bytes)}) - tr - th(class="table-active properties-header") Max Size - td(class="monospace") #{utils.formatBytes(getmempoolinfo.maxmempool)} - tr - th(class="table-active properties-header") Min Fee - td(class="monospace") #{getmempoolinfo.mempoolminfee.toLocaleString()} tr th(class="table-active properties-header") Total Fees - td(class="monospace") #{utils.formatCurrencyAmount(mempoolstats.totalFee, currencyFormatType)} + td(class="monospace") #{utils.formatCurrencyAmount(mempoolstats["totalFees"], currencyFormatType)} + if (global.exchangeRate) + span + span(data-toggle="tooltip", title=utils.formatExchangedCurrency(mempoolstats["totalFees"])) + i(class="fas fa-exchange-alt") + + tr + th(class="table-active properties-header") Average Fee + td(class="monospace") #{utils.formatCurrencyAmount(mempoolstats["averageFee"], currencyFormatType)} if (global.exchangeRate) span - span(data-toggle="tooltip", title=utils.formatExchangedCurrency(mempoolstats.totalFee)) + span(data-toggle="tooltip", title=utils.formatExchangedCurrency(mempoolstats["averageFee"])) i(class="fas fa-exchange-alt") + tr + th(class="table-active properties-header") Average Fee per Byte + td(class="monospace") #{(mempoolstats["averageFeePerByte"] * 100000000).toFixed(6)} sat/B - h4 Transaction count by fee level + h4 Transactions by fee rate hr if (false) #{JSON.stringify(mempoolstats)} - - var feeBucketLabels = [ "0 - 5 sat/B", "6 - 10 sat/B", "11 - 25 sat/B", "26 - 50 sat/B", "51 - 75 sat/B", "76 - 100 sat/B", "101 - 150 sat/B", "151+ sat/B" ]; - - var feeBucketTxCounts = [ mempoolstats.fee_0_5, mempoolstats.fee_6_10, mempoolstats.fee_11_25, mempoolstats.fee_26_50, mempoolstats.fee_51_75, mempoolstats.fee_76_100, mempoolstats.fee_101_150, mempoolstats.fee_151_max ]; - - var totalfeeBuckets = [ mempoolstats.totalfee_0_5, mempoolstats.totalfee_6_10, mempoolstats.totalfee_11_25, mempoolstats.totalfee_26_50, mempoolstats.totalfee_51_75, mempoolstats.totalfee_76_100, mempoolstats.totalfee_101_150, mempoolstats.totalfee_151_max ]; - - var bgColors = [ "bg-primary", "bg-success", "bg-info", "bg-warning", "bg-danger", "bg-primary progress-bar-striped", "bg-success progress-bar-striped", "bg-info progress-bar-striped" ]; + if (true) + - var feeBucketLabels = [("[0 - " + mempoolstats["satoshiPerByteBucketMaxima"][0] + ")")]; + each item, index in mempoolstats["satoshiPerByteBuckets"] + if (index > 0 && index < mempoolstats["satoshiPerByteBuckets"].length - 1) + - feeBucketLabels.push(("[" + mempoolstats["satoshiPerByteBucketMaxima"][index - 1] + " - " + mempoolstats["satoshiPerByteBucketMaxima"][index] + ")")); + + - feeBucketLabels.push((mempoolstats.satoshiPerByteBucketMaxima[mempoolstats.satoshiPerByteBucketMaxima.length - 1] + "+")); + + - var feeBucketTxCounts = mempoolstats["satoshiPerByteBucketCounts"]; + - var totalfeeBuckets = mempoolstats["satoshiPerByteBucketTotalFees"]; + + canvas(id="mempoolBarChart", height="100", class="mb-4") + + script(src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.min.js") - canvas(id="mempoolBarChart", height="100") + script var feeBucketLabels = []; + script var bgColors = []; + each feeBucketLabel, index in feeBucketLabels + - var percentTx = Math.round(100 * feeBucketTxCounts[index] / getmempoolinfo.size).toLocaleString(); + script feeBucketLabels.push(["#{feeBucketLabel}","#{feeBucketTxCounts[index]} tx (#{percentTx}%)"]); + script bgColors.push("hsl(#{(333 * index / feeBucketLabels.length)}, 100%, 50%)"); - script(src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.min.js") + script var feeBucketTxCounts = [#{feeBucketTxCounts}]; - script var feeBucketLabels = []; - each feeBucketLabel, index in feeBucketLabels - - var percentTx = Math.round(100 * feeBucketTxCounts[index] / getmempoolinfo.size).toLocaleString(); - script feeBucketLabels.push(["#{feeBucketLabel}","#{feeBucketTxCounts[index]} tx (#{percentTx}%)","#{utils.formatCurrencyAmount(totalfeeBuckets[index], currencyFormatType)}"]); + script. + var ctx = document.getElementById("mempoolBarChart").getContext('2d'); + var mempoolBarChart = new Chart(ctx, { + type: 'bar', + data: { + labels: feeBucketLabels, + datasets: [{ + data: feeBucketTxCounts, + backgroundColor: bgColors + }] + }, + options: { + legend: { + display: false + }, + scales: { + yAxes: [{ + ticks: { + beginAtZero:true + } + }] + } + } + }); - script var feeBucketTxCounts = [#{feeBucketTxCounts}]; + table(class="table table-striped table-responsive-sm") + thead + tr + th Fee Rate + th(class="text-right") Tx Count + th(class="text-right") Total Fees + th(class="text-right") Average Fee Rate + tbody + each item, index in mempoolstats["satoshiPerByteBuckets"] + tr + td #{mempoolstats["satoshiPerByteBucketLabels"][index]} + td(class="text-right monospace") #{item["count"].toLocaleString()} + td(class="text-right monospace") #{utils.formatCurrencyAmount(item["totalFees"], currencyFormatType)} - script. - var ctx = document.getElementById("mempoolBarChart").getContext('2d'); - var mempoolBarChart = new Chart(ctx, { - type: 'bar', - data: { - labels: feeBucketLabels, - datasets: [{ - data: feeBucketTxCounts, - backgroundColor: [ - 'rgba(255, 99, 132, 0.3)', - 'rgba(255, 159, 64, 0.3)', - 'rgba(255, 206, 86, 0.3)', - 'rgba(55, 212, 140, 0.3)', - 'rgba(75, 192, 192, 0.3)', - 'rgba(54, 162, 235, 0.3)', - 'rgba(153, 102, 255, 0.3)', - 'rgba(0, 0, 0, 0.3)', - ], - borderColor: [ - 'rgba(255, 99, 132, 1)', - 'rgba(255, 159, 64, 1)', - 'rgba(255, 206, 86, 1)', - 'rgba(55, 212, 140, 1)', - 'rgba(75, 192, 192, 1)', - 'rgba(54, 162, 235, 1)', - 'rgba(153, 102, 255, 1)', - 'rgba(0, 0, 0, 1)', - ], - borderWidth: 2 - }] - }, - options: { - legend: { - display: false - }, - scales: { - yAxes: [{ - ticks: { - beginAtZero:true - } - }] - } - } - }); - \ No newline at end of file + - var avgFeeRate = item["totalFees"] * 100000000 / item["totalBytes"]; + td(class="text-right monospace") #{avgFeeRate.toFixed(2)} sat/B + \ No newline at end of file