Changeset View
Changeset View
Standalone View
Standalone View
src/rpc/blockchain.cpp
Show First 20 Lines • Show All 109 Lines • ▼ Show 20 Lines | static int ComputeNextBlockAndDepth(const CBlockIndex *tip, | ||||
next = tip->GetAncestor(blockindex->nHeight + 1); | next = tip->GetAncestor(blockindex->nHeight + 1); | ||||
if (next && next->pprev == blockindex) { | if (next && next->pprev == blockindex) { | ||||
return tip->nHeight - blockindex->nHeight + 1; | return tip->nHeight - blockindex->nHeight + 1; | ||||
} | } | ||||
next = nullptr; | next = nullptr; | ||||
return blockindex == tip ? 1 : -1; | return blockindex == tip ? 1 : -1; | ||||
} | } | ||||
static CBlockIndex *ParseHashOrHeight(const UniValue ¶m, | |||||
ChainstateManager &chainman) { | |||||
LOCK(::cs_main); | |||||
CChain &active_chain = chainman.ActiveChain(); | |||||
if (param.isNum()) { | |||||
const int height{param.get_int()}; | |||||
if (height < 0) { | |||||
throw JSONRPCError( | |||||
RPC_INVALID_PARAMETER, | |||||
strprintf("Target block height %d is negative", height)); | |||||
} | |||||
const int current_tip{active_chain.Height()}; | |||||
if (height > current_tip) { | |||||
throw JSONRPCError( | |||||
RPC_INVALID_PARAMETER, | |||||
strprintf("Target block height %d after current tip %d", height, | |||||
current_tip)); | |||||
} | |||||
return active_chain[height]; | |||||
} else { | |||||
const BlockHash hash{ParseHashV(param, "hash_or_height")}; | |||||
CBlockIndex *pindex = chainman.m_blockman.LookupBlockIndex(hash); | |||||
if (!pindex) { | |||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); | |||||
} | |||||
if (!active_chain.Contains(pindex)) { | |||||
throw JSONRPCError(RPC_INVALID_PARAMETER, | |||||
strprintf("Block is not in chain %s", | |||||
Params().NetworkIDString())); | |||||
} | |||||
return pindex; | |||||
} | |||||
} | |||||
UniValue blockheaderToJSON(const CBlockIndex *tip, | UniValue blockheaderToJSON(const CBlockIndex *tip, | ||||
const CBlockIndex *blockindex) { | const CBlockIndex *blockindex) { | ||||
// Serialize passed information without accessing chain state of the active | // Serialize passed information without accessing chain state of the active | ||||
// chain! | // chain! | ||||
// For performance reasons | // For performance reasons | ||||
AssertLockNotHeld(cs_main); | AssertLockNotHeld(cs_main); | ||||
UniValue result(UniValue::VOBJ); | UniValue result(UniValue::VOBJ); | ||||
▲ Show 20 Lines • Show All 1,117 Lines • ▼ Show 20 Lines | if (hash_type_input == "hash_serialized") { | ||||
strprintf("%s is not a valid hash_type", hash_type_input)); | strprintf("%s is not a valid hash_type", hash_type_input)); | ||||
} | } | ||||
} | } | ||||
static RPCHelpMan gettxoutsetinfo() { | static RPCHelpMan gettxoutsetinfo() { | ||||
return RPCHelpMan{ | return RPCHelpMan{ | ||||
"gettxoutsetinfo", | "gettxoutsetinfo", | ||||
"Returns statistics about the unspent transaction output set.\n" | "Returns statistics about the unspent transaction output set.\n" | ||||
"Note this call may take some time.\n", | "Note this call may take some time if you are not using " | ||||
"coinstatsindex.\n", | |||||
{ | { | ||||
{"hash_type", RPCArg::Type::STR, /* default */ "hash_serialized", | {"hash_type", RPCArg::Type::STR, /* default */ "hash_serialized", | ||||
"Which UTXO set hash should be calculated. Options: " | "Which UTXO set hash should be calculated. Options: " | ||||
"'hash_serialized' (the legacy algorithm), 'muhash', 'none'."}, | "'hash_serialized' (the legacy algorithm), 'muhash', 'none'."}, | ||||
{"hash_or_height", | |||||
RPCArg::Type::NUM, | |||||
RPCArg::Optional::OMITTED, | |||||
"The block hash or height of the target height (only available " | |||||
"with coinstatsindex).", | |||||
"", | |||||
{"", "string or numeric"}}, | |||||
}, | }, | ||||
RPCResult{RPCResult::Type::OBJ, | RPCResult{RPCResult::Type::OBJ, | ||||
"", | "", | ||||
"", | "", | ||||
{ | { | ||||
{RPCResult::Type::NUM, "height", | {RPCResult::Type::NUM, "height", | ||||
"The current block height (index)"}, | "The current block height (index)"}, | ||||
{RPCResult::Type::STR_HEX, "bestblock", | {RPCResult::Type::STR_HEX, "bestblock", | ||||
"The hash of the block at the tip of the chain"}, | "The hash of the block at the tip of the chain"}, | ||||
{RPCResult::Type::NUM, "transactions", | |||||
"The number of transactions with unspent outputs"}, | |||||
{RPCResult::Type::NUM, "txouts", | {RPCResult::Type::NUM, "txouts", | ||||
"The number of unspent transaction outputs"}, | "The number of unspent transaction outputs"}, | ||||
{RPCResult::Type::NUM, "bogosize", | {RPCResult::Type::NUM, "bogosize", | ||||
"A meaningless metric for UTXO set size"}, | "Database-independent, meaningless metric indicating " | ||||
"the UTXO set size"}, | |||||
{RPCResult::Type::STR_HEX, "hash_serialized", | {RPCResult::Type::STR_HEX, "hash_serialized", | ||||
/* optional */ true, | /* optional */ true, | ||||
"The serialized hash (only present if 'hash_serialized' " | "The serialized hash (only present if 'hash_serialized' " | ||||
"hash_type is chosen)"}, | "hash_type is chosen)"}, | ||||
{RPCResult::Type::STR_HEX, "muhash", /* optional */ true, | {RPCResult::Type::STR_HEX, "muhash", /* optional */ true, | ||||
"The serialized hash (only present if 'muhash' " | "The serialized hash (only present if 'muhash' " | ||||
"hash_type is chosen)"}, | "hash_type is chosen)"}, | ||||
{RPCResult::Type::NUM, "transactions", | |||||
"The number of transactions with unspent outputs (not " | |||||
"available when coinstatsindex is used)"}, | |||||
{RPCResult::Type::NUM, "disk_size", | {RPCResult::Type::NUM, "disk_size", | ||||
"The estimated size of the chainstate on disk"}, | "The estimated size of the chainstate on disk (not " | ||||
"available when coinstatsindex is used)"}, | |||||
{RPCResult::Type::STR_AMOUNT, "total_amount", | {RPCResult::Type::STR_AMOUNT, "total_amount", | ||||
"The total amount"}, | "The total amount"}, | ||||
}}, | }}, | ||||
RPCExamples{HelpExampleCli("gettxoutsetinfo", "") + | RPCExamples{ | ||||
HelpExampleRpc("gettxoutsetinfo", "")}, | HelpExampleCli("gettxoutsetinfo", "") + | ||||
HelpExampleCli("gettxoutsetinfo", R"("none")") + | |||||
HelpExampleCli("gettxoutsetinfo", R"("none" 1000)") + | |||||
HelpExampleCli( | |||||
"gettxoutsetinfo", | |||||
R"("none" '"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09"')") + | |||||
HelpExampleRpc("gettxoutsetinfo", "") + | |||||
HelpExampleRpc("gettxoutsetinfo", R"("none")") + | |||||
HelpExampleRpc("gettxoutsetinfo", R"("none", 1000)") + | |||||
HelpExampleRpc( | |||||
"gettxoutsetinfo", | |||||
R"("none", "00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09")")}, | |||||
[&](const RPCHelpMan &self, const Config &config, | [&](const RPCHelpMan &self, const Config &config, | ||||
const JSONRPCRequest &request) -> UniValue { | const JSONRPCRequest &request) -> UniValue { | ||||
UniValue ret(UniValue::VOBJ); | UniValue ret(UniValue::VOBJ); | ||||
CBlockIndex *pindex{nullptr}; | |||||
const CoinStatsHashType hash_type{ | const CoinStatsHashType hash_type{ | ||||
request.params[0].isNull() | request.params[0].isNull() | ||||
? CoinStatsHashType::HASH_SERIALIZED | ? CoinStatsHashType::HASH_SERIALIZED | ||||
: ParseHashType(request.params[0].get_str())}; | : ParseHashType(request.params[0].get_str())}; | ||||
CCoinsStats stats{hash_type}; | CCoinsStats stats{hash_type}; | ||||
NodeContext &node = EnsureAnyNodeContext(request.context); | NodeContext &node = EnsureAnyNodeContext(request.context); | ||||
ChainstateManager &chainman = EnsureChainman(node); | ChainstateManager &chainman = EnsureChainman(node); | ||||
CChainState &active_chainstate = chainman.ActiveChainstate(); | CChainState &active_chainstate = chainman.ActiveChainstate(); | ||||
active_chainstate.ForceFlushStateToDisk(); | active_chainstate.ForceFlushStateToDisk(); | ||||
CCoinsView *coins_view; | CCoinsView *coins_view; | ||||
BlockManager *blockman; | BlockManager *blockman; | ||||
{ | { | ||||
LOCK(::cs_main); | LOCK(::cs_main); | ||||
coins_view = &active_chainstate.CoinsDB(); | coins_view = &active_chainstate.CoinsDB(); | ||||
blockman = &active_chainstate.m_blockman; | blockman = &active_chainstate.m_blockman; | ||||
} | } | ||||
if (!request.params[1].isNull()) { | |||||
if (!g_coin_stats_index) { | |||||
throw JSONRPCError(RPC_INVALID_PARAMETER, | |||||
"Querying specific block heights " | |||||
"requires coinstatsindex"); | |||||
} | |||||
pindex = ParseHashOrHeight(request.params[1], chainman); | |||||
} | |||||
if (GetUTXOStats(coins_view, *blockman, stats, | if (GetUTXOStats(coins_view, *blockman, stats, | ||||
node.rpc_interruption_point)) { | node.rpc_interruption_point, pindex)) { | ||||
ret.pushKV("height", int64_t(stats.nHeight)); | ret.pushKV("height", int64_t(stats.nHeight)); | ||||
ret.pushKV("bestblock", stats.hashBlock.GetHex()); | ret.pushKV("bestblock", stats.hashBlock.GetHex()); | ||||
ret.pushKV("transactions", int64_t(stats.nTransactions)); | |||||
ret.pushKV("txouts", int64_t(stats.nTransactionOutputs)); | ret.pushKV("txouts", int64_t(stats.nTransactionOutputs)); | ||||
ret.pushKV("bogosize", int64_t(stats.nBogoSize)); | ret.pushKV("bogosize", int64_t(stats.nBogoSize)); | ||||
if (hash_type == CoinStatsHashType::HASH_SERIALIZED) { | if (hash_type == CoinStatsHashType::HASH_SERIALIZED) { | ||||
ret.pushKV("hash_serialized", | ret.pushKV("hash_serialized", | ||||
stats.hashSerialized.GetHex()); | stats.hashSerialized.GetHex()); | ||||
} | } | ||||
if (hash_type == CoinStatsHashType::MUHASH) { | if (hash_type == CoinStatsHashType::MUHASH) { | ||||
ret.pushKV("muhash", stats.hashSerialized.GetHex()); | ret.pushKV("muhash", stats.hashSerialized.GetHex()); | ||||
} | } | ||||
if (!stats.from_index) { | |||||
ret.pushKV("transactions", | |||||
static_cast<int64_t>(stats.nTransactions)); | |||||
ret.pushKV("disk_size", stats.nDiskSize); | ret.pushKV("disk_size", stats.nDiskSize); | ||||
} | |||||
ret.pushKV("total_amount", stats.nTotalAmount); | ret.pushKV("total_amount", stats.nTotalAmount); | ||||
} else { | } else { | ||||
if (g_coin_stats_index) { | if (g_coin_stats_index) { | ||||
const IndexSummary summary{ | const IndexSummary summary{ | ||||
g_coin_stats_index->GetSummary()}; | g_coin_stats_index->GetSummary()}; | ||||
if (!summary.synced) { | if (!summary.synced) { | ||||
throw JSONRPCError( | throw JSONRPCError( | ||||
▲ Show 20 Lines • Show All 1,069 Lines • ▼ Show 20 Lines | return RPCHelpMan{ | ||||
"getblockstats", | "getblockstats", | ||||
R"("00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09", ["minfeerate","avgfeerate"])") + | R"("00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09", ["minfeerate","avgfeerate"])") + | ||||
HelpExampleRpc("getblockstats", | HelpExampleRpc("getblockstats", | ||||
R"(1000, ["minfeerate","avgfeerate"])")}, | R"(1000, ["minfeerate","avgfeerate"])")}, | ||||
[&](const RPCHelpMan &self, const Config &config, | [&](const RPCHelpMan &self, const Config &config, | ||||
const JSONRPCRequest &request) -> UniValue { | const JSONRPCRequest &request) -> UniValue { | ||||
ChainstateManager &chainman = EnsureAnyChainman(request.context); | ChainstateManager &chainman = EnsureAnyChainman(request.context); | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
CChain &active_chain = chainman.ActiveChain(); | CBlockIndex *pindex{ParseHashOrHeight(request.params[0], chainman)}; | ||||
CBlockIndex *pindex; | |||||
if (request.params[0].isNum()) { | |||||
const int height = request.params[0].get_int(); | |||||
const int current_tip = active_chain.Height(); | |||||
if (height < 0) { | |||||
throw JSONRPCError( | |||||
RPC_INVALID_PARAMETER, | |||||
strprintf("Target block height %d is negative", | |||||
height)); | |||||
} | |||||
if (height > current_tip) { | |||||
throw JSONRPCError( | |||||
RPC_INVALID_PARAMETER, | |||||
strprintf("Target block height %d after current tip %d", | |||||
height, current_tip)); | |||||
} | |||||
pindex = active_chain[height]; | |||||
} else { | |||||
const BlockHash hash( | |||||
ParseHashV(request.params[0], "hash_or_height")); | |||||
pindex = chainman.m_blockman.LookupBlockIndex(hash); | |||||
if (!pindex) { | |||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | |||||
"Block not found"); | |||||
} | |||||
if (!active_chain.Contains(pindex)) { | |||||
throw JSONRPCError(RPC_INVALID_PARAMETER, | |||||
strprintf("Block is not in chain %s", | |||||
Params().NetworkIDString())); | |||||
} | |||||
} | |||||
CHECK_NONFATAL(pindex != nullptr); | CHECK_NONFATAL(pindex != nullptr); | ||||
std::set<std::string> stats; | std::set<std::string> stats; | ||||
if (!request.params[1].isNull()) { | 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); | ||||
▲ Show 20 Lines • Show All 755 Lines • Show Last 20 Lines |