diff options
Diffstat (limited to 'src/rpc/blockchain.cpp')
-rw-r--r-- | src/rpc/blockchain.cpp | 318 |
1 files changed, 175 insertions, 143 deletions
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 398b948388..b80cdce3f0 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -19,11 +19,11 @@ #include <hash.h> #include <index/blockfilterindex.h> #include <index/coinstatsindex.h> +#include <kernel/coinstats.h> #include <logging/timer.h> #include <net.h> #include <net_processing.h> #include <node/blockstorage.h> -#include <node/coinstats.h> #include <node/context.h> #include <node/utxo_snapshot.h> #include <primitives/transaction.h> @@ -51,10 +51,10 @@ #include <memory> #include <mutex> +using kernel::CCoinsStats; +using kernel::CoinStatsHashType; + using node::BlockManager; -using node::CCoinsStats; -using node::CoinStatsHashType; -using node::GetUTXOStats; using node::NodeContext; using node::ReadBlockFromDisk; using node::SnapshotMetadata; @@ -66,7 +66,7 @@ struct CUpdatedBlock int height; }; -static Mutex cs_blockchange; +static GlobalMutex cs_blockchange; static std::condition_variable cond_blockchange; static CUpdatedBlock latestblock GUARDED_BY(cs_blockchange); @@ -110,7 +110,7 @@ static const CBlockIndex* ParseHashOrHeight(const UniValue& param, ChainstateMan CChain& active_chain = chainman.ActiveChain(); if (param.isNum()) { - const int height{param.get_int()}; + const int height{param.getInt<int>()}; if (height < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Target block height %d is negative", height)); } @@ -271,7 +271,7 @@ static RPCHelpMan waitfornewblock() { int timeout = 0; if (!request.params[0].isNull()) - timeout = request.params[0].get_int(); + timeout = request.params[0].getInt<int>(); CUpdatedBlock block; { @@ -317,7 +317,7 @@ static RPCHelpMan waitforblock() uint256 hash(ParseHashV(request.params[0], "blockhash")); if (!request.params[1].isNull()) - timeout = request.params[1].get_int(); + timeout = request.params[1].getInt<int>(); CUpdatedBlock block; { @@ -361,10 +361,10 @@ static RPCHelpMan waitforblockheight() { int timeout = 0; - int height = request.params[0].get_int(); + int height = request.params[0].getInt<int>(); if (!request.params[1].isNull()) - timeout = request.params[1].get_int(); + timeout = request.params[1].getInt<int>(); CUpdatedBlock block; { @@ -440,12 +440,17 @@ static RPCHelpMan getblockfrompeer() }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { + RPCTypeCheck(request.params, { + UniValue::VSTR, // blockhash + UniValue::VNUM, // peer_id + }); + const NodeContext& node = EnsureAnyNodeContext(request.context); ChainstateManager& chainman = EnsureChainman(node); PeerManager& peerman = EnsurePeerman(node); const uint256& block_hash{ParseHashV(request.params[0], "blockhash")}; - const NodeId peer_id{request.params[1].get_int64()}; + const NodeId peer_id{request.params[1].getInt<int64_t>()}; const CBlockIndex* const index = WITH_LOCK(cs_main, return chainman.m_blockman.LookupBlockIndex(block_hash);); @@ -485,7 +490,7 @@ static RPCHelpMan getblockhash() LOCK(cs_main); const CChain& active_chain = chainman.ActiveChain(); - int nHeight = request.params[0].get_int(); + int nHeight = request.params[0].getInt<int>(); if (nHeight < 0 || nHeight > active_chain.Height()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); @@ -598,6 +603,29 @@ static CBlockUndo GetUndoChecked(BlockManager& blockman, const CBlockIndex* pblo return blockUndo; } +const RPCResult getblock_vin{ + RPCResult::Type::ARR, "vin", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::ELISION, "", "The same output as verbosity = 2"}, + {RPCResult::Type::OBJ, "prevout", "(Only if undo information is available)", + { + {RPCResult::Type::BOOL, "generated", "Coinbase or not"}, + {RPCResult::Type::NUM, "height", "The height of the prevout"}, + {RPCResult::Type::NUM, "value", "The value in " + CURRENCY_UNIT}, + {RPCResult::Type::OBJ, "scriptPubKey", "", + { + {RPCResult::Type::STR, "asm", "The asm"}, + {RPCResult::Type::STR, "hex", "The hex"}, + {RPCResult::Type::STR, "address", /*optional=*/true, "The Bitcoin address (only if a well-defined address exists)"}, + {RPCResult::Type::STR, "type", "The type (one of: " + GetAllOutputTypes() + ")"}, + }}, + }}, + }}, + } +}; + static RPCHelpMan getblock() { return RPCHelpMan{"getblock", @@ -657,26 +685,7 @@ static RPCHelpMan getblock() { {RPCResult::Type::OBJ, "", "", { - {RPCResult::Type::ARR, "vin", "", - { - {RPCResult::Type::OBJ, "", "", - { - {RPCResult::Type::ELISION, "", "The same output as verbosity = 2"}, - {RPCResult::Type::OBJ, "prevout", "(Only if undo information is available)", - { - {RPCResult::Type::BOOL, "generated", "Coinbase or not"}, - {RPCResult::Type::NUM, "height", "The height of the prevout"}, - {RPCResult::Type::NUM, "value", "The value in " + CURRENCY_UNIT}, - {RPCResult::Type::OBJ, "scriptPubKey", "", - { - {RPCResult::Type::STR, "asm", "The asm"}, - {RPCResult::Type::STR, "hex", "The hex"}, - {RPCResult::Type::STR, "address", /*optional=*/true, "The Bitcoin address (only if a well-defined address exists)"}, - {RPCResult::Type::STR, "type", "The type (one of: " + GetAllOutputTypes() + ")"}, - }}, - }}, - }}, - }}, + getblock_vin, }}, }}, }}, @@ -694,7 +703,7 @@ static RPCHelpMan getblock() if (request.params[1].isBool()) { verbosity = request.params[1].get_bool() ? 1 : 0; } else { - verbosity = request.params[1].get_int(); + verbosity = request.params[1].getInt<int>(); } } @@ -759,9 +768,10 @@ static RPCHelpMan pruneblockchain() CChainState& active_chainstate = chainman.ActiveChainstate(); CChain& active_chain = active_chainstate.m_chain; - int heightParam = request.params[0].get_int(); - if (heightParam < 0) + int heightParam = request.params[0].getInt<int>(); + if (heightParam < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative block height."); + } // Height value more than a billion is too high to be a block height, and // too low to be a block time (corresponds to timestamp from Sep 2001). @@ -776,20 +786,20 @@ static RPCHelpMan pruneblockchain() unsigned int height = (unsigned int) heightParam; unsigned int chainHeight = (unsigned int) active_chain.Height(); - if (chainHeight < Params().PruneAfterHeight()) + if (chainHeight < chainman.GetParams().PruneAfterHeight()) { throw JSONRPCError(RPC_MISC_ERROR, "Blockchain is too short for pruning."); - else if (height > chainHeight) + } else if (height > chainHeight) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Blockchain is shorter than the attempted prune height."); - else if (height > chainHeight - MIN_BLOCKS_TO_KEEP) { + } else if (height > chainHeight - MIN_BLOCKS_TO_KEEP) { LogPrint(BCLog::RPC, "Attempt to prune blocks close to the tip. Retaining the minimum number of blocks.\n"); height = chainHeight - MIN_BLOCKS_TO_KEEP; } PruneBlockFilesManual(active_chainstate, height); - const CBlockIndex* block = CHECK_NONFATAL(active_chain.Tip()); - const CBlockIndex* last_block = node::GetFirstStoredBlock(block); + const CBlockIndex& block{*CHECK_NONFATAL(active_chain.Tip())}; + const CBlockIndex* last_block{active_chainstate.m_blockman.GetFirstStoredBlock(block)}; - return static_cast<uint64_t>(last_block->nHeight); + return static_cast<int64_t>(last_block->nHeight - 1); }, }; } @@ -807,6 +817,36 @@ CoinStatsHashType ParseHashType(const std::string& hash_type_input) } } +/** + * Calculate statistics about the unspent transaction output set + * + * @param[in] index_requested Signals if the coinstatsindex should be used (when available). + */ +static std::optional<kernel::CCoinsStats> GetUTXOStats(CCoinsView* view, node::BlockManager& blockman, + kernel::CoinStatsHashType hash_type, + const std::function<void()>& interruption_point = {}, + const CBlockIndex* pindex = nullptr, + bool index_requested = true) +{ + // Use CoinStatsIndex if it is requested and available and a hash_type of Muhash or None was requested + if ((hash_type == kernel::CoinStatsHashType::MUHASH || hash_type == kernel::CoinStatsHashType::NONE) && g_coin_stats_index && index_requested) { + if (pindex) { + return g_coin_stats_index->LookUpStats(pindex); + } else { + CBlockIndex* block_index = WITH_LOCK(::cs_main, return blockman.LookupBlockIndex(view->GetBestBlock())); + return g_coin_stats_index->LookUpStats(block_index); + } + } + + // If the coinstats index isn't requested or is otherwise not usable, the + // pindex should either be null or equal to the view's best block. This is + // because without the coinstats index we can only get coinstats about the + // best block. + CHECK_NONFATAL(!pindex || pindex->GetBlockHash() == view->GetBestBlock()); + + return kernel::ComputeUTXOStats(hash_type, view, blockman, interruption_point); +} + static RPCHelpMan gettxoutsetinfo() { return RPCHelpMan{"gettxoutsetinfo", @@ -814,7 +854,7 @@ static RPCHelpMan gettxoutsetinfo() "Note this call may take some time if you are not using coinstatsindex.\n", { {"hash_type", RPCArg::Type::STR, RPCArg::Default{"hash_serialized_2"}, "Which UTXO set hash should be calculated. Options: 'hash_serialized_2' (the legacy algorithm), 'muhash', 'none'."}, - {"hash_or_height", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "The block hash or height of the target height (only available with coinstatsindex).", "", {"", "string or numeric"}}, + {"hash_or_height", RPCArg::Type::NUM, RPCArg::DefaultHint{"the current best block"}, "The block hash or height of the target height (only available with coinstatsindex).", "", {"", "string or numeric"}}, {"use_index", RPCArg::Type::BOOL, RPCArg::Default{true}, "Use coinstatsindex, if available."}, }, RPCResult{ @@ -850,6 +890,7 @@ static RPCHelpMan gettxoutsetinfo() HelpExampleCli("gettxoutsetinfo", R"("none")") + HelpExampleCli("gettxoutsetinfo", R"("none" 1000)") + HelpExampleCli("gettxoutsetinfo", R"("none" '"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09"')") + + HelpExampleCli("-named gettxoutsetinfo", R"(hash_type='muhash' use_index='false')") + HelpExampleRpc("gettxoutsetinfo", "") + HelpExampleRpc("gettxoutsetinfo", R"("none")") + HelpExampleRpc("gettxoutsetinfo", R"("none", 1000)") + @@ -861,8 +902,7 @@ static RPCHelpMan gettxoutsetinfo() const CBlockIndex* pindex{nullptr}; const CoinStatsHashType hash_type{request.params[0].isNull() ? CoinStatsHashType::HASH_SERIALIZED : ParseHashType(request.params[0].get_str())}; - CCoinsStats stats{hash_type}; - stats.index_requested = request.params[2].isNull() || request.params[2].get_bool(); + bool index_requested = request.params[2].isNull() || request.params[2].get_bool(); NodeContext& node = EnsureAnyNodeContext(request.context); ChainstateManager& chainman = EnsureChainman(node); @@ -883,14 +923,17 @@ static RPCHelpMan gettxoutsetinfo() throw JSONRPCError(RPC_INVALID_PARAMETER, "Querying specific block heights requires coinstatsindex"); } - if (stats.m_hash_type == CoinStatsHashType::HASH_SERIALIZED) { + if (hash_type == CoinStatsHashType::HASH_SERIALIZED) { throw JSONRPCError(RPC_INVALID_PARAMETER, "hash_serialized_2 hash type cannot be queried for a specific block"); } + if (!index_requested) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot set use_index to false when querying for a specific block"); + } pindex = ParseHashOrHeight(request.params[1], chainman); } - if (stats.index_requested && g_coin_stats_index) { + if (index_requested && g_coin_stats_index) { if (!g_coin_stats_index->BlockUntilSyncedToCurrentChain()) { const IndexSummary summary{g_coin_stats_index->GetSummary()}; @@ -902,7 +945,9 @@ static RPCHelpMan gettxoutsetinfo() } } - if (GetUTXOStats(coins_view, *blockman, stats, node.rpc_interruption_point, pindex)) { + const std::optional<CCoinsStats> maybe_stats = GetUTXOStats(coins_view, *blockman, hash_type, node.rpc_interruption_point, pindex, index_requested); + if (maybe_stats.has_value()) { + const CCoinsStats& stats = maybe_stats.value(); ret.pushKV("height", (int64_t)stats.nHeight); ret.pushKV("bestblock", stats.hashBlock.GetHex()); ret.pushKV("txouts", (int64_t)stats.nTransactionOutputs); @@ -921,10 +966,13 @@ static RPCHelpMan gettxoutsetinfo() } else { ret.pushKV("total_unspendable_amount", ValueFromAmount(stats.total_unspendable_amount)); - CCoinsStats prev_stats{hash_type}; - + CCoinsStats prev_stats{}; if (pindex->nHeight > 0) { - GetUTXOStats(coins_view, *blockman, prev_stats, node.rpc_interruption_point, pindex->pprev); + const std::optional<CCoinsStats> maybe_prev_stats = GetUTXOStats(coins_view, *blockman, hash_type, node.rpc_interruption_point, pindex->pprev, index_requested); + if (!maybe_prev_stats) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set"); + } + prev_stats = maybe_prev_stats.value(); } UniValue block_info(UniValue::VOBJ); @@ -992,8 +1040,7 @@ static RPCHelpMan gettxout() UniValue ret(UniValue::VOBJ); uint256 hash(ParseHashV(request.params[0], "txid")); - int n = request.params[1].get_int(); - COutPoint out(hash, n); + COutPoint out{hash, request.params[1].getInt<uint32_t>()}; bool fMempool = true; if (!request.params[2].isNull()) fMempool = request.params[2].get_bool(); @@ -1050,39 +1097,39 @@ static RPCHelpMan verifychain() }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { - const int check_level{request.params[0].isNull() ? DEFAULT_CHECKLEVEL : request.params[0].get_int()}; - const int check_depth{request.params[1].isNull() ? DEFAULT_CHECKBLOCKS : request.params[1].get_int()}; + const int check_level{request.params[0].isNull() ? DEFAULT_CHECKLEVEL : request.params[0].getInt<int>()}; + const int check_depth{request.params[1].isNull() ? DEFAULT_CHECKBLOCKS : request.params[1].getInt<int>()}; ChainstateManager& chainman = EnsureAnyChainman(request.context); LOCK(cs_main); CChainState& active_chainstate = chainman.ActiveChainstate(); return CVerifyDB().VerifyDB( - active_chainstate, Params().GetConsensus(), active_chainstate.CoinsTip(), check_level, check_depth); + active_chainstate, chainman.GetParams().GetConsensus(), active_chainstate.CoinsTip(), check_level, check_depth); }, }; } -static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softforks, const Consensus::Params& params, Consensus::BuriedDeployment dep) +static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softforks, const ChainstateManager& chainman, Consensus::BuriedDeployment dep) { // For buried deployments. - if (!DeploymentEnabled(params, dep)) return; + if (!DeploymentEnabled(chainman, dep)) return; UniValue rv(UniValue::VOBJ); rv.pushKV("type", "buried"); // getdeploymentinfo reports the softfork as active from when the chain height is // one below the activation height - rv.pushKV("active", DeploymentActiveAfter(blockindex, params, dep)); - rv.pushKV("height", params.DeploymentHeight(dep)); + rv.pushKV("active", DeploymentActiveAfter(blockindex, chainman, dep)); + rv.pushKV("height", chainman.GetConsensus().DeploymentHeight(dep)); softforks.pushKV(DeploymentName(dep), rv); } -static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softforks, const Consensus::Params& consensusParams, Consensus::DeploymentPos id) +static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softforks, const ChainstateManager& chainman, Consensus::DeploymentPos id) { // For BIP9 deployments. - if (!DeploymentEnabled(consensusParams, id)) return; + if (!DeploymentEnabled(chainman, id)) return; if (blockindex == nullptr) return; auto get_state_name = [](const ThresholdState state) -> std::string { @@ -1098,29 +1145,29 @@ static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softfo UniValue bip9(UniValue::VOBJ); - const ThresholdState next_state = g_versionbitscache.State(blockindex, consensusParams, id); - const ThresholdState current_state = g_versionbitscache.State(blockindex->pprev, consensusParams, id); + const ThresholdState next_state = chainman.m_versionbitscache.State(blockindex, chainman.GetConsensus(), id); + const ThresholdState current_state = chainman.m_versionbitscache.State(blockindex->pprev, chainman.GetConsensus(), id); const bool has_signal = (ThresholdState::STARTED == current_state || ThresholdState::LOCKED_IN == current_state); // BIP9 parameters if (has_signal) { - bip9.pushKV("bit", consensusParams.vDeployments[id].bit); + bip9.pushKV("bit", chainman.GetConsensus().vDeployments[id].bit); } - bip9.pushKV("start_time", consensusParams.vDeployments[id].nStartTime); - bip9.pushKV("timeout", consensusParams.vDeployments[id].nTimeout); - bip9.pushKV("min_activation_height", consensusParams.vDeployments[id].min_activation_height); + bip9.pushKV("start_time", chainman.GetConsensus().vDeployments[id].nStartTime); + bip9.pushKV("timeout", chainman.GetConsensus().vDeployments[id].nTimeout); + bip9.pushKV("min_activation_height", chainman.GetConsensus().vDeployments[id].min_activation_height); // BIP9 status bip9.pushKV("status", get_state_name(current_state)); - bip9.pushKV("since", g_versionbitscache.StateSinceHeight(blockindex->pprev, consensusParams, id)); + bip9.pushKV("since", chainman.m_versionbitscache.StateSinceHeight(blockindex->pprev, chainman.GetConsensus(), id)); bip9.pushKV("status_next", get_state_name(next_state)); // BIP9 signalling status, if applicable if (has_signal) { UniValue statsUV(UniValue::VOBJ); std::vector<bool> signals; - BIP9Stats statsStruct = g_versionbitscache.Statistics(blockindex, consensusParams, id, &signals); + BIP9Stats statsStruct = chainman.m_versionbitscache.Statistics(blockindex, chainman.GetConsensus(), id, &signals); statsUV.pushKV("period", statsStruct.period); statsUV.pushKV("elapsed", statsStruct.elapsed); statsUV.pushKV("count", statsStruct.count); @@ -1141,7 +1188,7 @@ static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softfo UniValue rv(UniValue::VOBJ); rv.pushKV("type", "bip9"); if (ThresholdState::ACTIVE == next_state) { - rv.pushKV("height", g_versionbitscache.StateSinceHeight(blockindex, consensusParams, id)); + rv.pushKV("height", chainman.m_versionbitscache.StateSinceHeight(blockindex, chainman.GetConsensus(), id)); } rv.pushKV("active", ThresholdState::ACTIVE == next_state); rv.pushKV("bip9", bip9); @@ -1149,16 +1196,9 @@ static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softfo softforks.pushKV(DeploymentName(id), rv); } -namespace { -/* TODO: when -deprecatedrpc=softforks is removed, drop these */ -UniValue DeploymentInfo(const CBlockIndex* tip, const Consensus::Params& consensusParams); -extern const std::vector<RPCResult> RPCHelpForDeployment; -} - // used by rest.cpp:rest_chaininfo, so cannot be static RPCHelpMan getblockchaininfo() { - /* TODO: from v24, remove -deprecatedrpc=softforks */ return RPCHelpMan{"getblockchaininfo", "Returns an object containing various state info regarding blockchain processing.\n", {}, @@ -1177,15 +1217,9 @@ RPCHelpMan getblockchaininfo() {RPCResult::Type::STR_HEX, "chainwork", "total amount of work in active chain, in hexadecimal"}, {RPCResult::Type::NUM, "size_on_disk", "the estimated size of the block and undo files on disk"}, {RPCResult::Type::BOOL, "pruned", "if the blocks are subject to pruning"}, - {RPCResult::Type::NUM, "pruneheight", /*optional=*/true, "lowest-height complete block stored (only present if pruning is enabled)"}, + {RPCResult::Type::NUM, "pruneheight", /*optional=*/true, "height of the last block pruned, plus one (only present if pruning is enabled)"}, {RPCResult::Type::BOOL, "automatic_pruning", /*optional=*/true, "whether automatic pruning is enabled (only present if pruning is enabled)"}, {RPCResult::Type::NUM, "prune_target_size", /*optional=*/true, "the target size used by pruning (only present if automatic pruning is enabled)"}, - {RPCResult::Type::OBJ_DYN, "softforks", /*optional=*/true, "(DEPRECATED, returned only if config option -deprecatedrpc=softforks is passed) status of softforks", - { - {RPCResult::Type::OBJ, "xxxx", "name of the softfork", - RPCHelpForDeployment - }, - }}, {RPCResult::Type::STR, "warnings", "any network and blockchain warnings"}, }}, RPCExamples{ @@ -1199,24 +1233,23 @@ RPCHelpMan getblockchaininfo() LOCK(cs_main); CChainState& active_chainstate = chainman.ActiveChainstate(); - const CBlockIndex* tip = CHECK_NONFATAL(active_chainstate.m_chain.Tip()); - const int height = tip->nHeight; + const CBlockIndex& tip{*CHECK_NONFATAL(active_chainstate.m_chain.Tip())}; + const int height{tip.nHeight}; UniValue obj(UniValue::VOBJ); - obj.pushKV("chain", Params().NetworkIDString()); + obj.pushKV("chain", chainman.GetParams().NetworkIDString()); obj.pushKV("blocks", height); obj.pushKV("headers", chainman.m_best_header ? chainman.m_best_header->nHeight : -1); - obj.pushKV("bestblockhash", tip->GetBlockHash().GetHex()); - obj.pushKV("difficulty", (double)GetDifficulty(tip)); - obj.pushKV("time", (int64_t)tip->nTime); - obj.pushKV("mediantime", (int64_t)tip->GetMedianTimePast()); - obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), tip)); + obj.pushKV("bestblockhash", tip.GetBlockHash().GetHex()); + obj.pushKV("difficulty", GetDifficulty(&tip)); + obj.pushKV("time", tip.GetBlockTime()); + obj.pushKV("mediantime", tip.GetMedianTimePast()); + obj.pushKV("verificationprogress", GuessVerificationProgress(chainman.GetParams().TxData(), &tip)); obj.pushKV("initialblockdownload", active_chainstate.IsInitialBlockDownload()); - obj.pushKV("chainwork", tip->nChainWork.GetHex()); + obj.pushKV("chainwork", tip.nChainWork.GetHex()); obj.pushKV("size_on_disk", chainman.m_blockman.CalculateCurrentUsage()); obj.pushKV("pruned", node::fPruneMode); if (node::fPruneMode) { - const CBlockIndex* block = CHECK_NONFATAL(tip); - obj.pushKV("pruneheight", node::GetFirstStoredBlock(block)->nHeight); + obj.pushKV("pruneheight", chainman.m_blockman.GetFirstStoredBlock(tip)->nHeight); // if 0, execution bypasses the whole if block. bool automatic_pruning{args.GetIntArg("-prune", 0) != 1}; @@ -1226,11 +1259,6 @@ RPCHelpMan getblockchaininfo() } } - if (IsDeprecatedRPCEnabled("softforks")) { - const Consensus::Params& consensusParams = Params().GetConsensus(); - obj.pushKV("softforks", DeploymentInfo(tip, consensusParams)); - } - obj.pushKV("warnings", GetWarnings(false).original); return obj; }, @@ -1263,16 +1291,16 @@ const std::vector<RPCResult> RPCHelpForDeployment{ }}, }; -UniValue DeploymentInfo(const CBlockIndex* blockindex, const Consensus::Params& consensusParams) +UniValue DeploymentInfo(const CBlockIndex* blockindex, const ChainstateManager& chainman) { UniValue softforks(UniValue::VOBJ); - SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_HEIGHTINCB); - SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_DERSIG); - SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_CLTV); - SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_CSV); - SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_SEGWIT); - SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_TESTDUMMY); - SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_TAPROOT); + SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_HEIGHTINCB); + SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_DERSIG); + SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_CLTV); + SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_CSV); + SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_SEGWIT); + SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_TESTDUMMY); + SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_TAPROOT); return softforks; } } // anon namespace @@ -1311,12 +1339,10 @@ static RPCHelpMan getdeploymentinfo() } } - const Consensus::Params& consensusParams = Params().GetConsensus(); - UniValue deploymentinfo(UniValue::VOBJ); deploymentinfo.pushKV("hash", blockindex->GetBlockHash().ToString()); deploymentinfo.pushKV("height", blockindex->nHeight); - deploymentinfo.pushKV("deployments", DeploymentInfo(blockindex, consensusParams)); + deploymentinfo.pushKV("deployments", DeploymentInfo(blockindex, chainman)); return deploymentinfo; }, }; @@ -1584,7 +1610,7 @@ static RPCHelpMan getchaintxstats() { ChainstateManager& chainman = EnsureAnyChainman(request.context); const CBlockIndex* pindex; - int blockcount = 30 * 24 * 60 * 60 / Params().GetConsensus().nPowTargetSpacing; // By default: 1 month + int blockcount = 30 * 24 * 60 * 60 / chainman.GetParams().GetConsensus().nPowTargetSpacing; // By default: 1 month if (request.params[1].isNull()) { LOCK(cs_main); @@ -1606,16 +1632,16 @@ static RPCHelpMan getchaintxstats() if (request.params[0].isNull()) { blockcount = std::max(0, std::min(blockcount, pindex->nHeight - 1)); } else { - blockcount = request.params[0].get_int(); + blockcount = request.params[0].getInt<int>(); if (blockcount < 0 || (blockcount > 0 && blockcount >= pindex->nHeight)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block count: should be between 0 and the block's height - 1"); } } - const CBlockIndex* pindexPast = pindex->GetAncestor(pindex->nHeight - blockcount); - int nTimeDiff = pindex->GetMedianTimePast() - pindexPast->GetMedianTimePast(); - int nTxDiff = pindex->nChainTx - pindexPast->nChainTx; + const CBlockIndex& past_block{*CHECK_NONFATAL(pindex->GetAncestor(pindex->nHeight - blockcount))}; + const int64_t nTimeDiff{pindex->GetMedianTimePast() - past_block.GetMedianTimePast()}; + const int nTxDiff = pindex->nChainTx - past_block.nChainTx; UniValue ret(UniValue::VOBJ); ret.pushKV("time", (int64_t)pindex->nTime); @@ -1756,8 +1782,7 @@ static RPCHelpMan getblockstats() { ChainstateManager& chainman = EnsureAnyChainman(request.context); LOCK(cs_main); - const CBlockIndex* pindex{ParseHashOrHeight(request.params[0], chainman)}; - CHECK_NONFATAL(pindex != nullptr); + const CBlockIndex& pindex{*CHECK_NONFATAL(ParseHashOrHeight(request.params[0], chainman))}; std::set<std::string> stats; if (!request.params[1].isNull()) { @@ -1768,8 +1793,8 @@ static RPCHelpMan getblockstats() } } - const CBlock block = GetBlockChecked(chainman.m_blockman, pindex); - const CBlockUndo blockUndo = GetUndoChecked(chainman.m_blockman, pindex); + const CBlock& block = GetBlockChecked(chainman.m_blockman, &pindex); + const CBlockUndo& blockUndo = GetUndoChecked(chainman.m_blockman, &pindex); const bool do_all = stats.size() == 0; // Calculate everything if nothing selected (default) const bool do_mediantxsize = do_all || stats.count("mediantxsize") != 0; @@ -1887,25 +1912,25 @@ static RPCHelpMan getblockstats() ret_all.pushKV("avgfee", (block.vtx.size() > 1) ? totalfee / (block.vtx.size() - 1) : 0); ret_all.pushKV("avgfeerate", total_weight ? (totalfee * WITNESS_SCALE_FACTOR) / total_weight : 0); // Unit: sat/vbyte ret_all.pushKV("avgtxsize", (block.vtx.size() > 1) ? total_size / (block.vtx.size() - 1) : 0); - ret_all.pushKV("blockhash", pindex->GetBlockHash().GetHex()); + ret_all.pushKV("blockhash", pindex.GetBlockHash().GetHex()); ret_all.pushKV("feerate_percentiles", feerates_res); - ret_all.pushKV("height", (int64_t)pindex->nHeight); + ret_all.pushKV("height", (int64_t)pindex.nHeight); ret_all.pushKV("ins", inputs); ret_all.pushKV("maxfee", maxfee); ret_all.pushKV("maxfeerate", maxfeerate); ret_all.pushKV("maxtxsize", maxtxsize); ret_all.pushKV("medianfee", CalculateTruncatedMedian(fee_array)); - ret_all.pushKV("mediantime", pindex->GetMedianTimePast()); + ret_all.pushKV("mediantime", pindex.GetMedianTimePast()); ret_all.pushKV("mediantxsize", CalculateTruncatedMedian(txsize_array)); ret_all.pushKV("minfee", (minfee == MAX_MONEY) ? 0 : minfee); ret_all.pushKV("minfeerate", (minfeerate == MAX_MONEY) ? 0 : minfeerate); ret_all.pushKV("mintxsize", mintxsize == MAX_BLOCK_SERIALIZED_SIZE ? 0 : mintxsize); ret_all.pushKV("outs", outputs); - ret_all.pushKV("subsidy", GetBlockSubsidy(pindex->nHeight, Params().GetConsensus())); + ret_all.pushKV("subsidy", GetBlockSubsidy(pindex.nHeight, chainman.GetParams().GetConsensus())); ret_all.pushKV("swtotal_size", swtotal_size); ret_all.pushKV("swtotal_weight", swtotal_weight); ret_all.pushKV("swtxs", swtxs); - ret_all.pushKV("time", pindex->GetBlockTime()); + ret_all.pushKV("time", pindex.GetBlockTime()); ret_all.pushKV("total_out", total_out); ret_all.pushKV("total_size", total_size); ret_all.pushKV("total_weight", total_weight); @@ -1970,9 +1995,9 @@ static std::atomic<bool> g_should_abort_scan; class CoinsViewScanReserver { private: - bool m_could_reserve; + bool m_could_reserve{false}; public: - explicit CoinsViewScanReserver() : m_could_reserve(false) {} + explicit CoinsViewScanReserver() = default; bool reserve() { CHECK_NONFATAL(!m_could_reserve); @@ -2028,13 +2053,7 @@ static RPCHelpMan scantxoutset() "[scanobjects,...]"}, }, { - RPCResult{"When action=='abort'", RPCResult::Type::BOOL, "", ""}, - RPCResult{"When action=='status' and no scan is in progress", RPCResult::Type::NONE, "", ""}, - RPCResult{"When action=='status' and scan is in progress", RPCResult::Type::OBJ, "", "", - { - {RPCResult::Type::NUM, "progress", "The scan progress"}, - }}, - RPCResult{"When action=='start'", RPCResult::Type::OBJ, "", "", { + RPCResult{"when action=='start'; only returns after scan completes", RPCResult::Type::OBJ, "", "", { {RPCResult::Type::BOOL, "success", "Whether the scan was completed"}, {RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs scanned"}, {RPCResult::Type::NUM, "height", "The current block height (index)"}, @@ -2053,6 +2072,12 @@ static RPCHelpMan scantxoutset() }}, {RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount of all found unspent outputs in " + CURRENCY_UNIT}, }}, + RPCResult{"when action=='abort'", RPCResult::Type::BOOL, "success", "True if scan will be aborted (not necessarily before this RPC returns), or false if there is no scan to abort"}, + RPCResult{"when action=='status' and a scan is currently in progress", RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "progress", "Approximate percent complete"}, + }}, + RPCResult{"when action=='status' and no scan is in progress - possibly already completed", RPCResult::Type::NONE, "", ""}, }, RPCExamples{ HelpExampleCli("scantxoutset", "start \'[\"" + EXAMPLE_DESCRIPTOR_RAW + "\"]\'") + @@ -2073,7 +2098,7 @@ static RPCHelpMan scantxoutset() // no scan in progress return NullUniValue; } - result.pushKV("progress", g_scan_progress); + result.pushKV("progress", g_scan_progress.load()); return result; } else if (request.params[0].get_str() == "abort") { CoinsViewScanReserver reserver; @@ -2165,7 +2190,7 @@ static RPCHelpMan getblockfilter() "\nRetrieve a BIP 157 content filter for a particular block.\n", { {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hash of the block"}, - {"filtertype", RPCArg::Type::STR, RPCArg::Default{"basic"}, "The type name of the filter"}, + {"filtertype", RPCArg::Type::STR, RPCArg::Default{BlockFilterTypeName(BlockFilterType::BASIC)}, "The type name of the filter"}, }, RPCResult{ RPCResult::Type::OBJ, "", "", @@ -2180,7 +2205,7 @@ static RPCHelpMan getblockfilter() [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { uint256 block_hash = ParseHashV(request.params[0], "blockhash"); - std::string filtertype_name = "basic"; + std::string filtertype_name = BlockFilterTypeName(BlockFilterType::BASIC); if (!request.params[1].isNull()) { filtertype_name = request.params[1].get_str(); } @@ -2282,6 +2307,12 @@ static RPCHelpMan dumptxoutset() FILE* file{fsbridge::fopen(temppath, "wb")}; CAutoFile afile{file, SER_DISK, CLIENT_VERSION}; + if (afile.IsNull()) { + throw JSONRPCError( + RPC_INVALID_PARAMETER, + "Couldn't open file " + temppath.u8string() + " for writing."); + } + NodeContext& node = EnsureAnyNodeContext(request.context); UniValue result = CreateUTXOSnapshot( node, node.chainman->ActiveChainstate(), afile, path, temppath); @@ -2301,7 +2332,7 @@ UniValue CreateUTXOSnapshot( const fs::path& temppath) { std::unique_ptr<CCoinsViewCursor> pcursor; - CCoinsStats stats{CoinStatsHashType::HASH_SERIALIZED}; + std::optional<CCoinsStats> maybe_stats; const CBlockIndex* tip; { @@ -2321,19 +2352,20 @@ UniValue CreateUTXOSnapshot( chainstate.ForceFlushStateToDisk(); - if (!GetUTXOStats(&chainstate.CoinsDB(), chainstate.m_blockman, stats, node.rpc_interruption_point)) { + maybe_stats = GetUTXOStats(&chainstate.CoinsDB(), chainstate.m_blockman, CoinStatsHashType::HASH_SERIALIZED, node.rpc_interruption_point); + if (!maybe_stats) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set"); } pcursor = chainstate.CoinsDB().Cursor(); - tip = CHECK_NONFATAL(chainstate.m_blockman.LookupBlockIndex(stats.hashBlock)); + tip = CHECK_NONFATAL(chainstate.m_blockman.LookupBlockIndex(maybe_stats->hashBlock)); } LOG_TIME_SECONDS(strprintf("writing UTXO snapshot at height %s (%s) to file %s (via %s)", tip->nHeight, tip->GetBlockHash().ToString(), fs::PathToString(path), fs::PathToString(temppath))); - SnapshotMetadata metadata{tip->GetBlockHash(), stats.coins_count, tip->nChainTx}; + SnapshotMetadata metadata{tip->GetBlockHash(), maybe_stats->coins_count, tip->nChainTx}; afile << metadata; @@ -2355,11 +2387,11 @@ UniValue CreateUTXOSnapshot( afile.fclose(); UniValue result(UniValue::VOBJ); - result.pushKV("coins_written", stats.coins_count); + result.pushKV("coins_written", maybe_stats->coins_count); result.pushKV("base_hash", tip->GetBlockHash().ToString()); result.pushKV("base_height", tip->nHeight); result.pushKV("path", path.u8string()); - result.pushKV("txoutset_hash", stats.hashSerialized.ToString()); + result.pushKV("txoutset_hash", maybe_stats->hashSerialized.ToString()); // Cast required because univalue doesn't have serialization specified for // `unsigned int`, nChainTx's type. result.pushKV("nchaintx", uint64_t{tip->nChainTx}); |