mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-11-12 23:18:14 +01:00
rpc: faster getblockstats using BlockUndo data
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.
This commit is contained in:
@@ -28,6 +28,7 @@
|
||||
#include <sync.h>
|
||||
#include <txdb.h>
|
||||
#include <txmempool.h>
|
||||
#include <undo.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/system.h>
|
||||
#include <util/validation.h>
|
||||
@@ -822,6 +823,20 @@ static CBlock GetBlockChecked(const CBlockIndex* pblockindex)
|
||||
return block;
|
||||
}
|
||||
|
||||
static CBlockUndo GetUndoChecked(const CBlockIndex* pblockindex)
|
||||
{
|
||||
CBlockUndo blockUndo;
|
||||
if (IsBlockPruned(pblockindex)) {
|
||||
throw JSONRPCError(RPC_MISC_ERROR, "Undo data not available (pruned data)");
|
||||
}
|
||||
|
||||
if (!UndoReadFromDisk(blockUndo, pblockindex)) {
|
||||
throw JSONRPCError(RPC_MISC_ERROR, "Can't read undo data from disk");
|
||||
}
|
||||
|
||||
return blockUndo;
|
||||
}
|
||||
|
||||
static UniValue getblock(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
|
||||
@@ -1783,8 +1798,7 @@ static UniValue getblockstats(const JSONRPCRequest& request)
|
||||
{
|
||||
const RPCHelpMan help{"getblockstats",
|
||||
"\nCompute per block statistics for a given window. All amounts are in satoshis.\n"
|
||||
"It won't work for some heights with pruning.\n"
|
||||
"It won't work without -txindex for utxo_size_inc, *fee or *feerate stats.\n",
|
||||
"It won't work for some heights with pruning.\n",
|
||||
{
|
||||
{"hash_or_height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The block hash or height of the target block", "", {"", "string or numeric"}},
|
||||
{"stats", RPCArg::Type::ARR, /* default */ "all values", "Values to plot (see result below)",
|
||||
@@ -1879,6 +1893,7 @@ static UniValue getblockstats(const JSONRPCRequest& request)
|
||||
}
|
||||
|
||||
const CBlock block = GetBlockChecked(pindex);
|
||||
const CBlockUndo blockUndo = GetUndoChecked(pindex);
|
||||
|
||||
const bool do_all = stats.size() == 0; // Calculate everything if nothing selected (default)
|
||||
const bool do_mediantxsize = do_all || stats.count("mediantxsize") != 0;
|
||||
@@ -1892,10 +1907,6 @@ static UniValue getblockstats(const JSONRPCRequest& request)
|
||||
const bool do_calculate_weight = do_all || SetHasKeys(stats, "total_weight", "avgfeerate", "swtotal_weight", "avgfeerate", "feerate_percentiles", "minfeerate", "maxfeerate");
|
||||
const bool do_calculate_sw = do_all || SetHasKeys(stats, "swtxs", "swtotal_size", "swtotal_weight");
|
||||
|
||||
if (loop_inputs && !g_txindex) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "One or more of the selected stats requires -txindex enabled");
|
||||
}
|
||||
|
||||
CAmount maxfee = 0;
|
||||
CAmount maxfeerate = 0;
|
||||
CAmount minfee = MAX_MONEY;
|
||||
@@ -1916,7 +1927,8 @@ static UniValue getblockstats(const JSONRPCRequest& request)
|
||||
std::vector<std::pair<CAmount, int64_t>> feerate_array;
|
||||
std::vector<int64_t> txsize_array;
|
||||
|
||||
for (const auto& tx : block.vtx) {
|
||||
for (size_t i = 0; i < block.vtx.size(); ++i) {
|
||||
const auto& tx = block.vtx.at(i);
|
||||
outputs += tx->vout.size();
|
||||
|
||||
CAmount tx_total_out = 0;
|
||||
@@ -1960,14 +1972,9 @@ static UniValue getblockstats(const JSONRPCRequest& request)
|
||||
|
||||
if (loop_inputs) {
|
||||
CAmount tx_total_in = 0;
|
||||
for (const CTxIn& in : tx->vin) {
|
||||
CTransactionRef tx_in;
|
||||
uint256 hashBlock;
|
||||
if (!GetTransaction(in.prevout.hash, tx_in, Params().GetConsensus(), hashBlock)) {
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, std::string("Unexpected internal error (tx index seems corrupt)"));
|
||||
}
|
||||
|
||||
CTxOut prevoutput = tx_in->vout[in.prevout.n];
|
||||
const auto& txundo = blockUndo.vtxundo.at(i - 1);
|
||||
for (const Coin& coin: txundo.vprevout) {
|
||||
const CTxOut& prevoutput = coin.out;
|
||||
|
||||
tx_total_in += prevoutput.nValue;
|
||||
utxo_size_inc -= GetSerializeSize(prevoutput, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD;
|
||||
|
||||
Reference in New Issue
Block a user