Changeset View
Changeset View
Standalone View
Standalone View
src/rpc/blockchain.cpp
Show All 20 Lines | |||||
#include <primitives/transaction.h> | #include <primitives/transaction.h> | ||||
#include <rpc/server.h> | #include <rpc/server.h> | ||||
#include <rpc/util.h> | #include <rpc/util.h> | ||||
#include <script/descriptor.h> | #include <script/descriptor.h> | ||||
#include <streams.h> | #include <streams.h> | ||||
#include <sync.h> | #include <sync.h> | ||||
#include <txdb.h> | #include <txdb.h> | ||||
#include <txmempool.h> | #include <txmempool.h> | ||||
#include <undo.h> | |||||
#include <util/strencodings.h> | #include <util/strencodings.h> | ||||
#include <util/system.h> | #include <util/system.h> | ||||
#include <util/validation.h> | #include <util/validation.h> | ||||
#include <validation.h> | #include <validation.h> | ||||
#include <validationinterface.h> | #include <validationinterface.h> | ||||
#include <versionbitsinfo.h> // For VersionBitsDeploymentInfo | #include <versionbitsinfo.h> // For VersionBitsDeploymentInfo | ||||
#include <warnings.h> | #include <warnings.h> | ||||
▲ Show 20 Lines • Show All 887 Lines • ▼ Show 20 Lines | if (!ReadBlockFromDisk(block, pblockindex, | ||||
// non-whitelisted node sends us an unrequested long chain of valid | // non-whitelisted node sends us an unrequested long chain of valid | ||||
// blocks, we add the headers to our index, but don't accept the block). | // blocks, we add the headers to our index, but don't accept the block). | ||||
throw JSONRPCError(RPC_MISC_ERROR, "Block not found on disk"); | throw JSONRPCError(RPC_MISC_ERROR, "Block not found on disk"); | ||||
} | } | ||||
return block; | 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 Config &config, const JSONRPCRequest &request) { | static UniValue getblock(const Config &config, const JSONRPCRequest &request) { | ||||
const RPCHelpMan help{ | const RPCHelpMan help{ | ||||
"getblock", | "getblock", | ||||
"\nIf verbosity is 0 or false, returns a string that is " | "\nIf verbosity is 0 or false, returns a string that is " | ||||
"serialized, hex-encoded data for block 'hash'.\n" | "serialized, hex-encoded data for block 'hash'.\n" | ||||
"If verbosity is 1 or true, returns an Object with information " | "If verbosity is 1 or true, returns an Object with information " | ||||
"about block <hash>.\n" | "about block <hash>.\n" | ||||
"If verbosity is 2, returns an Object with information about " | "If verbosity is 2, returns an Object with information about " | ||||
▲ Show 20 Lines • Show All 1,127 Lines • ▼ Show 20 Lines | |||||
static UniValue getblockstats(const Config &config, | static UniValue getblockstats(const Config &config, | ||||
const JSONRPCRequest &request) { | const JSONRPCRequest &request) { | ||||
const RPCHelpMan help{ | const RPCHelpMan help{ | ||||
"getblockstats", | "getblockstats", | ||||
"\nCompute per block statistics for a given window. All amounts are " | "\nCompute per block statistics for a given window. All amounts are " | ||||
"in " + | "in " + | ||||
CURRENCY_UNIT + | CURRENCY_UNIT + | ||||
".\n" | ".\n" | ||||
"It won't work for some heights with pruning.\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", | |||||
{ | { | ||||
{"hash_or_height", | {"hash_or_height", | ||||
RPCArg::Type::NUM, | RPCArg::Type::NUM, | ||||
RPCArg::Optional::NO, | RPCArg::Optional::NO, | ||||
"The block hash or height of the target block", | "The block hash or height of the target block", | ||||
"", | "", | ||||
{"", "string or numeric"}}, | {"", "string or numeric"}}, | ||||
{"stats", | {"stats", | ||||
▲ Show 20 Lines • Show All 105 Lines • ▼ Show 20 Lines | if (!request.params[1].isNull()) { | ||||
const UniValue stats_univalue = request.params[1].get_array(); | const UniValue stats_univalue = request.params[1].get_array(); | ||||
for (unsigned int i = 0; i < stats_univalue.size(); i++) { | for (unsigned int i = 0; i < stats_univalue.size(); i++) { | ||||
const std::string stat = stats_univalue[i].get_str(); | const std::string stat = stats_univalue[i].get_str(); | ||||
stats.insert(stat); | stats.insert(stat); | ||||
} | } | ||||
} | } | ||||
const CBlock block = GetBlockChecked(config, pindex); | const CBlock block = GetBlockChecked(config, pindex); | ||||
const CBlockUndo blockUndo = GetUndoChecked(pindex); | |||||
// Calculate everything if nothing selected (default) | // Calculate everything if nothing selected (default) | ||||
const bool do_all = stats.size() == 0; | const bool do_all = stats.size() == 0; | ||||
const bool do_mediantxsize = do_all || stats.count("mediantxsize") != 0; | const bool do_mediantxsize = do_all || stats.count("mediantxsize") != 0; | ||||
const bool do_medianfee = do_all || stats.count("medianfee") != 0; | const bool do_medianfee = do_all || stats.count("medianfee") != 0; | ||||
const bool do_medianfeerate = do_all || stats.count("medianfeerate") != 0; | const bool do_medianfeerate = do_all || stats.count("medianfeerate") != 0; | ||||
const bool loop_inputs = | const bool loop_inputs = | ||||
do_all || do_medianfee || do_medianfeerate || | do_all || do_medianfee || do_medianfeerate || | ||||
SetHasKeys(stats, "utxo_size_inc", "totalfee", "avgfee", "avgfeerate", | SetHasKeys(stats, "utxo_size_inc", "totalfee", "avgfee", "avgfeerate", | ||||
"minfee", "maxfee", "minfeerate", "maxfeerate"); | "minfee", "maxfee", "minfeerate", "maxfeerate"); | ||||
const bool loop_outputs = do_all || loop_inputs || stats.count("total_out"); | const bool loop_outputs = do_all || loop_inputs || stats.count("total_out"); | ||||
const bool do_calculate_size = | const bool do_calculate_size = | ||||
do_mediantxsize || loop_inputs || | do_mediantxsize || loop_inputs || | ||||
SetHasKeys(stats, "total_size", "avgtxsize", "mintxsize", "maxtxsize"); | SetHasKeys(stats, "total_size", "avgtxsize", "mintxsize", "maxtxsize"); | ||||
if (loop_inputs && !g_txindex) { | |||||
throw JSONRPCError( | |||||
RPC_INVALID_PARAMETER, | |||||
"One or more of the selected stats requires -txindex enabled"); | |||||
} | |||||
const int64_t blockMaxSize = config.GetMaxBlockSize(); | const int64_t blockMaxSize = config.GetMaxBlockSize(); | ||||
Amount maxfee = Amount::zero(); | Amount maxfee = Amount::zero(); | ||||
Amount maxfeerate = Amount::zero(); | Amount maxfeerate = Amount::zero(); | ||||
Amount minfee = MAX_MONEY; | Amount minfee = MAX_MONEY; | ||||
Amount minfeerate = MAX_MONEY; | Amount minfeerate = MAX_MONEY; | ||||
Amount total_out = Amount::zero(); | Amount total_out = Amount::zero(); | ||||
Amount totalfee = Amount::zero(); | Amount totalfee = Amount::zero(); | ||||
int64_t inputs = 0; | int64_t inputs = 0; | ||||
int64_t maxtxsize = 0; | int64_t maxtxsize = 0; | ||||
int64_t mintxsize = blockMaxSize; | int64_t mintxsize = blockMaxSize; | ||||
int64_t outputs = 0; | int64_t outputs = 0; | ||||
int64_t total_size = 0; | int64_t total_size = 0; | ||||
int64_t utxo_size_inc = 0; | int64_t utxo_size_inc = 0; | ||||
std::vector<Amount> fee_array; | std::vector<Amount> fee_array; | ||||
std::vector<Amount> feerate_array; | std::vector<Amount> feerate_array; | ||||
std::vector<int64_t> txsize_array; | std::vector<int64_t> txsize_array; | ||||
const Consensus::Params ¶ms = config.GetChainParams().GetConsensus(); | for (size_t i = 0; i < block.vtx.size(); ++i) { | ||||
const auto &tx = block.vtx.at(i); | |||||
for (const auto &tx : block.vtx) { | |||||
outputs += tx->vout.size(); | outputs += tx->vout.size(); | ||||
Amount tx_total_out = Amount::zero(); | Amount tx_total_out = Amount::zero(); | ||||
if (loop_outputs) { | if (loop_outputs) { | ||||
for (const CTxOut &out : tx->vout) { | for (const CTxOut &out : tx->vout) { | ||||
tx_total_out += out.nValue; | tx_total_out += out.nValue; | ||||
utxo_size_inc += | utxo_size_inc += | ||||
GetSerializeSize(out, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD; | GetSerializeSize(out, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD; | ||||
} | } | ||||
Show All 16 Lines | for (size_t i = 0; i < block.vtx.size(); ++i) { | ||||
} | } | ||||
maxtxsize = std::max(maxtxsize, tx_size); | maxtxsize = std::max(maxtxsize, tx_size); | ||||
mintxsize = std::min(mintxsize, tx_size); | mintxsize = std::min(mintxsize, tx_size); | ||||
total_size += tx_size; | total_size += tx_size; | ||||
} | } | ||||
if (loop_inputs) { | if (loop_inputs) { | ||||
Amount tx_total_in = Amount::zero(); | Amount tx_total_in = Amount::zero(); | ||||
for (const CTxIn &in : tx->vin) { | const auto &txundo = blockUndo.vtxundo.at(i - 1); | ||||
CTransactionRef tx_in; | for (const Coin &coin : txundo.vprevout) { | ||||
BlockHash hashBlock; | const CTxOut &prevoutput = coin.GetTxOut(); | ||||
if (!GetTransaction(in.prevout.GetTxId(), tx_in, params, | |||||
hashBlock)) { | |||||
throw JSONRPCError(RPC_INTERNAL_ERROR, | |||||
std::string("Unexpected internal error " | |||||
"(tx index seems corrupt)")); | |||||
} | |||||
CTxOut prevoutput = tx_in->vout[in.prevout.GetN()]; | |||||
tx_total_in += prevoutput.nValue; | tx_total_in += prevoutput.nValue; | ||||
utxo_size_inc -= | utxo_size_inc -= | ||||
GetSerializeSize(prevoutput, PROTOCOL_VERSION) + | GetSerializeSize(prevoutput, PROTOCOL_VERSION) + | ||||
PER_UTXO_OVERHEAD; | PER_UTXO_OVERHEAD; | ||||
} | } | ||||
Amount txfee = tx_total_in - tx_total_out; | Amount txfee = tx_total_in - tx_total_out; | ||||
▲ Show 20 Lines • Show All 444 Lines • Show Last 20 Lines |