diff options
author | Felix Weis <mail@felixweis.com> | 2019-05-10 08:21:52 -0400 |
---|---|---|
committer | Felix Weis <mail@felixweis.com> | 2019-05-10 08:33:14 -0400 |
commit | d20d7567528e216badb8475df298bb3cec008985 (patch) | |
tree | 30be8c3046d254111e6122727db663600d306a8a /src/rpc/blockchain.cpp | |
parent | 08bd21a3bda9f621948c535e951880d7e318caa5 (diff) |
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.
Diffstat (limited to 'src/rpc/blockchain.cpp')
-rw-r--r-- | src/rpc/blockchain.cpp | 37 |
1 files changed, 22 insertions, 15 deletions
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 672fc69673..73ff746bd7 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -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; |