mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-07-01 11:11:15 +02:00
rpc: Improve getblockstats
- Fix getblockstats for block height 0 which previously returned an error. - Introduce alternative utxo_*_actual statistics which exclude unspendables: Genesis block, BIP30, unspendable outputs - Update test data - Explicitly test Genesis block results
This commit is contained in:
@ -593,6 +593,10 @@ static CBlockUndo GetUndoChecked(BlockManager& blockman, const CBlockIndex* pblo
|
|||||||
{
|
{
|
||||||
AssertLockHeld(::cs_main);
|
AssertLockHeld(::cs_main);
|
||||||
CBlockUndo blockUndo;
|
CBlockUndo blockUndo;
|
||||||
|
|
||||||
|
// The Genesis block does not have undo data
|
||||||
|
if (pblockindex->nHeight == 0) return blockUndo;
|
||||||
|
|
||||||
if (blockman.IsBlockPruned(pblockindex)) {
|
if (blockman.IsBlockPruned(pblockindex)) {
|
||||||
throw JSONRPCError(RPC_MISC_ERROR, "Undo data not available (pruned data)");
|
throw JSONRPCError(RPC_MISC_ERROR, "Undo data not available (pruned data)");
|
||||||
}
|
}
|
||||||
@ -1771,8 +1775,10 @@ static RPCHelpMan getblockstats()
|
|||||||
{RPCResult::Type::NUM, "total_weight", /*optional=*/true, "Total weight of all non-coinbase transactions"},
|
{RPCResult::Type::NUM, "total_weight", /*optional=*/true, "Total weight of all non-coinbase transactions"},
|
||||||
{RPCResult::Type::NUM, "totalfee", /*optional=*/true, "The fee total"},
|
{RPCResult::Type::NUM, "totalfee", /*optional=*/true, "The fee total"},
|
||||||
{RPCResult::Type::NUM, "txs", /*optional=*/true, "The number of transactions (including coinbase)"},
|
{RPCResult::Type::NUM, "txs", /*optional=*/true, "The number of transactions (including coinbase)"},
|
||||||
{RPCResult::Type::NUM, "utxo_increase", /*optional=*/true, "The increase/decrease in the number of unspent outputs"},
|
{RPCResult::Type::NUM, "utxo_increase", /*optional=*/true, "The increase/decrease in the number of unspent outputs (not discounting op_return and similar)"},
|
||||||
{RPCResult::Type::NUM, "utxo_size_inc", /*optional=*/true, "The increase/decrease in size for the utxo index (not discounting op_return and similar)"},
|
{RPCResult::Type::NUM, "utxo_size_inc", /*optional=*/true, "The increase/decrease in size for the utxo index (not discounting op_return and similar)"},
|
||||||
|
{RPCResult::Type::NUM, "utxo_increase_actual", /*optional=*/true, "The increase/decrease in the number of unspent outputs, not counting unspendables"},
|
||||||
|
{RPCResult::Type::NUM, "utxo_size_inc_actual", /*optional=*/true, "The increase/decrease in size for the utxo index, not counting unspendables"},
|
||||||
}},
|
}},
|
||||||
RPCExamples{
|
RPCExamples{
|
||||||
HelpExampleCli("getblockstats", R"('"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09"' '["minfeerate","avgfeerate"]')") +
|
HelpExampleCli("getblockstats", R"('"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09"' '["minfeerate","avgfeerate"]')") +
|
||||||
@ -1803,7 +1809,7 @@ static RPCHelpMan getblockstats()
|
|||||||
const bool do_medianfee = do_all || stats.count("medianfee") != 0;
|
const bool do_medianfee = do_all || stats.count("medianfee") != 0;
|
||||||
const bool do_feerate_percentiles = do_all || stats.count("feerate_percentiles") != 0;
|
const bool do_feerate_percentiles = do_all || stats.count("feerate_percentiles") != 0;
|
||||||
const bool loop_inputs = do_all || do_medianfee || do_feerate_percentiles ||
|
const bool loop_inputs = do_all || do_medianfee || do_feerate_percentiles ||
|
||||||
SetHasKeys(stats, "utxo_size_inc", "totalfee", "avgfee", "avgfeerate", "minfee", "maxfee", "minfeerate", "maxfeerate");
|
SetHasKeys(stats, "utxo_increase", "utxo_increase_actual", "utxo_size_inc", "utxo_size_inc_actual", "totalfee", "avgfee", "avgfeerate", "minfee", "maxfee", "minfeerate", "maxfeerate");
|
||||||
const bool loop_outputs = do_all || loop_inputs || stats.count("total_out");
|
const bool loop_outputs = do_all || loop_inputs || stats.count("total_out");
|
||||||
const bool do_calculate_size = do_mediantxsize ||
|
const bool do_calculate_size = do_mediantxsize ||
|
||||||
SetHasKeys(stats, "total_size", "avgtxsize", "mintxsize", "maxtxsize", "swtotal_size");
|
SetHasKeys(stats, "total_size", "avgtxsize", "mintxsize", "maxtxsize", "swtotal_size");
|
||||||
@ -1825,7 +1831,9 @@ static RPCHelpMan getblockstats()
|
|||||||
int64_t swtxs = 0;
|
int64_t swtxs = 0;
|
||||||
int64_t total_size = 0;
|
int64_t total_size = 0;
|
||||||
int64_t total_weight = 0;
|
int64_t total_weight = 0;
|
||||||
|
int64_t utxos = 0;
|
||||||
int64_t utxo_size_inc = 0;
|
int64_t utxo_size_inc = 0;
|
||||||
|
int64_t utxo_size_inc_actual = 0;
|
||||||
std::vector<CAmount> fee_array;
|
std::vector<CAmount> fee_array;
|
||||||
std::vector<std::pair<CAmount, int64_t>> feerate_array;
|
std::vector<std::pair<CAmount, int64_t>> feerate_array;
|
||||||
std::vector<int64_t> txsize_array;
|
std::vector<int64_t> txsize_array;
|
||||||
@ -1838,7 +1846,18 @@ static RPCHelpMan getblockstats()
|
|||||||
if (loop_outputs) {
|
if (loop_outputs) {
|
||||||
for (const CTxOut& out : tx->vout) {
|
for (const CTxOut& out : tx->vout) {
|
||||||
tx_total_out += out.nValue;
|
tx_total_out += out.nValue;
|
||||||
utxo_size_inc += GetSerializeSize(out, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD;
|
|
||||||
|
size_t out_size = GetSerializeSize(out, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD;
|
||||||
|
utxo_size_inc += out_size;
|
||||||
|
|
||||||
|
// The Genesis block and the repeated BIP30 block coinbases don't change the UTXO
|
||||||
|
// set counts, so they have to be excluded from the statistics
|
||||||
|
if (pindex.nHeight == 0 || (IsBIP30Repeat(pindex) && tx->IsCoinBase())) continue;
|
||||||
|
// Skip unspendable outputs since they are not included in the UTXO set
|
||||||
|
if (out.scriptPubKey.IsUnspendable()) continue;
|
||||||
|
|
||||||
|
++utxos;
|
||||||
|
utxo_size_inc_actual += out_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1880,7 +1899,9 @@ static RPCHelpMan getblockstats()
|
|||||||
const CTxOut& prevoutput = coin.out;
|
const CTxOut& prevoutput = coin.out;
|
||||||
|
|
||||||
tx_total_in += prevoutput.nValue;
|
tx_total_in += prevoutput.nValue;
|
||||||
utxo_size_inc -= GetSerializeSize(prevoutput, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD;
|
size_t prevout_size = GetSerializeSize(prevoutput, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD;
|
||||||
|
utxo_size_inc -= prevout_size;
|
||||||
|
utxo_size_inc_actual -= prevout_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
CAmount txfee = tx_total_in - tx_total_out;
|
CAmount txfee = tx_total_in - tx_total_out;
|
||||||
@ -1940,6 +1961,8 @@ static RPCHelpMan getblockstats()
|
|||||||
ret_all.pushKV("txs", (int64_t)block.vtx.size());
|
ret_all.pushKV("txs", (int64_t)block.vtx.size());
|
||||||
ret_all.pushKV("utxo_increase", outputs - inputs);
|
ret_all.pushKV("utxo_increase", outputs - inputs);
|
||||||
ret_all.pushKV("utxo_size_inc", utxo_size_inc);
|
ret_all.pushKV("utxo_size_inc", utxo_size_inc);
|
||||||
|
ret_all.pushKV("utxo_increase_actual", utxos - inputs);
|
||||||
|
ret_all.pushKV("utxo_size_inc_actual", utxo_size_inc_actual);
|
||||||
|
|
||||||
if (do_all) {
|
if (do_all) {
|
||||||
return ret_all;
|
return ret_all;
|
||||||
|
@ -142,7 +142,9 @@
|
|||||||
"totalfee": 0,
|
"totalfee": 0,
|
||||||
"txs": 1,
|
"txs": 1,
|
||||||
"utxo_increase": 2,
|
"utxo_increase": 2,
|
||||||
"utxo_size_inc": 163
|
"utxo_increase_actual": 1,
|
||||||
|
"utxo_size_inc": 163,
|
||||||
|
"utxo_size_inc_actual": 75
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"avgfee": 4460,
|
"avgfee": 4460,
|
||||||
@ -179,7 +181,9 @@
|
|||||||
"totalfee": 4460,
|
"totalfee": 4460,
|
||||||
"txs": 2,
|
"txs": 2,
|
||||||
"utxo_increase": 3,
|
"utxo_increase": 3,
|
||||||
"utxo_size_inc": 236
|
"utxo_increase_actual": 2,
|
||||||
|
"utxo_size_inc": 236,
|
||||||
|
"utxo_size_inc_actual": 148
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"avgfee": 24906,
|
"avgfee": 24906,
|
||||||
@ -216,7 +220,9 @@
|
|||||||
"totalfee": 74720,
|
"totalfee": 74720,
|
||||||
"txs": 4,
|
"txs": 4,
|
||||||
"utxo_increase": 5,
|
"utxo_increase": 5,
|
||||||
"utxo_size_inc": 384
|
"utxo_size_inc": 384,
|
||||||
|
"utxo_increase_actual": 4,
|
||||||
|
"utxo_size_inc_actual": 296
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -161,6 +161,14 @@ class GetblockstatsTest(BitcoinTestFramework):
|
|||||||
assert_raises_rpc_error(-1, 'getblockstats hash_or_height ( stats )', self.nodes[0].getblockstats, '00', 1, 2)
|
assert_raises_rpc_error(-1, 'getblockstats hash_or_height ( stats )', self.nodes[0].getblockstats, '00', 1, 2)
|
||||||
assert_raises_rpc_error(-1, 'getblockstats hash_or_height ( stats )', self.nodes[0].getblockstats)
|
assert_raises_rpc_error(-1, 'getblockstats hash_or_height ( stats )', self.nodes[0].getblockstats)
|
||||||
|
|
||||||
|
self.log.info('Test block height 0')
|
||||||
|
genesis_stats = self.nodes[0].getblockstats(0)
|
||||||
|
assert_equal(genesis_stats["blockhash"], "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")
|
||||||
|
assert_equal(genesis_stats["utxo_increase"], 1)
|
||||||
|
assert_equal(genesis_stats["utxo_size_inc"], 117)
|
||||||
|
assert_equal(genesis_stats["utxo_increase_actual"], 0)
|
||||||
|
assert_equal(genesis_stats["utxo_size_inc_actual"], 0)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
GetblockstatsTest().main()
|
GetblockstatsTest().main()
|
||||||
|
Reference in New Issue
Block a user