Skip to content

Commit e2371f8

Browse files
author
MarcoFalke
committed
Merge #14802: rpc: faster getblockstats using BlockUndo data
d20d756 rpc: faster getblockstats using BlockUndo data (Felix Weis) Pull request description: Using undo data for a block (rev?????.dat) we can retrieve value information about prevouts and calculate the final transaction fee (rate). This approach is about 80x faster, drops the requirement for `-txindex`, and works for all non-pruned blocks. ``` # 2018-11-25T16:36:19Z Bitcoin Core version v0.17.99.0-edc715240-dirty (release build) seq 550100 550200 0.00s user 0.00s system 62% cpu 0.004 total xargs -n1 src/bitcoin-cli getblockstats 0.21s user 0.19s system 17% cpu 2.302 total # 2018-11-25T16:39:17Z Bitcoin Core version v0.17.0 (release build) seq 550100 550200 0.00s user 0.00s system 87% cpu 0.002 total xargs -n1 src/bitcoin-cli getblockstats 0.24s user 0.22s system 0% cpu 3:19.42 total ``` ACKs for commit d20d75: MarcoFalke: re-utACK d20d756 Tree-SHA512: 5babc3eb8d2fee2cb23dc12f522656b80737a540cbf2b13390a8f388304c46c064cca76f896b46a6e2abae8cc582d28e1ab20dd4bb17ad6142f20630c2d30c54
2 parents 1495975 + d20d756 commit e2371f8

File tree

4 files changed

+174
-187
lines changed

4 files changed

+174
-187
lines changed

doc/release-notes-14802.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
RPC changes
2+
-----------
3+
The `getblockstats` RPC is faster for fee calculation by using BlockUndo data. Also, `-txindex` is no longer required and `getblockstats` works for all non-pruned blocks.

src/rpc/blockchain.cpp

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <sync.h>
2929
#include <txdb.h>
3030
#include <txmempool.h>
31+
#include <undo.h>
3132
#include <util/strencodings.h>
3233
#include <util/system.h>
3334
#include <util/validation.h>
@@ -828,6 +829,20 @@ static CBlock GetBlockChecked(const CBlockIndex* pblockindex)
828829
return block;
829830
}
830831

832+
static CBlockUndo GetUndoChecked(const CBlockIndex* pblockindex)
833+
{
834+
CBlockUndo blockUndo;
835+
if (IsBlockPruned(pblockindex)) {
836+
throw JSONRPCError(RPC_MISC_ERROR, "Undo data not available (pruned data)");
837+
}
838+
839+
if (!UndoReadFromDisk(blockUndo, pblockindex)) {
840+
throw JSONRPCError(RPC_MISC_ERROR, "Can't read undo data from disk");
841+
}
842+
843+
return blockUndo;
844+
}
845+
831846
static UniValue getblock(const JSONRPCRequest& request)
832847
{
833848
const RPCHelpMan help{"getblock",
@@ -1799,8 +1814,7 @@ static UniValue getblockstats(const JSONRPCRequest& request)
17991814
{
18001815
const RPCHelpMan help{"getblockstats",
18011816
"\nCompute per block statistics for a given window. All amounts are in satoshis.\n"
1802-
"It won't work for some heights with pruning.\n"
1803-
"It won't work without -txindex for utxo_size_inc, *fee or *feerate stats.\n",
1817+
"It won't work for some heights with pruning.\n",
18041818
{
18051819
{"hash_or_height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The block hash or height of the target block", "", {"", "string or numeric"}},
18061820
{"stats", RPCArg::Type::ARR, /* default */ "all values", "Values to plot (see result below)",
@@ -1895,6 +1909,7 @@ static UniValue getblockstats(const JSONRPCRequest& request)
18951909
}
18961910

18971911
const CBlock block = GetBlockChecked(pindex);
1912+
const CBlockUndo blockUndo = GetUndoChecked(pindex);
18981913

18991914
const bool do_all = stats.size() == 0; // Calculate everything if nothing selected (default)
19001915
const bool do_mediantxsize = do_all || stats.count("mediantxsize") != 0;
@@ -1908,10 +1923,6 @@ static UniValue getblockstats(const JSONRPCRequest& request)
19081923
const bool do_calculate_weight = do_all || SetHasKeys(stats, "total_weight", "avgfeerate", "swtotal_weight", "avgfeerate", "feerate_percentiles", "minfeerate", "maxfeerate");
19091924
const bool do_calculate_sw = do_all || SetHasKeys(stats, "swtxs", "swtotal_size", "swtotal_weight");
19101925

1911-
if (loop_inputs && !g_txindex) {
1912-
throw JSONRPCError(RPC_INVALID_PARAMETER, "One or more of the selected stats requires -txindex enabled");
1913-
}
1914-
19151926
CAmount maxfee = 0;
19161927
CAmount maxfeerate = 0;
19171928
CAmount minfee = MAX_MONEY;
@@ -1932,7 +1943,8 @@ static UniValue getblockstats(const JSONRPCRequest& request)
19321943
std::vector<std::pair<CAmount, int64_t>> feerate_array;
19331944
std::vector<int64_t> txsize_array;
19341945

1935-
for (const auto& tx : block.vtx) {
1946+
for (size_t i = 0; i < block.vtx.size(); ++i) {
1947+
const auto& tx = block.vtx.at(i);
19361948
outputs += tx->vout.size();
19371949

19381950
CAmount tx_total_out = 0;
@@ -1976,14 +1988,9 @@ static UniValue getblockstats(const JSONRPCRequest& request)
19761988

19771989
if (loop_inputs) {
19781990
CAmount tx_total_in = 0;
1979-
for (const CTxIn& in : tx->vin) {
1980-
CTransactionRef tx_in;
1981-
uint256 hashBlock;
1982-
if (!GetTransaction(in.prevout.hash, tx_in, Params().GetConsensus(), hashBlock)) {
1983-
throw JSONRPCError(RPC_INTERNAL_ERROR, std::string("Unexpected internal error (tx index seems corrupt)"));
1984-
}
1985-
1986-
CTxOut prevoutput = tx_in->vout[in.prevout.n];
1991+
const auto& txundo = blockUndo.vtxundo.at(i - 1);
1992+
for (const Coin& coin: txundo.vprevout) {
1993+
const CTxOut& prevoutput = coin.out;
19871994

19881995
tx_total_in += prevoutput.nValue;
19891996
utxo_size_inc -= GetSerializeSize(prevoutput, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD;

0 commit comments

Comments
 (0)