Skip to content

Commit 2ca5a49

Browse files
committed
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
1 parent cb94db1 commit 2ca5a49

File tree

3 files changed

+45
-8
lines changed

3 files changed

+45
-8
lines changed

src/rpc/blockchain.cpp

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,10 @@ static CBlockUndo GetUndoChecked(BlockManager& blockman, const CBlockIndex* pblo
593593
{
594594
AssertLockHeld(::cs_main);
595595
CBlockUndo blockUndo;
596+
597+
// The Genesis block does not have undo data
598+
if (pblockindex->nHeight == 0) return blockUndo;
599+
596600
if (blockman.IsBlockPruned(pblockindex)) {
597601
throw JSONRPCError(RPC_MISC_ERROR, "Undo data not available (pruned data)");
598602
}
@@ -1771,8 +1775,10 @@ static RPCHelpMan getblockstats()
17711775
{RPCResult::Type::NUM, "total_weight", /*optional=*/true, "Total weight of all non-coinbase transactions"},
17721776
{RPCResult::Type::NUM, "totalfee", /*optional=*/true, "The fee total"},
17731777
{RPCResult::Type::NUM, "txs", /*optional=*/true, "The number of transactions (including coinbase)"},
1774-
{RPCResult::Type::NUM, "utxo_increase", /*optional=*/true, "The increase/decrease in the number of unspent outputs"},
1778+
{RPCResult::Type::NUM, "utxo_increase", /*optional=*/true, "The increase/decrease in the number of unspent outputs (not discounting op_return and similar)"},
17751779
{RPCResult::Type::NUM, "utxo_size_inc", /*optional=*/true, "The increase/decrease in size for the utxo index (not discounting op_return and similar)"},
1780+
{RPCResult::Type::NUM, "utxo_increase_actual", /*optional=*/true, "The increase/decrease in the number of unspent outputs, not counting unspendables"},
1781+
{RPCResult::Type::NUM, "utxo_size_inc_actual", /*optional=*/true, "The increase/decrease in size for the utxo index, not counting unspendables"},
17761782
}},
17771783
RPCExamples{
17781784
HelpExampleCli("getblockstats", R"('"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09"' '["minfeerate","avgfeerate"]')") +
@@ -1803,7 +1809,7 @@ static RPCHelpMan getblockstats()
18031809
const bool do_medianfee = do_all || stats.count("medianfee") != 0;
18041810
const bool do_feerate_percentiles = do_all || stats.count("feerate_percentiles") != 0;
18051811
const bool loop_inputs = do_all || do_medianfee || do_feerate_percentiles ||
1806-
SetHasKeys(stats, "utxo_size_inc", "totalfee", "avgfee", "avgfeerate", "minfee", "maxfee", "minfeerate", "maxfeerate");
1812+
SetHasKeys(stats, "utxo_increase", "utxo_increase_actual", "utxo_size_inc", "utxo_size_inc_actual", "totalfee", "avgfee", "avgfeerate", "minfee", "maxfee", "minfeerate", "maxfeerate");
18071813
const bool loop_outputs = do_all || loop_inputs || stats.count("total_out");
18081814
const bool do_calculate_size = do_mediantxsize ||
18091815
SetHasKeys(stats, "total_size", "avgtxsize", "mintxsize", "maxtxsize", "swtotal_size");
@@ -1825,7 +1831,9 @@ static RPCHelpMan getblockstats()
18251831
int64_t swtxs = 0;
18261832
int64_t total_size = 0;
18271833
int64_t total_weight = 0;
1834+
int64_t utxos = 0;
18281835
int64_t utxo_size_inc = 0;
1836+
int64_t utxo_size_inc_actual = 0;
18291837
std::vector<CAmount> fee_array;
18301838
std::vector<std::pair<CAmount, int64_t>> feerate_array;
18311839
std::vector<int64_t> txsize_array;
@@ -1838,7 +1846,18 @@ static RPCHelpMan getblockstats()
18381846
if (loop_outputs) {
18391847
for (const CTxOut& out : tx->vout) {
18401848
tx_total_out += out.nValue;
1841-
utxo_size_inc += GetSerializeSize(out, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD;
1849+
1850+
size_t out_size = GetSerializeSize(out, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD;
1851+
utxo_size_inc += out_size;
1852+
1853+
// The Genesis block and the repeated BIP30 block coinbases don't change the UTXO
1854+
// set counts, so they have to be excluded from the statistics
1855+
if (pindex.nHeight == 0 || (IsBIP30Repeat(pindex) && tx->IsCoinBase())) continue;
1856+
// Skip unspendable outputs since they are not included in the UTXO set
1857+
if (out.scriptPubKey.IsUnspendable()) continue;
1858+
1859+
++utxos;
1860+
utxo_size_inc_actual += out_size;
18421861
}
18431862
}
18441863

@@ -1880,7 +1899,9 @@ static RPCHelpMan getblockstats()
18801899
const CTxOut& prevoutput = coin.out;
18811900

18821901
tx_total_in += prevoutput.nValue;
1883-
utxo_size_inc -= GetSerializeSize(prevoutput, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD;
1902+
size_t prevout_size = GetSerializeSize(prevoutput, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD;
1903+
utxo_size_inc -= prevout_size;
1904+
utxo_size_inc_actual -= prevout_size;
18841905
}
18851906

18861907
CAmount txfee = tx_total_in - tx_total_out;
@@ -1940,6 +1961,8 @@ static RPCHelpMan getblockstats()
19401961
ret_all.pushKV("txs", (int64_t)block.vtx.size());
19411962
ret_all.pushKV("utxo_increase", outputs - inputs);
19421963
ret_all.pushKV("utxo_size_inc", utxo_size_inc);
1964+
ret_all.pushKV("utxo_increase_actual", utxos - inputs);
1965+
ret_all.pushKV("utxo_size_inc_actual", utxo_size_inc_actual);
19431966

19441967
if (do_all) {
19451968
return ret_all;

test/functional/data/rpc_getblockstats.json

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,9 @@
142142
"totalfee": 0,
143143
"txs": 1,
144144
"utxo_increase": 2,
145-
"utxo_size_inc": 163
145+
"utxo_increase_actual": 1,
146+
"utxo_size_inc": 163,
147+
"utxo_size_inc_actual": 75
146148
},
147149
{
148150
"avgfee": 4460,
@@ -179,7 +181,9 @@
179181
"totalfee": 4460,
180182
"txs": 2,
181183
"utxo_increase": 3,
182-
"utxo_size_inc": 236
184+
"utxo_increase_actual": 2,
185+
"utxo_size_inc": 236,
186+
"utxo_size_inc_actual": 148
183187
},
184188
{
185189
"avgfee": 24906,
@@ -216,7 +220,9 @@
216220
"totalfee": 74720,
217221
"txs": 4,
218222
"utxo_increase": 5,
219-
"utxo_size_inc": 384
223+
"utxo_size_inc": 384,
224+
"utxo_increase_actual": 4,
225+
"utxo_size_inc_actual": 296
220226
}
221227
]
222-
}
228+
}

test/functional/rpc_getblockstats.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,14 @@ def run_test(self):
161161
assert_raises_rpc_error(-1, 'getblockstats hash_or_height ( stats )', self.nodes[0].getblockstats, '00', 1, 2)
162162
assert_raises_rpc_error(-1, 'getblockstats hash_or_height ( stats )', self.nodes[0].getblockstats)
163163

164+
self.log.info('Test block height 0')
165+
genesis_stats = self.nodes[0].getblockstats(0)
166+
assert_equal(genesis_stats["blockhash"], "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")
167+
assert_equal(genesis_stats["utxo_increase"], 1)
168+
assert_equal(genesis_stats["utxo_size_inc"], 117)
169+
assert_equal(genesis_stats["utxo_increase_actual"], 0)
170+
assert_equal(genesis_stats["utxo_size_inc_actual"], 0)
171+
164172

165173
if __name__ == '__main__':
166174
GetblockstatsTest().main()

0 commit comments

Comments
 (0)