33// Distributed under the MIT software license, see the accompanying
44// file COPYING or http://www.opensource.org/licenses/mit-license.php.
55
6+ #include " blockchain.h"
7+
68#include " amount.h"
79#include " chain.h"
810#include " chainparams.h"
@@ -74,7 +76,7 @@ UniValue blockheaderToJSON(const CBlockIndex* blockindex)
7476 return result;
7577}
7678
77- UniValue blockToJSON (const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false )
79+ UniValue blockToJSON (const CBlock& block, const CBlockIndex* blockindex, bool txDetails)
7880{
7981 UniValue result (UniValue::VOBJ);
8082 result.push_back (Pair (" hash" , blockindex->GetBlockHash ().GetHex ()));
@@ -342,7 +344,7 @@ void entryToJSON(UniValue &info, const CTxMemPoolEntry &e)
342344 info.push_back (Pair (" depends" , depends));
343345}
344346
345- UniValue mempoolToJSON (bool fVerbose = false )
347+ UniValue mempoolToJSON (bool fVerbose )
346348{
347349 if (fVerbose )
348350 {
@@ -1421,6 +1423,35 @@ static T CalculateTruncatedMedian(std::vector<T>& scores)
14211423 }
14221424}
14231425
1426+ void CalculatePercentilesByWeight (CAmount result[NUM_GETBLOCKSTATS_PERCENTILES], std::vector<std::pair<CAmount, int64_t >>& scores, int64_t total_weight)
1427+ {
1428+ if (scores.empty ()) {
1429+ return ;
1430+ }
1431+
1432+ std::sort (scores.begin (), scores.end ());
1433+
1434+ // 10th, 25th, 50th, 75th, and 90th percentile weight units.
1435+ const double weights[NUM_GETBLOCKSTATS_PERCENTILES] = {
1436+ total_weight / 10.0 , total_weight / 4.0 , total_weight / 2.0 , (total_weight * 3.0 ) / 4.0 , (total_weight * 9.0 ) / 10.0
1437+ };
1438+
1439+ int64_t next_percentile_index = 0 ;
1440+ int64_t cumulative_weight = 0 ;
1441+ for (const auto & element : scores) {
1442+ cumulative_weight += element.second ;
1443+ while (next_percentile_index < NUM_GETBLOCKSTATS_PERCENTILES && cumulative_weight >= weights[next_percentile_index]) {
1444+ result[next_percentile_index] = element.first ;
1445+ ++next_percentile_index;
1446+ }
1447+ }
1448+
1449+ // Fill any remaining percentiles with the last value.
1450+ for (int64_t i = next_percentile_index; i < NUM_GETBLOCKSTATS_PERCENTILES; i++) {
1451+ result[i] = scores.back ().first ;
1452+ }
1453+ }
1454+
14241455template <typename T>
14251456static inline bool SetHasKeys (const std::set<T>& set) {return false ;}
14261457template <typename T, typename Tk, typename ... Args>
@@ -1454,13 +1485,19 @@ static UniValue getblockstats(const JSONRPCRequest& request)
14541485 " \" avgfeerate\" : xxxxx, (numeric) Average feerate (in satoshis per virtual byte)\n "
14551486 " \" avgtxsize\" : xxxxx, (numeric) Average transaction size\n "
14561487 " \" blockhash\" : xxxxx, (string) The block hash (to check for potential reorgs)\n "
1488+ " \" feerate_percentiles\" : [ (array of numeric) Feerates at the 10th, 25th, 50th, 75th, and 90th percentile weight unit (in satoshis per virtual byte)\n "
1489+ " \" 10th_percentile_feerate\" , (numeric) The 10th percentile feerate\n "
1490+ " \" 25th_percentile_feerate\" , (numeric) The 25th percentile feerate\n "
1491+ " \" 50th_percentile_feerate\" , (numeric) The 50th percentile feerate\n "
1492+ " \" 75th_percentile_feerate\" , (numeric) The 75th percentile feerate\n "
1493+ " \" 90th_percentile_feerate\" , (numeric) The 90th percentile feerate\n "
1494+ " ],\n "
14571495 " \" height\" : xxxxx, (numeric) The height of the block\n "
14581496 " \" ins\" : xxxxx, (numeric) The number of inputs (excluding coinbase)\n "
14591497 " \" maxfee\" : xxxxx, (numeric) Maximum fee in the block\n "
14601498 " \" maxfeerate\" : xxxxx, (numeric) Maximum feerate (in satoshis per virtual byte)\n "
14611499 " \" maxtxsize\" : xxxxx, (numeric) Maximum transaction size\n "
14621500 " \" medianfee\" : xxxxx, (numeric) Truncated median fee in the block\n "
1463- " \" medianfeerate\" : xxxxx, (numeric) Truncated median feerate (in satoshis per virtual byte)\n "
14641501 " \" mediantime\" : xxxxx, (numeric) The block median time past\n "
14651502 " \" mediantxsize\" : xxxxx, (numeric) Truncated median transaction size\n "
14661503 " \" minfee\" : xxxxx, (numeric) Minimum fee in the block\n "
@@ -1529,13 +1566,13 @@ static UniValue getblockstats(const JSONRPCRequest& request)
15291566 const bool do_all = stats.size () == 0 ; // Calculate everything if nothing selected (default)
15301567 const bool do_mediantxsize = do_all || stats.count (" mediantxsize" ) != 0 ;
15311568 const bool do_medianfee = do_all || stats.count (" medianfee" ) != 0 ;
1532- const bool do_medianfeerate = do_all || stats.count (" medianfeerate " ) != 0 ;
1569+ const bool do_feerate_percentiles = do_all || stats.count (" feerate_percentiles " ) != 0 ;
15331570 const bool loop_inputs = do_all || stats.count (" utxo_size_inc" );
1534- const bool loop_outputs = do_all || do_medianfee || do_medianfeerate || loop_inputs ||
1571+ const bool loop_outputs = do_all || do_medianfee || do_feerate_percentiles || loop_inputs ||
15351572 SetHasKeys (stats, " totalfee" , " avgfee" , " avgfeerate" , " minfee" , " maxfee" , " minfeerate" , " maxfeerate" , " total_out" );
15361573 const bool do_calculate_size = do_mediantxsize ||
15371574 SetHasKeys (stats, " total_size" , " avgtxsize" , " mintxsize" , " maxtxsize" , " swtotal_size" );
1538- const bool do_calculate_weight = do_all || SetHasKeys (stats, " total_weight" , " avgfeerate" , " swtotal_weight" , " avgfeerate" , " medianfeerate " , " minfeerate" , " maxfeerate" );
1575+ const bool do_calculate_weight = do_all || SetHasKeys (stats, " total_weight" , " avgfeerate" , " swtotal_weight" , " avgfeerate" , " feerate_percentiles " , " minfeerate" , " maxfeerate" );
15391576 const bool do_calculate_sw = do_all || SetHasKeys (stats, " swtxs" , " swtotal_size" , " swtotal_weight" );
15401577
15411578 CAmount maxfee = 0 ;
@@ -1555,7 +1592,7 @@ static UniValue getblockstats(const JSONRPCRequest& request)
15551592 int64_t total_weight = 0 ;
15561593 int64_t utxo_size_inc = 0 ;
15571594 std::vector<CAmount> fee_array;
1558- std::vector<CAmount> feerate_array;
1595+ std::vector<std::pair< CAmount, int64_t > > feerate_array;
15591596 std::vector<int64_t > txsize_array;
15601597
15611598 for (const auto & tx : block.vtx ) {
@@ -1642,26 +1679,34 @@ static UniValue getblockstats(const JSONRPCRequest& request)
16421679
16431680 // New feerate uses satoshis per virtual byte instead of per serialized byte
16441681 CAmount feerate = weight ? (txfee * WITNESS_SCALE_FACTOR) / weight : 0 ;
1645- if (do_medianfeerate ) {
1646- feerate_array.push_back ( feerate);
1682+ if (do_feerate_percentiles ) {
1683+ feerate_array.emplace_back ( std::make_pair ( feerate, weight) );
16471684 }
16481685 maxfeerate = std::max (maxfeerate, feerate);
16491686 minfeerate = std::min (minfeerate, feerate);
16501687 }
16511688 }
16521689
1690+ CAmount feerate_percentiles[NUM_GETBLOCKSTATS_PERCENTILES] = { 0 };
1691+ CalculatePercentilesByWeight (feerate_percentiles, feerate_array, total_weight);
1692+
1693+ UniValue feerates_res (UniValue::VARR);
1694+ for (int64_t i = 0 ; i < NUM_GETBLOCKSTATS_PERCENTILES; i++) {
1695+ feerates_res.push_back (feerate_percentiles[i]);
1696+ }
1697+
16531698 UniValue ret_all (UniValue::VOBJ);
16541699 ret_all.pushKV (" avgfee" , (block.vtx .size () > 1 ) ? totalfee / (block.vtx .size () - 1 ) : 0 );
16551700 ret_all.pushKV (" avgfeerate" , total_weight ? (totalfee * WITNESS_SCALE_FACTOR) / total_weight : 0 ); // Unit: sat/vbyte
16561701 ret_all.pushKV (" avgtxsize" , (block.vtx .size () > 1 ) ? total_size / (block.vtx .size () - 1 ) : 0 );
16571702 ret_all.pushKV (" blockhash" , pindex->GetBlockHash ().GetHex ());
1703+ ret_all.pushKV (" feerate_percentiles" , feerates_res);
16581704 ret_all.pushKV (" height" , (int64_t )pindex->nHeight );
16591705 ret_all.pushKV (" ins" , inputs);
16601706 ret_all.pushKV (" maxfee" , maxfee);
16611707 ret_all.pushKV (" maxfeerate" , maxfeerate);
16621708 ret_all.pushKV (" maxtxsize" , maxtxsize);
16631709 ret_all.pushKV (" medianfee" , CalculateTruncatedMedian (fee_array));
1664- ret_all.pushKV (" medianfeerate" , CalculateTruncatedMedian (feerate_array));
16651710 ret_all.pushKV (" mediantime" , pindex->GetMedianTimePast ());
16661711 ret_all.pushKV (" mediantxsize" , CalculateTruncatedMedian (txsize_array));
16671712 ret_all.pushKV (" minfee" , (minfee == MAX_MONEY) ? 0 : minfee);
0 commit comments