Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rpc: faster getblockstats using BlockUndo data #14802

Merged
merged 1 commit into from May 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions doc/release-notes-14802.md
@@ -0,0 +1,3 @@
RPC changes
-----------
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.
37 changes: 22 additions & 15 deletions src/rpc/blockchain.cpp
Expand Up @@ -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>
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)",
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down