LCOV - code coverage report
Current view: top level - src/rpc - blockchain.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 1756 1917 91.6 %
Date: 2026-06-25 07:23:43 Functions: 111 116 95.7 %

          Line data    Source code
       1             : // Copyright (c) 2010 Satoshi Nakamoto
       2             : // Copyright (c) 2009-2022 The Bitcoin Core developers
       3             : // Copyright (c) 2014-2025 The Dash Core developers
       4             : // Distributed under the MIT software license, see the accompanying
       5             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       6             : 
       7             : #include <rpc/blockchain.h>
       8             : 
       9             : #include <blockfilter.h>
      10             : #include <chain.h>
      11             : #include <chainparams.h>
      12             : #include <coins.h>
      13             : #include <consensus/amount.h>
      14             : #include <core_io.h>
      15             : #include <consensus/params.h>
      16             : #include <consensus/validation.h>
      17             : #include <deploymentinfo.h>
      18             : #include <deploymentstatus.h>
      19             : #include <evo/chainhelper.h>
      20             : #include <fs.h>
      21             : #include <index/blockfilterindex.h>
      22             : #include <index/coinstatsindex.h>
      23             : #include <index/timestampindex.h>
      24             : #include <index/txindex.h>
      25             : #include <kernel/coinstats.h>
      26             : #include <logging/timer.h>
      27             : #include <node/blockstorage.h>
      28             : #include <net.h>
      29             : #include <net_processing.h>
      30             : #include <node/context.h>
      31             : #include <node/utxo_snapshot.h>
      32             : #include <merkleblock.h>
      33             : #include <primitives/transaction.h>
      34             : #include <rpc/server.h>
      35             : #include <rpc/server_util.h>
      36             : #include <rpc/util.h>
      37             : #include <script/descriptor.h>
      38             : #include <streams.h>
      39             : #include <sync.h>
      40             : #include <txmempool.h>
      41             : #include <undo.h>
      42             : #include <univalue.h>
      43             : #include <util/check.h>
      44             : #include <util/strencodings.h>
      45             : #include <util/system.h>
      46             : #include <util/translation.h>
      47             : #include <validation.h>
      48             : #include <validationinterface.h>
      49             : #include <versionbits.h>
      50             : #include <warnings.h>
      51             : 
      52             : #include <chainlock/chainlock.h>
      53             : #include <evo/assetlocktx.h>
      54             : #include <evo/cbtx.h>
      55             : #include <evo/evodb.h>
      56             : #include <evo/mnhftx.h>
      57             : #include <evo/specialtx.h>
      58             : #include <instantsend/instantsend.h>
      59             : #include <llmq/context.h>
      60             : 
      61             : #include <stdint.h>
      62             : 
      63             : #include <atomic>
      64             : #include <condition_variable>
      65             : #include <mutex>
      66             : 
      67             : using kernel::CCoinsStats;
      68             : using kernel::CoinStatsHashType;
      69             : 
      70             : using node::BlockManager;
      71             : using node::NodeContext;
      72             : using node::ReadBlockFromDisk;
      73             : using node::SnapshotMetadata;
      74             : using node::UndoReadFromDisk;
      75             : 
      76             : struct CUpdatedBlock
      77             : {
      78             :     uint256 hash;
      79             :     int height;
      80             : };
      81             : 
      82             : static GlobalMutex cs_blockchange;
      83             : static std::condition_variable cond_blockchange;
      84        3308 : static CUpdatedBlock latestblock GUARDED_BY(cs_blockchange);
      85             : 
      86             : extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, const CTxMemPool& mempool, const CChainState& active_chainstate, const chainlock::Chainlocks& chainlocks, const llmq::CInstantSendManager& isman, UniValue& entry, TxVerbosity verbosity = TxVerbosity::SHOW_DETAILS);
      87             : 
      88             : /* Calculate the difficulty for a given block index.
      89             :  */
      90       26449 : double GetDifficulty(const CBlockIndex* blockindex)
      91             : {
      92       26449 :     CHECK_NONFATAL(blockindex);
      93             : 
      94       26449 :     return ConvertBitsToDouble(blockindex->nBits);
      95             : }
      96             : 
      97       11124 : static int ComputeNextBlockAndDepth(const CBlockIndex* tip, const CBlockIndex* blockindex, const CBlockIndex*& next)
      98             : {
      99       11124 :     next = tip->GetAncestor(blockindex->nHeight + 1);
     100       11124 :     if (next && next->pprev == blockindex) {
     101        4343 :         return tip->nHeight - blockindex->nHeight + 1;
     102             :     }
     103        6781 :     next = nullptr;
     104        6781 :     return blockindex == tip ? 1 : -1;
     105       11124 : }
     106             : 
     107         231 : static const CBlockIndex* ParseHashOrHeight(const UniValue& param, ChainstateManager& chainman)
     108             : {
     109         231 :     LOCK(::cs_main);
     110         231 :     CChain& active_chain = chainman.ActiveChain();
     111             : 
     112         231 :     if (param.isNum()) {
     113         215 :         const int height{param.getInt<int>()};
     114         215 :         if (height < 0) {
     115           2 :             throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Target block height %d is negative", height));
     116             :         }
     117         213 :         const int current_tip{active_chain.Height()};
     118         213 :         if (height > current_tip) {
     119           2 :             throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Target block height %d after current tip %d", height, current_tip));
     120             :         }
     121             : 
     122         211 :         return active_chain[height];
     123             :     } else {
     124          16 :         const uint256 hash{ParseHashV(param, "hash_or_height")};
     125          16 :         const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash);
     126             : 
     127          16 :         if (!pindex) {
     128           4 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
     129             :         }
     130             : 
     131          12 :         return pindex;
     132             :     }
     133         239 : }
     134             : 
     135       11124 : UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex, const chainlock::Chainlocks& chainlocks)
     136             : {
     137             :     // Serialize passed information without accessing chain state of the active chain!
     138       11124 :     AssertLockNotHeld(cs_main); // For performance reasons
     139             : 
     140       11124 :     UniValue result(UniValue::VOBJ);
     141       11124 :     result.pushKV("hash", blockindex->GetBlockHash().GetHex());
     142             :     const CBlockIndex* pnext;
     143       11124 :     int confirmations = ComputeNextBlockAndDepth(tip, blockindex, pnext);
     144       11124 :     result.pushKV("confirmations", confirmations);
     145       11124 :     result.pushKV("height", blockindex->nHeight);
     146       11124 :     result.pushKV("version", blockindex->nVersion);
     147       11124 :     result.pushKV("versionHex", strprintf("%08x", blockindex->nVersion));
     148       11124 :     result.pushKV("merkleroot", blockindex->hashMerkleRoot.GetHex());
     149       11124 :     result.pushKV("time", (int64_t)blockindex->nTime);
     150       11124 :     result.pushKV("mediantime", (int64_t)blockindex->GetMedianTimePast());
     151       11124 :     result.pushKV("nonce", (uint64_t)blockindex->nNonce);
     152       11124 :     result.pushKV("bits", strprintf("%08x", blockindex->nBits));
     153       11124 :     result.pushKV("difficulty", GetDifficulty(blockindex));
     154       11124 :     result.pushKV("chainwork", blockindex->nChainWork.GetHex());
     155       11124 :     result.pushKV("nTx", (uint64_t)blockindex->nTx);
     156             : 
     157       11124 :     if (blockindex->pprev)
     158       11087 :         result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex());
     159       11124 :     if (pnext)
     160        4343 :         result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex());
     161             : 
     162       11124 :     result.pushKV("chainlock", chainlocks.HasChainLock(blockindex->nHeight, blockindex->GetBlockHash()));
     163             : 
     164       11124 :     return result;
     165       11124 : }
     166             : 
     167        4394 : UniValue blockToJSON(BlockManager& blockman, const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, const chainlock::Chainlocks& chainlocks, const llmq::CInstantSendManager& isman, TxVerbosity verbosity)
     168             : {
     169        4394 :     UniValue result = blockheaderToJSON(tip, blockindex, chainlocks);
     170             : 
     171        4394 :     result.pushKV("size", (int)::GetSerializeSize(block, PROTOCOL_VERSION));
     172        4394 :     UniValue txs(UniValue::VARR);
     173        4394 :     switch (verbosity) {
     174             :         case TxVerbosity::SHOW_TXID:
     175       20559 :             for (const CTransactionRef& tx : block.vtx) {
     176       16455 :                 txs.push_back(tx->GetHash().GetHex());
     177             :             }
     178        4104 :             break;
     179             :         case TxVerbosity::SHOW_DETAILS:
     180             :         case TxVerbosity::SHOW_DETAILS_AND_PREVOUT:
     181         290 :             CBlockUndo blockUndo;
     182         580 :             const bool is_not_pruned{WITH_LOCK(::cs_main, return !blockman.IsBlockPruned(blockindex))};
     183         580 :             const bool have_undo{is_not_pruned && UndoReadFromDisk(blockUndo, blockindex)};
     184             : 
     185         700 :             for (size_t i = 0; i < block.vtx.size(); ++i) {
     186         410 :                 const CTransactionRef& tx = block.vtx.at(i);
     187             :                 // coinbase transaction (i.e. i == 0) doesn't have undo data
     188         410 :                 const CTxUndo* txundo = (have_undo && i > 0) ? &blockUndo.vtxundo.at(i - 1) : nullptr;
     189         410 :                 UniValue objTx(UniValue::VOBJ);
     190         410 :                 TxToUniv(*tx, /*block_hash=*/uint256(), /*entry=*/objTx, /*include_hex=*/true, /*serialize_flags=*/0, txundo, verbosity);
     191         410 :                 bool fLocked = isman.IsLocked(tx->GetHash());
     192         410 :                 objTx.pushKV("instantlock", fLocked || result["chainlock"].get_bool());
     193         410 :                 objTx.pushKV("instantlock_internal", fLocked);
     194         410 :                 txs.push_back(objTx);
     195         410 :             }
     196             :             break;
     197         290 :     }
     198             : 
     199        4394 :     result.pushKV("tx", txs);
     200        4394 :     if (!block.vtx[0]->vExtraPayload.empty()) {
     201        6258 :         if (const auto opt_cbTx = GetTxPayload<CCbTx>(block.vtx[0]->vExtraPayload)) {
     202        3129 :             result.pushKV("cbTx", opt_cbTx->ToJson());
     203        3129 :         }
     204        3129 :     }
     205             : 
     206        4394 :     return result;
     207        4394 : }
     208             : 
     209       17260 : static RPCHelpMan getblockcount()
     210             : {
     211       34520 :     return RPCHelpMan{"getblockcount",
     212       17260 :                 "\nReturns the height of the most-work fully-validated chain.\n"
     213             :                 "The genesis block has height 0.\n",
     214       17260 :                 {},
     215       17260 :                 RPCResult{
     216       17260 :                     RPCResult::Type::NUM, "", "The current block count"},
     217       17260 :                 RPCExamples{
     218       17260 :                     HelpExampleCli("getblockcount", "")
     219       17260 :             + HelpExampleRpc("getblockcount", "")
     220             :                 },
     221       28364 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     222             : {
     223       11104 :     ChainstateManager& chainman = EnsureAnyChainman(request.context);
     224       11104 :     LOCK(cs_main);
     225       11104 :     return chainman.ActiveChain().Height();
     226       11104 : },
     227             :     };
     228           0 : }
     229             : 
     230       58251 : static RPCHelpMan getbestblockhash()
     231             : {
     232      116502 :     return RPCHelpMan{"getbestblockhash",
     233       58251 :                 "\nReturns the hash of the best (tip) block in the most-work fully-validated chain.\n",
     234       58251 :                 {},
     235       58251 :                 RPCResult{
     236       58251 :                     RPCResult::Type::STR_HEX, "", "the block hash, hex-encoded"},
     237       58251 :                 RPCExamples{
     238       58251 :                     HelpExampleCli("getbestblockhash", "")
     239       58251 :             + HelpExampleRpc("getbestblockhash", "")
     240             :                 },
     241      110346 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     242             : {
     243       52095 :     ChainstateManager& chainman = EnsureAnyChainman(request.context);
     244       52095 :     LOCK(cs_main);
     245       52095 :     return chainman.ActiveChain().Tip()->GetBlockHash().GetHex();
     246       52095 : },
     247             :     };
     248           0 : }
     249             : 
     250        6215 : static RPCHelpMan getbestchainlock()
     251             : {
     252       12430 :     return RPCHelpMan{"getbestchainlock",
     253        6215 :         "\nReturns information about the best ChainLock. Throws an error if there is no known ChainLock yet.",
     254        6215 :         {},
     255        6215 :         RPCResult{
     256        6215 :             RPCResult::Type::OBJ, "", "",
     257       37290 :             {
     258        6215 :                 {RPCResult::Type::STR_HEX, "hash", "The block hash hex-encoded"},
     259        6215 :                 {RPCResult::Type::NUM, "height", "The block height or index"},
     260        6215 :                 {RPCResult::Type::STR_HEX, "signature", "The ChainLock's BLS signature"},
     261        6215 :                 {RPCResult::Type::BOOL, "known_block", "True if the block is known by our node"},
     262        6215 :                 {RPCResult::Type::STR_HEX, "hex", "The serialized, hex-encoded data for best ChainLock"},
     263             :             }},
     264        6215 :         RPCExamples{
     265        6215 :             HelpExampleCli("getbestchainlock", "")
     266        6215 :             + HelpExampleRpc("getbestchainlock", "")
     267             :         },
     268        6274 :     [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     269             : {
     270          59 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     271             : 
     272          59 :     CHECK_NONFATAL(node.chainlocks);
     273          59 :     const chainlock::ChainLockSig clsig = node.chainlocks->GetBestChainLock();
     274          59 :     if (clsig.IsNull()) {
     275           6 :         throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to find any ChainLock");
     276             :     }
     277             : 
     278          53 :     UniValue result(UniValue::VOBJ);
     279             : 
     280          53 :     result.pushKV("blockhash", clsig.getBlockHash().GetHex());
     281          53 :     result.pushKV("height", clsig.getHeight());
     282          53 :     result.pushKV("signature", clsig.getSig().ToString());
     283             : 
     284             :     {
     285          53 :         const ChainstateManager& chainman = EnsureChainman(node);
     286          53 :         LOCK(cs_main);
     287          53 :         result.pushKV("known_block", chainman.m_blockman.LookupBlockIndex(clsig.getBlockHash()) != nullptr);
     288          53 :     }
     289             : 
     290          53 :     CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
     291          53 :     ssTx << clsig;
     292          53 :     result.pushKV("hex", HexStr(ssTx));
     293             : 
     294          53 :     return result;
     295          65 : },
     296             :     };
     297           0 : }
     298             : 
     299      226543 : void RPCNotifyBlockChange(const CBlockIndex* pindex)
     300             : {
     301      226543 :     if(pindex) {
     302      223524 :         LOCK(cs_blockchange);
     303      223524 :         latestblock.hash = pindex->GetBlockHash();
     304      223524 :         latestblock.height = pindex->nHeight;
     305      223524 :     }
     306      226543 :     cond_blockchange.notify_all();
     307      226543 : }
     308             : 
     309        6142 : static RPCHelpMan waitfornewblock()
     310             : {
     311       12284 :     return RPCHelpMan{"waitfornewblock",
     312        6142 :                 "\nWaits for a specific new block and returns useful info about it.\n"
     313             :                 "\nReturns the current block on timeout or exit.\n",
     314       12284 :                 {
     315        6142 :                     {"timeout", RPCArg::Type::NUM, RPCArg::Default{0}, "Time in milliseconds to wait for a response. 0 indicates no timeout."},
     316             :                 },
     317        6142 :                 RPCResult{
     318        6142 :                     RPCResult::Type::OBJ, "", "",
     319       18426 :                     {
     320        6142 :                         {RPCResult::Type::STR_HEX, "hash", "The blockhash"},
     321        6142 :                         {RPCResult::Type::NUM, "height", "Block height"},
     322             :                     }},
     323        6142 :                 RPCExamples{
     324        6142 :                     HelpExampleCli("waitfornewblock", "1000")
     325        6142 :             + HelpExampleRpc("waitfornewblock", "1000")
     326             :                 },
     327        6144 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     328             : {
     329           2 :     int timeout = 0;
     330           2 :     if (!request.params[0].isNull())
     331           0 :         timeout = request.params[0].getInt<int>();
     332             : 
     333           2 :     CUpdatedBlock block;
     334             :     {
     335           2 :         WAIT_LOCK(cs_blockchange, lock);
     336           2 :         block = latestblock;
     337           2 :         if(timeout)
     338           0 :             cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&block]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); });
     339             :         else
     340           6 :             cond_blockchange.wait(lock, [&block]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); });
     341           2 :         block = latestblock;
     342           2 :     }
     343           2 :     UniValue ret(UniValue::VOBJ);
     344           2 :     ret.pushKV("hash", block.hash.GetHex());
     345           2 :     ret.pushKV("height", block.height);
     346           2 :     return ret;
     347           2 : },
     348             :     };
     349           0 : }
     350             : 
     351        6161 : static RPCHelpMan waitforblock()
     352             : {
     353       12322 :     return RPCHelpMan{"waitforblock",
     354        6161 :                 "\nWaits for a specific new block and returns useful info about it.\n"
     355             :                 "\nReturns the current block on timeout or exit.\n",
     356       18483 :                 {
     357        6161 :                     {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Block hash to wait for."},
     358        6161 :                     {"timeout", RPCArg::Type::NUM, RPCArg::Default{0}, "Time in milliseconds to wait for a response. 0 indicates no timeout."},
     359             :                 },
     360        6161 :                 RPCResult{
     361        6161 :                     RPCResult::Type::OBJ, "", "",
     362       18483 :                     {
     363        6161 :                         {RPCResult::Type::STR_HEX, "hash", "The blockhash"},
     364        6161 :                         {RPCResult::Type::NUM, "height", "Block height"},
     365             :                     }},
     366        6161 :                 RPCExamples{
     367        6161 :                     HelpExampleCli("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\" 1000")
     368        6161 :             + HelpExampleRpc("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\", 1000")
     369             :                 },
     370        6182 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     371             : {
     372          21 :     int timeout = 0;
     373             : 
     374          21 :     uint256 hash(ParseHashV(request.params[0], "blockhash"));
     375             : 
     376          21 :     if (!request.params[1].isNull())
     377          21 :         timeout = request.params[1].getInt<int>();
     378             : 
     379          21 :     CUpdatedBlock block;
     380             :     {
     381          21 :         WAIT_LOCK(cs_blockchange, lock);
     382          21 :         if(timeout)
     383          63 :             cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&hash]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.hash == hash || !IsRPCRunning();});
     384             :         else
     385           0 :             cond_blockchange.wait(lock, [&hash]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.hash == hash || !IsRPCRunning(); });
     386          21 :         block = latestblock;
     387          21 :     }
     388             : 
     389          21 :     UniValue ret(UniValue::VOBJ);
     390          21 :     ret.pushKV("hash", block.hash.GetHex());
     391          21 :     ret.pushKV("height", block.height);
     392          21 :     return ret;
     393          21 : },
     394             :     };
     395           0 : }
     396             : 
     397        6160 : static RPCHelpMan waitforblockheight()
     398             : {
     399       12320 :     return RPCHelpMan{"waitforblockheight",
     400        6160 :                 "\nWaits for (at least) block height and returns the height and hash\n"
     401             :                 "of the current tip.\n"
     402             :                 "\nReturns the current block on timeout or exit.\n",
     403       18480 :                 {
     404        6160 :                     {"height", RPCArg::Type::NUM, RPCArg::Optional::NO, "Block height to wait for."},
     405        6160 :                     {"timeout", RPCArg::Type::NUM, RPCArg::Default{0}, "Time in milliseconds to wait for a response. 0 indicates no timeout."},
     406             :                 },
     407        6160 :                 RPCResult{
     408        6160 :                     RPCResult::Type::OBJ, "", "",
     409       18480 :                     {
     410        6160 :                         {RPCResult::Type::STR_HEX, "hash", "The blockhash"},
     411        6160 :                         {RPCResult::Type::NUM, "height", "Block height"},
     412             :                     }},
     413        6160 :                 RPCExamples{
     414        6160 :                     HelpExampleCli("waitforblockheight", "100 1000")
     415        6160 :             + HelpExampleRpc("waitforblockheight", "100, 1000")
     416             :                 },
     417        6180 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     418             : {
     419          20 :     int timeout = 0;
     420             : 
     421          20 :     int height = request.params[0].getInt<int>();
     422             : 
     423          20 :     if (!request.params[1].isNull())
     424          16 :         timeout = request.params[1].getInt<int>();
     425             : 
     426          20 :     CUpdatedBlock block;
     427             :     {
     428          20 :         WAIT_LOCK(cs_blockchange, lock);
     429          20 :         if(timeout)
     430          36 :             cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&height]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.height >= height || !IsRPCRunning();});
     431             :         else
     432          30 :             cond_blockchange.wait(lock, [&height]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.height >= height || !IsRPCRunning(); });
     433          20 :         block = latestblock;
     434          20 :     }
     435          20 :     UniValue ret(UniValue::VOBJ);
     436          20 :     ret.pushKV("hash", block.hash.GetHex());
     437          20 :     ret.pushKV("height", block.height);
     438          20 :     return ret;
     439          20 : },
     440             :     };
     441           0 : }
     442             : 
     443       24649 : static RPCHelpMan syncwithvalidationinterfacequeue()
     444             : {
     445       49298 :     return RPCHelpMan{"syncwithvalidationinterfacequeue",
     446       24649 :                 "\nWaits for the validation interface queue to catch up on everything that was there when we entered this function.\n",
     447       24649 :                 {},
     448       24649 :                 RPCResult{RPCResult::Type::NONE, "", ""},
     449       24649 :                 RPCExamples{
     450       24649 :                     HelpExampleCli("syncwithvalidationinterfacequeue","")
     451       24649 :             + HelpExampleRpc("syncwithvalidationinterfacequeue","")
     452             :                 },
     453       43158 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     454             : {
     455       18509 :     SyncWithValidationInterfaceQueue();
     456       18509 :     return UniValue::VNULL;
     457           0 : },
     458             :     };
     459           0 : }
     460             : 
     461        6160 : static RPCHelpMan getdifficulty()
     462             : {
     463       12320 :     return RPCHelpMan{"getdifficulty",
     464        6160 :                 "\nReturns the proof-of-work difficulty as a multiple of the minimum difficulty.\n",
     465        6160 :                 {},
     466        6160 :                 RPCResult{
     467        6160 :                     RPCResult::Type::NUM, "", "the proof-of-work difficulty as a multiple of the minimum difficulty."},
     468        6160 :                 RPCExamples{
     469        6160 :                     HelpExampleCli("getdifficulty", "")
     470        6160 :             + HelpExampleRpc("getdifficulty", "")
     471             :                 },
     472        6164 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     473             : {
     474             : 
     475           4 :     ChainstateManager& chainman = EnsureAnyChainman(request.context);
     476           4 :     LOCK(cs_main);
     477           4 :     return GetDifficulty(chainman.ActiveChain().Tip());
     478           4 : },
     479             :     };
     480           0 : }
     481             : 
     482        6174 : static RPCHelpMan getblockfrompeer()
     483             : {
     484        6174 :     return RPCHelpMan{
     485        6174 :         "getblockfrompeer",
     486        6174 :         "Attempt to fetch block from a given peer.\n\n"
     487             :         "We must have the header for this block, e.g. using submitheader.\n"
     488             :         "Subsequent calls for the same block and a new peer will cause the response from the previous peer to be ignored.\n"
     489             :         "Peers generally ignore requests for a stale block that they never fully verified, or one that is more than a month old.\n"
     490             :         "When a peer does not respond with a block, we will disconnect.\n\n"
     491             :         "Returns an empty JSON object if the request was successfully scheduled.",
     492       18522 :         {
     493        6174 :             {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash to try to fetch"},
     494        6174 :             {"peer_id", RPCArg::Type::NUM, RPCArg::Optional::NO, "The peer to fetch it from (see getpeerinfo for peer IDs)"},
     495             :         },
     496        6174 :         RPCResult{RPCResult::Type::OBJ, "", /*optional=*/false, "", {}},
     497        6174 :         RPCExamples{
     498        6174 :             HelpExampleCli("getblockfrompeer", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" 0")
     499        6174 :             + HelpExampleRpc("getblockfrompeer", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" 0")
     500             :         },
     501        6192 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     502             : {
     503          66 :     RPCTypeCheck(request.params, {
     504          18 :         UniValue::VSTR, // blockhash
     505          18 :         UniValue::VNUM, // peer_id
     506             :     });
     507             : 
     508          14 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     509          14 :     ChainstateManager& chainman = EnsureChainman(node);
     510          14 :     PeerManager& peerman = EnsurePeerman(node);
     511             : 
     512          14 :     const uint256& block_hash{ParseHashV(request.params[0], "blockhash")};
     513          12 :     const NodeId peer_id{request.params[1].getInt<int64_t>()};
     514             : 
     515          24 :     const CBlockIndex* const index = WITH_LOCK(cs_main, return chainman.m_blockman.LookupBlockIndex(block_hash););
     516             : 
     517          12 :     if (!index) {
     518           2 :         throw JSONRPCError(RPC_MISC_ERROR, "Block header missing");
     519             :     }
     520             : 
     521             :     // Fetching blocks before the node has syncing past their height can prevent block files from
     522             :     // being pruned, so we avoid it if the node is in prune mode.
     523          10 :     if (index->nHeight > chainman.ActiveChain().Tip()->nHeight && node::fPruneMode) {
     524           2 :         throw JSONRPCError(RPC_MISC_ERROR, "In prune mode, only blocks that the node has already synced previously can be fetched from a peer");
     525             :     }
     526             : 
     527          16 :     const bool block_has_data = WITH_LOCK(::cs_main, return index->nStatus & BLOCK_HAVE_DATA);
     528           8 :     if (block_has_data) {
     529           2 :         throw JSONRPCError(RPC_MISC_ERROR, "Block already downloaded");
     530             :     }
     531          10 :     if (const auto err{peerman.FetchBlock(peer_id, *index)}) {
     532           4 :         throw JSONRPCError(RPC_MISC_ERROR, err.value());
     533             :     }
     534           2 :     return UniValue::VOBJ;
     535          16 : },
     536             :     };
     537           0 : }
     538             : 
     539        6160 : static RPCHelpMan getblockhashes()
     540             : {
     541       12320 :     return RPCHelpMan{"getblockhashes",
     542        6160 :         "\nReturns array of hashes of blocks within the timestamp range provided.\n",
     543       18480 :         {
     544        6160 :             {"high", RPCArg::Type::NUM, RPCArg::Optional::NO, "The newer block timestamp"},
     545        6160 :             {"low", RPCArg::Type::NUM, RPCArg::Optional::NO, "The older block timestamp"},
     546             :         },
     547        6160 :         RPCResult{
     548        6160 :             RPCResult::Type::ARR, "", "",
     549        6160 :             {{RPCResult::Type::STR_HEX, "", "The block hash"}}},
     550        6160 :         RPCExamples{
     551        6160 :             HelpExampleCli("getblockhashes", "1231614698 1231024505")
     552        6160 :             + HelpExampleRpc("getblockhashes", "1231614698, 1231024505")
     553             :         },
     554        6164 :     [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     555             : {
     556           4 :     if (!g_timestampindex) {
     557           0 :         throw JSONRPCError(RPC_MISC_ERROR, "Timestamp index is not enabled. Start with -timestampindex to enable.");
     558             :     }
     559             : 
     560           4 :     if (!g_timestampindex->BlockUntilSyncedToCurrentChain()) {
     561           0 :         throw JSONRPCError(RPC_MISC_ERROR, strprintf("Timestamp index is syncing. Current height: %d", g_timestampindex->GetSummary().best_block_height));
     562             :     }
     563             : 
     564           4 :     unsigned int high = request.params[0].getInt<int>();
     565           4 :     unsigned int low = request.params[1].getInt<int>();
     566           4 :     std::vector<uint256> blockHashes;
     567             : 
     568           4 :     if (!g_timestampindex->GetBlockHashes(high, low, blockHashes)) {
     569           0 :         throw JSONRPCError(RPC_MISC_ERROR, "Failed to read timestamp index.");
     570             :     }
     571             : 
     572           4 :     UniValue result(UniValue::VARR);
     573          22 :     for (const auto& hash : blockHashes) {
     574          18 :         result.push_back(hash.GetHex());
     575             :     }
     576             : 
     577           4 :     return result;
     578           4 : },
     579             :     };
     580           0 : }
     581             : 
     582       14878 : static RPCHelpMan getblockhash()
     583             : {
     584       29756 :     return RPCHelpMan{"getblockhash",
     585       14878 :                 "\nReturns hash of block in best-block-chain at height provided.\n",
     586       29756 :                 {
     587       14878 :                     {"height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The height index"},
     588             :                 },
     589       14878 :                 RPCResult{
     590       14878 :                     RPCResult::Type::STR_HEX, "", "The block hash"},
     591       14878 :                 RPCExamples{
     592       14878 :                     HelpExampleCli("getblockhash", "1000")
     593       14878 :             + HelpExampleRpc("getblockhash", "1000")
     594             :                 },
     595       23600 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     596             : {
     597        8722 :     ChainstateManager& chainman = EnsureAnyChainman(request.context);
     598        8722 :     LOCK(cs_main);
     599        8722 :     const CChain& active_chain = chainman.ActiveChain();
     600             : 
     601        8722 :     int nHeight = request.params[0].getInt<int>();
     602        8722 :     if (nHeight < 0 || nHeight > active_chain.Height())
     603           2 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range");
     604             : 
     605        8720 :     const CBlockIndex* pblockindex = active_chain[nHeight];
     606        8720 :     return pblockindex->GetBlockHash().GetHex();
     607        8724 : },
     608             :     };
     609           0 : }
     610             : 
     611       12953 : static RPCHelpMan getblockheader()
     612             : {
     613       25906 :     return RPCHelpMan{"getblockheader",
     614       12953 :                 "\nIf verbose is false, returns a string that is serialized, hex-encoded data for blockheader 'hash'.\n"
     615             :                 "If verbose is true, returns an Object with information about blockheader <hash>.\n",
     616       38859 :                 {
     617       12953 :                     {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
     618       12953 :                     {"verbose", RPCArg::Type::BOOL, RPCArg::Default{true}, "true for a json object, false for the hex-encoded data"},
     619             :                 },
     620       38859 :                 {
     621       25906 :                     RPCResult{"for verbose = true",
     622       12953 :                         RPCResult::Type::OBJ, "", "",
     623      220201 :                         {
     624       12953 :                             {RPCResult::Type::STR_HEX, "hash", "the block hash (same as provided)"},
     625       12953 :                             {RPCResult::Type::NUM, "confirmations", "The number of confirmations, or -1 if the block is not on the main chain"},
     626       12953 :                             {RPCResult::Type::NUM, "height", "The block height or index"},
     627       12953 :                             {RPCResult::Type::NUM, "version", "The block version"},
     628       12953 :                             {RPCResult::Type::STR_HEX, "versionHex", "The block version formatted in hexadecimal"},
     629       12953 :                             {RPCResult::Type::STR_HEX, "merkleroot", "The merkle root"},
     630       12953 :                             {RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME},
     631       12953 :                             {RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME},
     632       12953 :                             {RPCResult::Type::NUM, "nonce", "The nonce"},
     633       12953 :                             {RPCResult::Type::STR_HEX, "bits", "The bits"},
     634       12953 :                             {RPCResult::Type::NUM, "difficulty", "The difficulty"},
     635       12953 :                             {RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the current chain"},
     636       12953 :                             {RPCResult::Type::NUM, "nTx", "The number of transactions in the block"},
     637       12953 :                             {RPCResult::Type::STR_HEX, "previousblockhash", /*optional=*/true, "The hash of the previous block (if available)"},
     638       12953 :                             {RPCResult::Type::STR_HEX, "nextblockhash", /*optional=*/true, "The hash of the next block (if available)"},
     639       12953 :                             {RPCResult::Type::BOOL, "chainlock", "The state of the block ChainLock"},
     640             :                         }},
     641       25906 :                     RPCResult{"for verbose=false",
     642       12953 :                         RPCResult::Type::STR_HEX, "", "A string that is serialized, hex-encoded data for block 'hash'"},
     643             :                 },
     644       12953 :                 RPCExamples{
     645       12953 :                     HelpExampleCli("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
     646       12953 :             + HelpExampleRpc("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
     647             :                 },
     648       19750 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     649             : {
     650        6803 :     uint256 hash(ParseHashV(request.params[0], "hash"));
     651        6789 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     652             : 
     653        6789 :     bool fVerbose = true;
     654        6789 :     if (!request.params[1].isNull())
     655         125 :         fVerbose = request.params[1].get_bool();
     656             : 
     657             :     const CBlockIndex* pblockindex;
     658             :     const CBlockIndex* tip;
     659             :     {
     660        6789 :         ChainstateManager& chainman = EnsureChainman(node);
     661        6789 :         LOCK(cs_main);
     662        6789 :         pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
     663        6789 :         tip = chainman.ActiveChain().Tip();
     664        6789 :     }
     665             : 
     666        6789 :     if (!pblockindex) {
     667           6 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
     668             :     }
     669             : 
     670        6783 :     if (!fVerbose)
     671             :     {
     672          73 :         CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
     673          73 :         ssBlock << pblockindex->GetBlockHeader();
     674          73 :         std::string strHex = HexStr(ssBlock);
     675          73 :         return strHex;
     676          73 :     }
     677             : 
     678        6710 :     CHECK_NONFATAL(node.chainlocks);
     679        6710 :     return blockheaderToJSON(tip, pblockindex, *node.chainlocks);
     680        6797 : },
     681             :     };
     682           0 : }
     683             : 
     684        6176 : static RPCHelpMan getblockheaders()
     685             : {
     686       12352 :     return RPCHelpMan{"getblockheaders",
     687        6176 :         "\nReturns an array of items with information about <count> blockheaders starting from <hash>.\n"
     688             :         "\nIf verbose is false, each item is a string that is serialized, hex-encoded data for a single blockheader.\n"
     689             :         "If verbose is true, each item is an Object with information about a single blockheader.\n",
     690       24704 :         {
     691        6176 :             {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
     692        6176 :             {"count", RPCArg::Type::NUM, RPCArg::Default{int{MAX_HEADERS_UNCOMPRESSED_RESULT}}, ""},
     693        6176 :             {"verbose", RPCArg::Type::BOOL, RPCArg::Default{true}, "true for a json object, false for the hex-encoded data"},
     694             :         },
     695       18528 :         {
     696       12352 :             RPCResult{"for verbose = true",
     697        6176 :                 RPCResult::Type::ARR, "", "",
     698       12352 :                     {{RPCResult::Type::OBJ, "", "",
     699      104992 :                     {
     700        6176 :                         {RPCResult::Type::STR_HEX, "hash", "The block hash (same as provided)"},
     701        6176 :                         {RPCResult::Type::NUM, "confirmations", "The number of confirmations, or -1 if the block is not on the main chain"},
     702        6176 :                         {RPCResult::Type::NUM, "height", "The block height or index"},
     703        6176 :                         {RPCResult::Type::NUM, "version", "The block version"},
     704        6176 :                         {RPCResult::Type::STR_HEX, "versionHex", "The block version formatted in hexadecimal"},
     705        6176 :                         {RPCResult::Type::STR_HEX, "merkleroot", "The merkle root"},
     706        6176 :                         {RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME},
     707        6176 :                         {RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME},
     708        6176 :                         {RPCResult::Type::NUM, "nonce", "The nonce"},
     709        6176 :                         {RPCResult::Type::STR_HEX, "bits", "The bits"},
     710        6176 :                         {RPCResult::Type::NUM, "difficulty", "The difficulty"},
     711        6176 :                         {RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the current chain"},
     712        6176 :                         {RPCResult::Type::NUM, "nTx", "The number of transactions in the block"},
     713        6176 :                         {RPCResult::Type::STR_HEX, "previousblockhash", /*optional=*/true, "The hash of the previous block (if available)"},
     714        6176 :                         {RPCResult::Type::STR_HEX, "nextblockhash", /*optional=*/true, "The hash of the next block (if available)"},
     715        6176 :                         {RPCResult::Type::BOOL, "chainlock", "The state of the block ChainLock"},
     716             :                     }},
     717             :                 }},
     718       12352 :             RPCResult{"for verbose=false",
     719        6176 :                 RPCResult::Type::ARR, "", "",
     720        6176 :                     {{RPCResult::Type::STR_HEX, "", "A string that is serialized, hex-encoded data for block 'hash'"}}},
     721             :         },
     722        6176 :         RPCExamples{
     723        6176 :             HelpExampleCli("getblockheaders", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" 2000")
     724        6176 :     + HelpExampleRpc("getblockheaders", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" 2000")
     725             :         },
     726        6196 :     [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     727             : {
     728             : 
     729          24 :     uint256 hash(ParseHashV(request.params[0], "blockhash"));
     730             : 
     731          12 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     732             : 
     733          12 :     ChainstateManager& chainman = EnsureChainman(node);
     734             : 
     735          12 :     CChainState& active_chainstate = chainman.ActiveChainstate();
     736          12 :     CChain& active_chain = active_chainstate.m_chain;
     737             : 
     738             :     const CBlockIndex* pblockindex;
     739             :     const CBlockIndex* tip;
     740             :     {
     741          12 :         LOCK(cs_main);
     742          12 :         pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
     743          12 :         tip = active_chain.Tip();
     744          12 :     }
     745             : 
     746          12 :     if (!pblockindex) {
     747           4 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
     748             :     }
     749             : 
     750           8 :     int nCount = MAX_HEADERS_UNCOMPRESSED_RESULT;
     751           8 :     if (!request.params[1].isNull())
     752           0 :         nCount = request.params[1].getInt<int>();
     753             : 
     754           8 :     if (nCount <= 0 || nCount > (int)MAX_HEADERS_UNCOMPRESSED_RESULT)
     755           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Count is out of range");
     756             : 
     757           8 :     bool fVerbose = true;
     758           8 :     if (!request.params[2].isNull())
     759           4 :         fVerbose = request.params[2].get_bool();
     760             : 
     761           8 :     UniValue arrHeaders(UniValue::VARR);
     762             : 
     763           8 :     if (!fVerbose)
     764             :     {
     765           8 :         for (; pblockindex; pblockindex = active_chain.Next(pblockindex))
     766             :         {
     767           4 :             CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
     768           4 :             ssBlock << pblockindex->GetBlockHeader();
     769           4 :             std::string strHex = HexStr(ssBlock);
     770           4 :             arrHeaders.push_back(strHex);
     771           4 :             if (--nCount <= 0)
     772           0 :                 break;
     773           4 :         }
     774           4 :         return arrHeaders;
     775             :     }
     776             : 
     777           4 :     CHECK_NONFATAL(node.chainlocks);
     778           8 :     for (; pblockindex; pblockindex = active_chain.Next(pblockindex))
     779             :     {
     780           4 :         arrHeaders.push_back(blockheaderToJSON(tip, pblockindex, *node.chainlocks));
     781           4 :         if (--nCount <= 0)
     782           0 :             break;
     783           4 :     }
     784             : 
     785           4 :     return arrHeaders;
     786          20 : },
     787             :     };
     788           0 : }
     789             : 
     790        5129 : static CBlock GetBlockChecked(BlockManager& blockman, const CBlockIndex* pblockindex)
     791             : {
     792        5129 :     CBlock block;
     793             :     {
     794        5129 :         LOCK(cs_main);
     795        5129 :         if (blockman.IsBlockPruned(pblockindex)) {
     796           0 :             throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)");
     797             :         }
     798        5129 :     }
     799             : 
     800        5129 :     if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) {
     801             :         // Block not found on disk. This could be because we have the block
     802             :         // header in our index but not yet have the block or did not accept the
     803             :         // block. Or if the block was pruned right after we released the lock above.
     804          31 :         throw JSONRPCError(RPC_MISC_ERROR, "Block not found on disk");
     805             :     }
     806             : 
     807        5098 :     return block;
     808        5160 : }
     809             : 
     810         193 : static CBlockUndo GetUndoChecked(BlockManager& blockman, const CBlockIndex* pblockindex)
     811             : {
     812         193 :     CBlockUndo blockUndo;
     813             : 
     814             :     // The Genesis block does not have undo data
     815         193 :     if (pblockindex->nHeight == 0) return blockUndo;
     816             : 
     817             :     {
     818         191 :         LOCK(cs_main);
     819         191 :         if (blockman.IsBlockPruned(pblockindex)) {
     820           0 :             throw JSONRPCError(RPC_MISC_ERROR, "Undo data not available (pruned data)");
     821             :         }
     822         191 :     }
     823             : 
     824         191 :     if (!UndoReadFromDisk(blockUndo, pblockindex)) {
     825           0 :         throw JSONRPCError(RPC_MISC_ERROR, "Can't read undo data from disk");
     826             :     }
     827             : 
     828         191 :     return blockUndo;
     829         193 : }
     830             : 
     831        6156 : static RPCHelpMan getmerkleblocks()
     832             : {
     833       12312 :     return RPCHelpMan{"getmerkleblocks",
     834        6156 :         "\nReturns an array of hex-encoded merkleblocks for <count> blocks starting from <hash> which match <filter>.\n",
     835       24624 :         {
     836        6156 :             {"filter", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex-encoded bloom filter"},
     837        6156 :             {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
     838        6156 :             {"count", RPCArg::Type::NUM, RPCArg::Default{int{MAX_HEADERS_UNCOMPRESSED_RESULT}}, ""},
     839             :         },
     840        6156 :         RPCResult{
     841        6156 :                 RPCResult::Type::ARR, "", "",
     842        6156 :                     {{RPCResult::Type::STR_HEX, "", "A string that is serialized, hex-encoded data for a merkleblock"}}},
     843        6156 :         RPCExamples{
     844        6156 :             HelpExampleCli("getmerkleblocks", "\"2303028005802040100040000008008400048141010000f8400420800080025004000004130000000000000001\" \"00000000007e1432d2af52e8463278bf556b55cf5049262f25634557e2e91202\" 2000")
     845        6156 :     + HelpExampleRpc("getmerkleblocks", "\"2303028005802040100040000008008400048141010000f8400420800080025004000004130000000000000001\" \"00000000007e1432d2af52e8463278bf556b55cf5049262f25634557e2e91202\" 2000")
     846             :         },
     847        6156 :     [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     848             : {
     849           0 :     ChainstateManager& chainman = EnsureAnyChainman(request.context);
     850           0 :     LOCK(cs_main);
     851             : 
     852           0 :     CBloomFilter filter;
     853           0 :     std::string strFilter = request.params[0].get_str();
     854           0 :     CDataStream ssBloomFilter(ParseHex(strFilter), SER_NETWORK, PROTOCOL_VERSION);
     855           0 :     ssBloomFilter >> filter;
     856           0 :     if (!filter.IsWithinSizeConstraints()) {
     857           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Filter is not within size constraints");
     858             :     }
     859             : 
     860           0 :     uint256 hash(ParseHashV(request.params[1], "blockhash"));
     861             : 
     862           0 :     const CBlockIndex* pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
     863           0 :     if (!pblockindex) {
     864           0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
     865             :     }
     866             : 
     867           0 :     int nCount = MAX_HEADERS_UNCOMPRESSED_RESULT;
     868           0 :     if (!request.params[2].isNull())
     869           0 :         nCount = request.params[2].getInt<int>();
     870             : 
     871           0 :     if (nCount <= 0 || nCount > (int)MAX_HEADERS_UNCOMPRESSED_RESULT) {
     872           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Count is out of range");
     873             :     }
     874             : 
     875           0 :     CBlock block = GetBlockChecked(chainman.m_blockman, pblockindex);
     876             : 
     877           0 :     UniValue arrMerkleBlocks(UniValue::VARR);
     878             : 
     879           0 :     for (; pblockindex; pblockindex = chainman.ActiveChain().Next(pblockindex))
     880             :     {
     881           0 :         if (--nCount < 0) {
     882           0 :             break;
     883             :         }
     884             : 
     885           0 :         if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) {
     886             :             // this shouldn't happen, we already checked pruning case earlier
     887           0 :             throw JSONRPCError(RPC_MISC_ERROR, "Block not found on disk");
     888             :         }
     889             : 
     890           0 :         CMerkleBlock merkleblock(block, filter);
     891           0 :         if (merkleblock.vMatchedTxn.empty()) {
     892             :             // ignore blocks that do not match the filter
     893           0 :             continue;
     894             :         }
     895             : 
     896           0 :         CDataStream ssMerkleBlock(SER_NETWORK, PROTOCOL_VERSION);
     897           0 :         ssMerkleBlock << merkleblock;
     898           0 :         std::string strHex = HexStr(ssMerkleBlock);
     899           0 :         arrMerkleBlocks.push_back(strHex);
     900           0 :     }
     901           0 :     return arrMerkleBlocks;
     902           0 : },
     903             :     };
     904           0 : }
     905             : 
     906        3308 : const RPCResult getblock_vin{
     907        3308 :     RPCResult::Type::ARR, "vin", "",
     908        6616 :     {
     909        6616 :         {RPCResult::Type::OBJ, "", "",
     910        9924 :         {
     911        3308 :             {RPCResult::Type::ELISION, "", "The same output as verbosity = 2"},
     912        6616 :             {RPCResult::Type::OBJ, "prevout", "(Only if undo information is available)",
     913       16540 :             {
     914        3308 :                 {RPCResult::Type::BOOL, "generated", "Coinbase or not"},
     915        3308 :                 {RPCResult::Type::NUM, "height", "The height of the prevout"},
     916        3308 :                 {RPCResult::Type::STR_AMOUNT, "value", "The value in " + CURRENCY_UNIT},
     917        6616 :                 {RPCResult::Type::OBJ, "scriptPubKey", "",
     918       19848 :                 {
     919        3308 :                     {RPCResult::Type::STR, "asm", "Disassembly of the public key script"},
     920        3308 :                     {RPCResult::Type::STR, "desc", "Inferred descriptor for the output"},
     921        3308 :                     {RPCResult::Type::STR_HEX, "hex", "The raw public key script bytes, hex-encoded"},
     922        3308 :                     {RPCResult::Type::STR, "address", /*optional=*/true, "The Dash address (only if a well-defined address exists)"},
     923        3308 :                     {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"},
     924             :                 }},
     925             :             }},
     926             :         }},
     927             :     }
     928             : };
     929             : 
     930       11139 : static RPCHelpMan getblock()
     931             : {
     932       22278 :     return RPCHelpMan{"getblock",
     933       11139 :                 "\nIf verbosity is 0, returns a string that is serialized, hex-encoded data for block 'hash'.\n"
     934             :                 "If verbosity is 1, returns an Object with information about block <hash>.\n"
     935             :                 "If verbosity is 2, returns an Object with information about block <hash> and information about each transaction.\n"
     936             :                 "If verbosity is 3, returns an Object with information about block <hash> and information about each transaction, including prevout information for inputs (only for unpruned blocks in the current best chain).\n",
     937       33417 :                 {
     938       11139 :                     {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
     939       11139 :                     {"verbosity|verbose", RPCArg::Type::NUM, RPCArg::Default{1}, "0 for hex-encoded data, 1 for a JSON object, 2 for JSON object with transaction data, and 3 for JSON object with transaction data including prevout information for inputs"},
     940             :                 },
     941       55695 :                 {
     942       22278 :                     RPCResult{"for verbosity = 0",
     943       11139 :                 RPCResult::Type::STR_HEX, "", "A string that is serialized, hex-encoded data for block 'hash'"},
     944       22278 :                     RPCResult{"for verbosity = 1",
     945       11139 :                 RPCResult::Type::OBJ, "", "",
     946      222780 :                 {
     947       11139 :                     {RPCResult::Type::STR_HEX, "hash", "the block hash (same as provided)"},
     948       11139 :                     {RPCResult::Type::NUM, "confirmations", "The number of confirmations, or -1 if the block is not on the main chain"},
     949       11139 :                     {RPCResult::Type::NUM, "height", "The block height or index"},
     950       11139 :                     {RPCResult::Type::NUM, "version", "The block version"},
     951       11139 :                     {RPCResult::Type::STR_HEX, "versionHex", "The block version formatted in hexadecimal"},
     952       11139 :                     {RPCResult::Type::STR_HEX, "merkleroot", "The merkle root"},
     953       11139 :                     {RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME},
     954       11139 :                     {RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME},
     955       11139 :                     {RPCResult::Type::NUM, "nonce", "The nonce"},
     956       11139 :                     {RPCResult::Type::STR_HEX, "bits", "The bits"},
     957       11139 :                     {RPCResult::Type::NUM, "difficulty", "The difficulty"},
     958       11139 :                     {RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the current chain"},
     959       11139 :                     {RPCResult::Type::NUM, "nTx", "The number of transactions in the block"},
     960       11139 :                     {RPCResult::Type::STR_HEX, "previousblockhash", /*optional=*/true, "The hash of the previous block (if available)"},
     961       11139 :                     {RPCResult::Type::STR_HEX, "nextblockhash", /*optional=*/true, "The hash of the next block (if available)"},
     962       11139 :                     {RPCResult::Type::BOOL, "chainlock", "The state of the block ChainLock"},
     963       11139 :                     {RPCResult::Type::NUM, "size", "The block size"},
     964       22278 :                     {RPCResult::Type::ARR, "tx", "The transaction ids",
     965       11139 :                         {{RPCResult::Type::STR_HEX, "", "The transaction id"}}},
     966       11139 :                     CCbTx::GetJsonHelp(/*key=*/"cbTx", /*optional=*/true),
     967             :                 }},
     968       22278 :                     RPCResult{"for verbosity = 2",
     969       11139 :                 RPCResult::Type::OBJ, "", "",
     970       33417 :                 {
     971       11139 :                     {RPCResult::Type::ELISION, "", "Same output as verbosity = 1"},
     972       22278 :                     {RPCResult::Type::ARR, "tx", "",
     973       22278 :                     {
     974       22278 :                         {RPCResult::Type::OBJ, "", "",
     975       33417 :                         {
     976       11139 :                             {RPCResult::Type::ELISION, "", "The transactions in the format of the getrawtransaction RPC. Different from verbosity = 1 \"tx\" result"},
     977       11139 :                             {RPCResult::Type::NUM, "fee", "The transaction fee in " + CURRENCY_UNIT + ", omitted if block undo data is not available"},
     978             :                         }},
     979             :                     }},
     980             :                 }},
     981       22278 :                     RPCResult{"for verbosity = 3",
     982       11139 :                 RPCResult::Type::OBJ, "", "",
     983       33417 :                 {
     984       11139 :                     {RPCResult::Type::ELISION, "", "Same output as verbosity = 2"},
     985       22278 :                     {RPCResult::Type::ARR, "tx", "",
     986       22278 :                     {
     987       22278 :                         {RPCResult::Type::OBJ, "", "",
     988       11139 :                         {
     989       11139 :                             getblock_vin,
     990             :                         }},
     991             :                     }},
     992             :                 }},
     993             :         },
     994       11139 :                 RPCExamples{
     995       11139 :                     HelpExampleCli("getblock", "\"00000000000fd08c2fb661d2fcb0d49abb3a91e5f27082ce64feed3b4dede2e2\"")
     996       11139 :             + HelpExampleRpc("getblock", "\"00000000000fd08c2fb661d2fcb0d49abb3a91e5f27082ce64feed3b4dede2e2\"")
     997             :                 },
     998       16149 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     999             : {
    1000        5059 :     uint256 hash(ParseHashV(request.params[0], "blockhash"));
    1001             : 
    1002        5010 :     int verbosity = 1;
    1003        5010 :     if (!request.params[1].isNull()) {
    1004         955 :         if (request.params[1].isBool()) {
    1005         423 :             verbosity = request.params[1].get_bool() ? 1 : 0;
    1006         423 :         } else {
    1007         532 :             verbosity = request.params[1].getInt<int>();
    1008             :         }
    1009         955 :     }
    1010             : 
    1011        5010 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
    1012             : 
    1013             :     const CBlockIndex* pblockindex;
    1014             :     const CBlockIndex* tip;
    1015        5010 :     ChainstateManager& chainman = EnsureChainman(node);
    1016             :     {
    1017        5010 :         LOCK(cs_main);
    1018        5010 :         pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
    1019        4979 :         tip = chainman.ActiveChain().Tip();
    1020             : 
    1021        4948 :         if (!pblockindex) {
    1022          49 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
    1023             :         }
    1024        4948 :     }
    1025             : 
    1026        4899 :     const CBlock block{GetBlockChecked(chainman.m_blockman, pblockindex)};
    1027             : 
    1028        4899 :     if (verbosity <= 0)
    1029             :     {
    1030         513 :         CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
    1031         513 :         ssBlock << block;
    1032         513 :         std::string strHex = HexStr(ssBlock);
    1033         513 :         return strHex;
    1034         513 :     }
    1035             : 
    1036        4386 :     const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
    1037        4386 :     CHECK_NONFATAL(node.chainlocks);
    1038             :     TxVerbosity tx_verbosity;
    1039        4386 :     if (verbosity == 1) {
    1040        4102 :         tx_verbosity = TxVerbosity::SHOW_TXID;
    1041        4386 :     } else if (verbosity == 2) {
    1042         268 :         tx_verbosity = TxVerbosity::SHOW_DETAILS;
    1043         268 :     } else {
    1044          16 :         tx_verbosity = TxVerbosity::SHOW_DETAILS_AND_PREVOUT;
    1045             :     }
    1046             : 
    1047        4386 :     return blockToJSON(chainman.m_blockman, block, tip, pblockindex, *node.chainlocks, *llmq_ctx.isman, tx_verbosity);
    1048        5010 : },
    1049             :     };
    1050           0 : }
    1051             : 
    1052        6156 : static RPCHelpMan pruneblockchain()
    1053             : {
    1054       12312 :     return RPCHelpMan{"pruneblockchain", "",
    1055       12312 :                 {
    1056        6156 :                     {"height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The block height to prune up to. May be set to a discrete height, or to a " + UNIX_EPOCH_TIME + "\n"
    1057             :             "                  to prune blocks whose block time is at least 2 hours older than the provided timestamp."},
    1058             :                 },
    1059        6156 :                 RPCResult{
    1060        6156 :                     RPCResult::Type::NUM, "", "Height of the last block pruned"},
    1061        6156 :                 RPCExamples{
    1062        6156 :                     HelpExampleCli("pruneblockchain", "1000")
    1063        6156 :             + HelpExampleRpc("pruneblockchain", "1000")
    1064             :                 },
    1065        6156 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    1066             : {
    1067           0 :     if (!node::fPruneMode)
    1068           0 :         throw JSONRPCError(RPC_MISC_ERROR, "Cannot prune blocks because node is not in prune mode.");
    1069             : 
    1070           0 :     ChainstateManager& chainman = EnsureAnyChainman(request.context);
    1071           0 :     LOCK(cs_main);
    1072           0 :     CChainState& active_chainstate = chainman.ActiveChainstate();
    1073           0 :     CChain& active_chain = active_chainstate.m_chain;
    1074             : 
    1075           0 :     int heightParam = request.params[0].getInt<int>();
    1076           0 :     if (heightParam < 0) {
    1077           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative block height.");
    1078             :     }
    1079             : 
    1080             :     // Height value more than a billion is too high to be a block height, and
    1081             :     // too low to be a block time (corresponds to timestamp from Sep 2001).
    1082           0 :     if (heightParam > 1000000000) {
    1083             :         // Add a 2 hour buffer to include blocks which might have had old timestamps
    1084           0 :         const CBlockIndex* pindex = active_chain.FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW, 0);
    1085           0 :         if (!pindex) {
    1086           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Could not find block with at least the specified timestamp.");
    1087             :         }
    1088           0 :         heightParam = pindex->nHeight;
    1089           0 :     }
    1090             : 
    1091           0 :     unsigned int height = (unsigned int) heightParam;
    1092           0 :     unsigned int chainHeight = (unsigned int) active_chain.Height();
    1093           0 :     if (chainHeight < Params().PruneAfterHeight())
    1094           0 :         throw JSONRPCError(RPC_MISC_ERROR, "Blockchain is too short for pruning.");
    1095           0 :     else if (height > chainHeight)
    1096           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Blockchain is shorter than the attempted prune height.");
    1097           0 :     else if (height > chainHeight - MIN_BLOCKS_TO_KEEP) {
    1098           0 :         LogPrint(BCLog::RPC, "Attempt to prune blocks close to the tip.  Retaining the minimum number of blocks.\n");
    1099           0 :         height = chainHeight - MIN_BLOCKS_TO_KEEP;
    1100           0 :     }
    1101             : 
    1102           0 :     PruneBlockFilesManual(active_chainstate, height);
    1103           0 :     const CBlockIndex& block{*CHECK_NONFATAL(active_chain.Tip())};
    1104           0 :     const CBlockIndex* last_block{active_chainstate.m_blockman.GetFirstStoredBlock(block)};
    1105             : 
    1106           0 :     return static_cast<int64_t>(last_block->nHeight - 1);
    1107           0 : },
    1108             :     };
    1109           0 : }
    1110             : 
    1111          88 : CoinStatsHashType ParseHashType(const std::string& hash_type_input)
    1112             : {
    1113          88 :     if (hash_type_input == "hash_serialized_2") {
    1114          12 :         return CoinStatsHashType::HASH_SERIALIZED;
    1115          76 :     } else if (hash_type_input == "muhash") {
    1116          48 :         return CoinStatsHashType::MUHASH;
    1117          28 :     } else if (hash_type_input == "none") {
    1118          24 :         return CoinStatsHashType::NONE;
    1119             :     } else {
    1120           4 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("'%s' is not a valid hash_type", hash_type_input));
    1121             :     }
    1122          88 : }
    1123             : 
    1124         196 : std::optional<kernel::CCoinsStats> GetUTXOStats(CCoinsView* view, node::BlockManager& blockman,
    1125             :                                                 kernel::CoinStatsHashType hash_type,
    1126             :                                                 const std::function<void()>& interruption_point,
    1127             :                                                 const CBlockIndex* pindex,
    1128             :                                                 bool index_requested)
    1129             : {
    1130             :     // Use CoinStatsIndex if it is requested and available and a hash_type of Muhash or None was requested
    1131         196 :     if ((hash_type == kernel::CoinStatsHashType::MUHASH || hash_type == kernel::CoinStatsHashType::NONE) && g_coin_stats_index && index_requested) {
    1132          88 :         if (pindex) {
    1133          88 :             return g_coin_stats_index->LookUpStats(pindex);
    1134             :         } else {
    1135           0 :             CBlockIndex* block_index = WITH_LOCK(::cs_main, return blockman.LookupBlockIndex(view->GetBestBlock()));
    1136           0 :             return g_coin_stats_index->LookUpStats(block_index);
    1137             :         }
    1138             :     }
    1139             : 
    1140             :     // If the coinstats index isn't requested or is otherwise not usable, the
    1141             :     // pindex should either be null or equal to the view's best block. This is
    1142             :     // because without the coinstats index we can only get coinstats about the
    1143             :     // best block.
    1144         108 :     CHECK_NONFATAL(!pindex || pindex->GetBlockHash() == view->GetBestBlock());
    1145             : 
    1146         108 :     return kernel::ComputeUTXOStats(hash_type, view, blockman, interruption_point);
    1147         196 : }
    1148             : 
    1149        6317 : static RPCHelpMan gettxoutsetinfo()
    1150             : {
    1151       12634 :     return RPCHelpMan{"gettxoutsetinfo",
    1152        6317 :                 "\nReturns statistics about the unspent transaction output set.\n"
    1153             :                 "Note this call may take some time if you are not using coinstatsindex.\n",
    1154       25268 :                 {
    1155        6317 :                     {"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'."},
    1156        6317 :                     {"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"}},
    1157        6317 :                     {"use_index", RPCArg::Type::BOOL, RPCArg::Default{true}, "Use coinstatsindex, if available."},
    1158             :                 },
    1159        6317 :                 RPCResult{
    1160        6317 :                     RPCResult::Type::OBJ, "", "",
    1161       75804 :                     {
    1162        6317 :                         {RPCResult::Type::NUM, "height", "The block height (index) of the returned statistics"},
    1163        6317 :                         {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at which these statistics are calculated"},
    1164        6317 :                         {RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs"},
    1165        6317 :                         {RPCResult::Type::NUM, "bogosize", "Database-independent, meaningless metric indicating the UTXO set size"},
    1166        6317 :                         {RPCResult::Type::STR_HEX, "hash_serialized_2", /*optional=*/true, "The serialized hash (only present if 'hash_serialized_2' hash_type is chosen)"},
    1167        6317 :                         {RPCResult::Type::STR_HEX, "muhash", /*optional=*/true, "The serialized hash (only present if 'muhash' hash_type is chosen)"},
    1168        6317 :                         {RPCResult::Type::NUM, "transactions", /*optional=*/true, "The number of transactions with unspent outputs (not available when coinstatsindex is used)"},
    1169        6317 :                         {RPCResult::Type::NUM, "disk_size", /*optional=*/true, "The estimated size of the chainstate on disk (not available when coinstatsindex is used)"},
    1170        6317 :                         {RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount of coins in the UTXO set"},
    1171        6317 :                         {RPCResult::Type::STR_AMOUNT, "total_unspendable_amount", /*optional=*/true, "The total amount of coins permanently excluded from the UTXO set (only available if coinstatsindex is used)"},
    1172       12634 :                         {RPCResult::Type::OBJ, "block_info", /*optional=*/true, "Info on amounts in the block at this block height (only available if coinstatsindex is used)",
    1173       37902 :                         {
    1174        6317 :                             {RPCResult::Type::STR_AMOUNT, "prevout_spent", "Total amount of all prevouts spent in this block"},
    1175        6317 :                             {RPCResult::Type::STR_AMOUNT, "coinbase", "Coinbase subsidy amount of this block"},
    1176        6317 :                             {RPCResult::Type::STR_AMOUNT, "new_outputs_ex_coinbase", "Total amount of new outputs created by this block"},
    1177        6317 :                             {RPCResult::Type::STR_AMOUNT, "unspendable", "Total amount of unspendable outputs created in this block"},
    1178       12634 :                             {RPCResult::Type::OBJ, "unspendables", "Detailed view of the unspendable categories",
    1179       31585 :                             {
    1180        6317 :                                 {RPCResult::Type::STR_AMOUNT, "genesis_block", "The unspendable amount of the Genesis block subsidy"},
    1181        6317 :                                 {RPCResult::Type::STR_AMOUNT, "bip30", "Transactions overridden by duplicates (no longer possible with BIP30)"},
    1182        6317 :                                 {RPCResult::Type::STR_AMOUNT, "scripts", "Amounts sent to scripts that are unspendable (for example OP_RETURN outputs)"},
    1183        6317 :                                 {RPCResult::Type::STR_AMOUNT, "unclaimed_rewards", "Fee rewards that miners did not claim in their coinbase transaction"},
    1184             :                             }}
    1185             :                         }},
    1186             :                     }},
    1187        6317 :                 RPCExamples{
    1188       12634 :                     HelpExampleCli("gettxoutsetinfo", "") +
    1189       12634 :                     HelpExampleCli("gettxoutsetinfo", R"("none")") +
    1190       12634 :                     HelpExampleCli("gettxoutsetinfo", R"("none" 1000)") +
    1191       12634 :                     HelpExampleCli("gettxoutsetinfo", R"("none" '"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09"')") +
    1192       12634 :                     HelpExampleCli("-named gettxoutsetinfo", R"(hash_type='muhash' use_index='false')") +
    1193       12634 :                     HelpExampleRpc("gettxoutsetinfo", "") +
    1194       12634 :                     HelpExampleRpc("gettxoutsetinfo", R"("none")") +
    1195       12634 :                     HelpExampleRpc("gettxoutsetinfo", R"("none", 1000)") +
    1196        6317 :                     HelpExampleRpc("gettxoutsetinfo", R"("none", "00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09")")
    1197             :                 },
    1198        6478 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    1199             : {
    1200         179 :     UniValue ret(UniValue::VOBJ);
    1201             : 
    1202         161 :     const CBlockIndex* pindex{nullptr};
    1203         161 :     const CoinStatsHashType hash_type{request.params[0].isNull() ? CoinStatsHashType::HASH_SERIALIZED : ParseHashType(request.params[0].get_str())};
    1204         157 :     bool index_requested = request.params[2].isNull() || request.params[2].get_bool();
    1205             : 
    1206         157 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
    1207         157 :     ChainstateManager& chainman = EnsureChainman(node);
    1208         157 :     CChainState& active_chainstate = chainman.ActiveChainstate();
    1209         157 :     active_chainstate.ForceFlushStateToDisk();
    1210             : 
    1211             :     CCoinsView* coins_view;
    1212             :     BlockManager* blockman;
    1213             :     {
    1214         157 :         LOCK(::cs_main);
    1215         157 :         coins_view = &active_chainstate.CoinsDB();
    1216         157 :         blockman = &active_chainstate.m_blockman;
    1217         157 :         pindex = blockman->LookupBlockIndex(coins_view->GetBestBlock());
    1218         157 :     }
    1219             : 
    1220         157 :     if (!request.params[1].isNull()) {
    1221          44 :         if (!g_coin_stats_index) {
    1222           4 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Querying specific block heights requires coinstatsindex");
    1223             :         }
    1224             : 
    1225          40 :         if (hash_type == CoinStatsHashType::HASH_SERIALIZED) {
    1226           8 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "hash_serialized_2 hash type cannot be queried for a specific block");
    1227             :         }
    1228             : 
    1229          32 :         if (!index_requested) {
    1230           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot set use_index to false when querying for a specific block");
    1231             :         }
    1232          32 :         pindex = ParseHashOrHeight(request.params[1], chainman);
    1233          30 :     }
    1234             : 
    1235         143 :     if (index_requested && g_coin_stats_index) {
    1236          46 :         if (!g_coin_stats_index->BlockUntilSyncedToCurrentChain()) {
    1237           0 :             const IndexSummary summary{g_coin_stats_index->GetSummary()};
    1238             : 
    1239             :             // If a specific block was requested and the index has already synced past that height, we can return the
    1240             :             // data already even though the index is not fully synced yet.
    1241           0 :             if (pindex->nHeight > summary.best_block_height) {
    1242           0 :                 throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf("Unable to get data because coinstatsindex is still syncing. Current height: %d", summary.best_block_height));
    1243             :             }
    1244           0 :         }
    1245          46 :     }
    1246             : 
    1247         143 :     const std::optional<CCoinsStats> maybe_stats = GetUTXOStats(coins_view, *blockman, hash_type, node.rpc_interruption_point, pindex, index_requested);
    1248         143 :     if (maybe_stats.has_value()) {
    1249         143 :         const CCoinsStats& stats = maybe_stats.value();
    1250         143 :         ret.pushKV("height", (int64_t)stats.nHeight);
    1251         143 :         ret.pushKV("bestblock", stats.hashBlock.GetHex());
    1252         143 :         ret.pushKV("txouts", (int64_t)stats.nTransactionOutputs);
    1253         143 :         ret.pushKV("bogosize", (int64_t)stats.nBogoSize);
    1254         143 :         if (hash_type == CoinStatsHashType::HASH_SERIALIZED) {
    1255          77 :             ret.pushKV("hash_serialized_2", stats.hashSerialized.GetHex());
    1256          77 :         }
    1257         143 :         if (hash_type == CoinStatsHashType::MUHASH) {
    1258          46 :             ret.pushKV("muhash", stats.hashSerialized.GetHex());
    1259          46 :         }
    1260         143 :         CHECK_NONFATAL(stats.total_amount.has_value());
    1261         143 :         ret.pushKV("total_amount", ValueFromAmount(stats.total_amount.value()));
    1262         143 :         if (!stats.index_used) {
    1263          97 :             ret.pushKV("transactions", static_cast<int64_t>(stats.nTransactions));
    1264          97 :             ret.pushKV("disk_size", stats.nDiskSize);
    1265          97 :         } else {
    1266          46 :             ret.pushKV("total_unspendable_amount", ValueFromAmount(stats.total_unspendable_amount));
    1267             : 
    1268          46 :             CCoinsStats prev_stats{};
    1269          46 :             if (pindex->nHeight > 0) {
    1270          42 :                 const std::optional<CCoinsStats> maybe_prev_stats = GetUTXOStats(coins_view, *blockman, hash_type, node.rpc_interruption_point, pindex->pprev, index_requested);
    1271          42 :                 if (!maybe_prev_stats) {
    1272           0 :                     throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
    1273             :                 }
    1274          42 :                 prev_stats = maybe_prev_stats.value();
    1275          42 :             }
    1276             : 
    1277          46 :             UniValue block_info(UniValue::VOBJ);
    1278          46 :             block_info.pushKV("prevout_spent", ValueFromAmount(stats.total_prevout_spent_amount - prev_stats.total_prevout_spent_amount));
    1279          46 :             block_info.pushKV("coinbase", ValueFromAmount(stats.total_coinbase_amount - prev_stats.total_coinbase_amount));
    1280          46 :             block_info.pushKV("new_outputs_ex_coinbase", ValueFromAmount(stats.total_new_outputs_ex_coinbase_amount - prev_stats.total_new_outputs_ex_coinbase_amount));
    1281          46 :             block_info.pushKV("unspendable", ValueFromAmount(stats.total_unspendable_amount - prev_stats.total_unspendable_amount));
    1282             : 
    1283          46 :             UniValue unspendables(UniValue::VOBJ);
    1284          46 :             unspendables.pushKV("genesis_block", ValueFromAmount(stats.total_unspendables_genesis_block - prev_stats.total_unspendables_genesis_block));
    1285          46 :             unspendables.pushKV("bip30", ValueFromAmount(stats.total_unspendables_bip30 - prev_stats.total_unspendables_bip30));
    1286          46 :             unspendables.pushKV("scripts", ValueFromAmount(stats.total_unspendables_scripts - prev_stats.total_unspendables_scripts));
    1287          46 :             unspendables.pushKV("unclaimed_rewards", ValueFromAmount(stats.total_unspendables_unclaimed_rewards - prev_stats.total_unspendables_unclaimed_rewards));
    1288          46 :             block_info.pushKV("unspendables", unspendables);
    1289             : 
    1290          46 :             ret.pushKV("block_info", block_info);
    1291          46 :         }
    1292         143 :     } else {
    1293           0 :         throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
    1294             :     }
    1295         143 :     return ret;
    1296         173 : },
    1297             :     };
    1298           0 : }
    1299             : 
    1300       21093 : static RPCHelpMan gettxout()
    1301             : {
    1302       42186 :     return RPCHelpMan{"gettxout",
    1303       21093 :         "\nReturns details about an unspent transaction output.\n",
    1304       84372 :         {
    1305       21093 :             {"txid", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction id"},
    1306       21093 :             {"n", RPCArg::Type::NUM, RPCArg::Optional::NO, "vout number"},
    1307       21093 :             {"include_mempool", RPCArg::Type::BOOL, RPCArg::Default{true}, "Whether to include the mempool. Note that an unspent output that is spent in the mempool won't appear."},
    1308             :         },
    1309       63279 :         {
    1310       21093 :             RPCResult{"If the UTXO was not found", RPCResult::Type::NONE, "", ""},
    1311      126558 :             RPCResult{"Otherwise", RPCResult::Type::OBJ, "", "", {
    1312       21093 :                 {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"},
    1313       21093 :                 {RPCResult::Type::NUM, "confirmations", "The number of confirmations"},
    1314       21093 :                 {RPCResult::Type::STR_AMOUNT, "value", "The transaction value in " + CURRENCY_UNIT},
    1315      126558 :                 {RPCResult::Type::OBJ, "scriptPubKey", "", {
    1316       21093 :                     {RPCResult::Type::STR, "asm", "Disassembly of the public key script"},
    1317       21093 :                     {RPCResult::Type::STR, "desc", "Inferred descriptor for the output"},
    1318       21093 :                     {RPCResult::Type::STR_HEX, "hex", "The raw public key script bytes, hex-encoded"},
    1319       21093 :                     {RPCResult::Type::STR_HEX, "type", "The type, eg pubkeyhash"},
    1320       21093 :                     {RPCResult::Type::STR, "address", /*optional=*/ true, "Dash address (only if a well-defined address exists)"},
    1321             :                 }},
    1322       21093 :                 {RPCResult::Type::BOOL, "coinbase", "Coinbase or not"},
    1323             :             }},
    1324             :         },
    1325       21093 :         RPCExamples{
    1326             :             "\nGet unspent transactions\n"
    1327       21093 :             + HelpExampleCli("listunspent", "") +
    1328             :             "\nView the details\n"
    1329       21093 :             + HelpExampleCli("gettxout", "\"txid\" 1") +
    1330             :             "\nAs a JSON-RPC call\n"
    1331       21093 :             + HelpExampleRpc("gettxout", "\"txid\", 1")
    1332             :                 },
    1333       36030 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    1334             : {
    1335             : 
    1336       14937 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
    1337             : 
    1338       14937 :     ChainstateManager& chainman = EnsureChainman(node);
    1339       14937 :     LOCK(cs_main);
    1340             : 
    1341       14937 :     CChainState& active_chainstate = chainman.ActiveChainstate();
    1342             : 
    1343       14937 :     UniValue ret(UniValue::VOBJ);
    1344             : 
    1345       14937 :     uint256 hash(ParseHashV(request.params[0], "txid"));
    1346       14937 :     COutPoint out{hash, request.params[1].getInt<uint32_t>()};
    1347       14937 :     bool fMempool = true;
    1348       14937 :     if (!request.params[2].isNull())
    1349          72 :         fMempool = request.params[2].get_bool();
    1350             : 
    1351       14937 :     Coin coin;
    1352       14937 :     CCoinsViewCache* coins_view = &active_chainstate.CoinsTip();
    1353             : 
    1354       14937 :     if (fMempool) {
    1355       14889 :         const CTxMemPool& mempool = EnsureMemPool(node);
    1356       14889 :         LOCK(mempool.cs);
    1357       14889 :         CCoinsViewMemPool view(coins_view, mempool);
    1358       14889 :         if (!view.GetCoin(out, coin) || mempool.isSpent(out)) {
    1359          12 :             return UniValue::VNULL;
    1360             :         }
    1361       14889 :     } else {
    1362          48 :         if (!coins_view->GetCoin(out, coin)) {
    1363           6 :             return UniValue::VNULL;
    1364             :         }
    1365             :     }
    1366             : 
    1367       14919 :     const CBlockIndex* pindex = active_chainstate.m_blockman.LookupBlockIndex(coins_view->GetBestBlock());
    1368       14919 :     ret.pushKV("bestblock", pindex->GetBlockHash().GetHex());
    1369       14919 :     if (coin.nHeight == MEMPOOL_HEIGHT) {
    1370        3612 :         ret.pushKV("confirmations", 0);
    1371        3612 :     } else {
    1372       11307 :         ret.pushKV("confirmations", (int64_t)(pindex->nHeight - coin.nHeight + 1));
    1373             :     }
    1374       14919 :     ret.pushKV("value", ValueFromAmount(coin.out.nValue));
    1375       14919 :     UniValue o(UniValue::VOBJ);
    1376       14919 :     ScriptToUniv(coin.out.scriptPubKey, /*out=*/o, /*include_hex=*/true, /*include_address=*/true);
    1377       14919 :     ret.pushKV("scriptPubKey", o);
    1378       14919 :     ret.pushKV("coinbase", (bool)coin.fCoinBase);
    1379             : 
    1380       14919 :     return ret;
    1381       14937 : },
    1382             :     };
    1383           0 : }
    1384             : 
    1385        6160 : static RPCHelpMan verifychain()
    1386             : {
    1387       12320 :     return RPCHelpMan{"verifychain",
    1388        6160 :                 "\nVerifies blockchain database.\n",
    1389       18480 :                 {
    1390       12320 :                     {"checklevel", RPCArg::Type::NUM, RPCArg::DefaultHint{strprintf("%d, range=0-4", DEFAULT_CHECKLEVEL)},
    1391        6160 :                         strprintf("How thorough the block verification is:\n - %s", MakeUnorderedList(CHECKLEVEL_DOC))},
    1392        6160 :                     {"nblocks", RPCArg::Type::NUM, RPCArg::DefaultHint{strprintf("%d, 0=all", DEFAULT_CHECKBLOCKS)}, "The number of blocks to check."},
    1393             :                 },
    1394        6160 :                 RPCResult{
    1395        6160 :                     RPCResult::Type::BOOL, "", "Verified or not"},
    1396        6160 :                 RPCExamples{
    1397        6160 :                     HelpExampleCli("verifychain", "")
    1398        6160 :             + HelpExampleRpc("verifychain", "")
    1399             :                 },
    1400        6164 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    1401             : {
    1402           4 :     const int check_level{request.params[0].isNull() ? DEFAULT_CHECKLEVEL : request.params[0].getInt<int>()};
    1403           4 :     const int check_depth{request.params[1].isNull() ? DEFAULT_CHECKBLOCKS : request.params[1].getInt<int>()};
    1404             : 
    1405           4 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
    1406             : 
    1407           4 :     ChainstateManager& chainman = EnsureChainman(node);
    1408           4 :     LOCK(cs_main);
    1409             : 
    1410           4 :     CChainState& active_chainstate = chainman.ActiveChainstate();
    1411           8 :     return CVerifyDB().VerifyDB(
    1412           4 :         active_chainstate, Params().GetConsensus(), active_chainstate.CoinsTip(), *CHECK_NONFATAL(node.evodb), check_level, check_depth);
    1413           4 : },
    1414             :     };
    1415           0 : }
    1416             : 
    1417      227040 : static void SoftForkDescPushBack(const CBlockIndex* active_chain_tip, UniValue& softforks, const ChainstateManager& chainman, Consensus::BuriedDeployment dep)
    1418             : {
    1419             :     // For buried deployments.
    1420             : 
    1421      227040 :     if (!DeploymentEnabled(chainman, dep)) return;
    1422             : 
    1423      227040 :     UniValue rv(UniValue::VOBJ);
    1424      227040 :     rv.pushKV("type", "buried");
    1425             :     // getblockchaininfo reports the softfork as active from when the chain height is
    1426             :     // one below the activation height
    1427      227040 :     rv.pushKV("active", DeploymentActiveAfter(active_chain_tip, chainman.GetConsensus(), dep));
    1428      227040 :     rv.pushKV("height", chainman.GetConsensus().DeploymentHeight(dep));
    1429      227040 :     softforks.pushKV(DeploymentName(dep), rv);
    1430      227040 : }
    1431             : 
    1432       30272 : static void SoftForkDescPushBack(const CBlockIndex* active_chain_tip, const std::unordered_map<uint8_t, int>& signals, UniValue& softforks, const ChainstateManager& chainman, Consensus::DeploymentPos id)
    1433             : {
    1434             :     // For BIP9 deployments.
    1435             : 
    1436       30272 :     if (!DeploymentEnabled(chainman, id)) return;
    1437             : 
    1438       30272 :     UniValue bip9(UniValue::VOBJ);
    1439       30272 :     const ThresholdState thresholdState = chainman.m_versionbitscache.State(active_chain_tip, chainman.GetConsensus(), id);
    1440       30272 :     switch (thresholdState) {
    1441       16194 :     case ThresholdState::DEFINED: bip9.pushKV("status", "defined"); break;
    1442        3021 :     case ThresholdState::STARTED: bip9.pushKV("status", "started"); break;
    1443       10635 :     case ThresholdState::LOCKED_IN: bip9.pushKV("status", "locked_in"); break;
    1444         422 :     case ThresholdState::ACTIVE: bip9.pushKV("status", "active"); break;
    1445           0 :     case ThresholdState::FAILED: bip9.pushKV("status", "failed"); break;
    1446             :     }
    1447       30272 :     const bool has_signal = (ThresholdState::STARTED == thresholdState || ThresholdState::LOCKED_IN == thresholdState);
    1448       30272 :     if (has_signal) {
    1449       13656 :         bip9.pushKV("bit", chainman.GetConsensus().vDeployments[id].bit);
    1450       13656 :     }
    1451       30272 :     bip9.pushKV("start_time", chainman.GetConsensus().vDeployments[id].nStartTime);
    1452       30272 :     bip9.pushKV("timeout", chainman.GetConsensus().vDeployments[id].nTimeout);
    1453       30272 :     bip9.pushKV("min_activation_height", chainman.GetConsensus().vDeployments[id].min_activation_height);
    1454       30272 :     bip9.pushKV("ehf", chainman.GetConsensus().vDeployments[id].useEHF);
    1455       30272 :     if (auto it = signals.find(chainman.GetConsensus().vDeployments[id].bit); it != signals.end()) {
    1456         228 :         bip9.pushKV("ehf_height", it->second);
    1457         228 :     }
    1458       30272 :     int64_t since_height = chainman.m_versionbitscache.StateSinceHeight(active_chain_tip, chainman.GetConsensus(), id);
    1459       30272 :     bip9.pushKV("since", since_height);
    1460       30272 :     if (has_signal) {
    1461       13656 :         UniValue statsUV(UniValue::VOBJ);
    1462       13656 :         BIP9Stats statsStruct = chainman.m_versionbitscache.Statistics(active_chain_tip, chainman.GetConsensus(), id);
    1463       13656 :         statsUV.pushKV("period", statsStruct.period);
    1464       13656 :         statsUV.pushKV("elapsed", statsStruct.elapsed);
    1465       13656 :         statsUV.pushKV("count", statsStruct.count);
    1466       13656 :         if (ThresholdState::LOCKED_IN != thresholdState) {
    1467        3021 :             statsUV.pushKV("threshold", statsStruct.threshold);
    1468        3021 :             statsUV.pushKV("possible", statsStruct.possible);
    1469        3021 :         }
    1470       13656 :         bip9.pushKV("statistics", statsUV);
    1471       13656 :     }
    1472       30272 :     if (ThresholdState::LOCKED_IN == thresholdState) {
    1473       10635 :         bip9.pushKV("activation_height", since_height + static_cast<int>(chainman.GetConsensus().vDeployments[id].nWindowSize));
    1474       10635 :     }
    1475       30272 :     bip9.pushKV("min_activation_height", chainman.GetConsensus().vDeployments[id].min_activation_height);
    1476             : 
    1477       30272 :     UniValue rv(UniValue::VOBJ);
    1478       30272 :     rv.pushKV("type", "bip9");
    1479       30272 :     rv.pushKV("bip9", bip9);
    1480       30272 :     if (ThresholdState::ACTIVE == thresholdState) {
    1481         422 :         rv.pushKV("height", since_height);
    1482         422 :     }
    1483       30272 :     rv.pushKV("active", ThresholdState::ACTIVE == thresholdState);
    1484             : 
    1485       30272 :     softforks.pushKV(DeploymentName(id), rv);
    1486       30272 : }
    1487             : 
    1488       21294 : RPCHelpMan getblockchaininfo()
    1489             : {
    1490       42588 :     return RPCHelpMan{"getblockchaininfo",
    1491       21294 :         "Returns an object containing various state info regarding blockchain processing.\n",
    1492       21294 :         {},
    1493       21294 :         RPCResult{
    1494       21294 :             RPCResult::Type::OBJ, "", "",
    1495      383292 :             {
    1496       21294 :                 {RPCResult::Type::STR, "chain", "current network name (main, test, regtest) and "
    1497             :                                                 "devnet or devnet-<name> for \"-devnet\" and \"-devnet=<name>\" respectively\n"},
    1498       21294 :                 {RPCResult::Type::NUM, "blocks", "the height of the most-work fully-validated chain. The genesis block has height 0"},
    1499       21294 :                 {RPCResult::Type::NUM, "headers", "the current number of headers we have validated"},
    1500       21294 :                 {RPCResult::Type::STR, "bestblockhash", "the hash of the currently best block"},
    1501       21294 :                 {RPCResult::Type::NUM, "difficulty", "the current difficulty"},
    1502       21294 :                 {RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME},
    1503       21294 :                 {RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME},
    1504       21294 :                 {RPCResult::Type::NUM, "verificationprogress", "estimate of verification progress [0..1]"},
    1505       21294 :                 {RPCResult::Type::BOOL, "initialblockdownload", "(debug information) estimate of whether this node is in Initial Block Download mode"},
    1506       21294 :                 {RPCResult::Type::STR_HEX, "chainwork", "total amount of work in active chain, in hexadecimal"},
    1507       21294 :                 {RPCResult::Type::NUM, "size_on_disk", "the estimated size of the block and undo files on disk"},
    1508       21294 :                 {RPCResult::Type::BOOL, "pruned", "if the blocks are subject to pruning"},
    1509       21294 :                 {RPCResult::Type::NUM, "pruneheight", /*optional=*/true, "height of the last block pruned, plus one (only present if pruning is enabled)"},
    1510       21294 :                 {RPCResult::Type::BOOL, "automatic_pruning", /*optional=*/true, "whether automatic pruning is enabled (only present if pruning is enabled)"},
    1511       21294 :                 {RPCResult::Type::NUM, "prune_target_size", /*optional=*/true, "the target size used by pruning (only present if automatic pruning is enabled)"},
    1512       42588 :                 {RPCResult::Type::OBJ_DYN, "softforks", "status of softforks in progress",
    1513       42588 :                 {
    1514       42588 :                     {RPCResult::Type::OBJ, "xxxx", "name of the softfork",
    1515      106470 :                     {
    1516       21294 :                         {RPCResult::Type::STR, "type", "one of \"buried\", \"bip9\""},
    1517       42588 :                         {RPCResult::Type::OBJ, "bip9", /*optional=*/true, "status of bip9 softforks (only for \"bip9\" type)",
    1518      234234 :                         {
    1519       21294 :                             {RPCResult::Type::STR, "status", "one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\""},
    1520       21294 :                             {RPCResult::Type::NUM, "bit", /*optional=*/true, "the bit (0-28) in the block version field used to signal this softfork (only for \"started\" and \"locked_in\" status)"},
    1521       21294 :                             {RPCResult::Type::NUM_TIME, "start_time", "the minimum median time past of a block at which the bit gains its meaning"},
    1522       21294 :                             {RPCResult::Type::NUM_TIME, "timeout", "the median time past of a block at which the deployment is considered failed if not yet locked in"},
    1523       21294 :                             {RPCResult::Type::BOOL, "ehf", "returns true for EHF activated forks"},
    1524       21294 :                             {RPCResult::Type::NUM, "ehf_height", /*optional=*/true, "the minimum height when miner's signals for the deployment matter. Below this height miner signaling cannot trigger hard fork lock-in. Not specified for non-EHF forks"},
    1525       21294 :                             {RPCResult::Type::NUM, "since", "height of the first block to which the status applies"},
    1526       21294 :                             {RPCResult::Type::NUM, "activation_height", "expected activation height for this softfork (only for \"locked_in\" status)"},
    1527       21294 :                             {RPCResult::Type::NUM, "min_activation_height", "minimum height of blocks for which the rules may be enforced"},
    1528       42588 :                             {RPCResult::Type::OBJ, "statistics", /*optional=*/true, "numeric statistics about signalling for a softfork (only for \"started\" and \"locked_in\" status)",
    1529      127764 :                             {
    1530       21294 :                                 {RPCResult::Type::NUM, "period", "the length in blocks of the signalling period"},
    1531       21294 :                                 {RPCResult::Type::NUM, "threshold", /*optional=*/true, "the number of blocks with the version bit set required to activate the feature (only for \"started\" status)"},
    1532       21294 :                                 {RPCResult::Type::NUM, "elapsed", "the number of blocks elapsed since the beginning of the current period"},
    1533       21294 :                                 {RPCResult::Type::NUM, "count", "the number of blocks with the version bit set in the current period"},
    1534       21294 :                                 {RPCResult::Type::BOOL, "possible", /*optional=*/true, "returns false if there are not enough blocks left in this period to pass activation threshold (only for \"started\" status)"},
    1535             :                             }},
    1536             :                         }},
    1537       21294 :                         {RPCResult::Type::NUM, "height", /*optional=*/true, "height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)"},
    1538       21294 :                         {RPCResult::Type::BOOL, "active", "true if the rules are enforced for the mempool and the next block"},
    1539             :                     }},
    1540             :                 }},
    1541       21294 :                 {RPCResult::Type::STR, "warnings", "any network and blockchain warnings"},
    1542             :             }},
    1543       21294 :         RPCExamples{
    1544       21294 :             HelpExampleCli("getblockchaininfo", "")
    1545       21294 :     + HelpExampleRpc("getblockchaininfo", "")
    1546             :         },
    1547       36430 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    1548             : {
    1549             : 
    1550       15136 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
    1551       15136 :     const ArgsManager& args{EnsureArgsman(node)};
    1552       15136 :     ChainstateManager& chainman = EnsureChainman(node);
    1553             : 
    1554       15136 :     LOCK(cs_main);
    1555       15136 :     CChainState& active_chainstate = chainman.ActiveChainstate();
    1556             : 
    1557       15136 :     const CBlockIndex& tip{*CHECK_NONFATAL(active_chainstate.m_chain.Tip())};
    1558       15136 :     const int height{tip.nHeight};
    1559             : 
    1560       15136 :     const auto ehfSignals{active_chainstate.ChainHelper().GetSignalsStage(&tip)};
    1561             : 
    1562       15136 :     UniValue obj(UniValue::VOBJ);
    1563       15136 :     if (args.IsArgSet("-devnet")) {
    1564           0 :         obj.pushKV("chain", args.GetDevNetName());
    1565           0 :     } else {
    1566       15136 :         obj.pushKV("chain", Params().NetworkIDString());
    1567             :     }
    1568       15136 :     obj.pushKV("blocks", height);
    1569       15136 :     obj.pushKV("headers", chainman.m_best_header ? chainman.m_best_header->nHeight : -1);
    1570       15136 :     obj.pushKV("bestblockhash", tip.GetBlockHash().GetHex());
    1571       15136 :     obj.pushKV("difficulty", GetDifficulty(&tip));
    1572       15136 :     obj.pushKV("time", tip.GetBlockTime());
    1573       15136 :     obj.pushKV("mediantime", tip.GetMedianTimePast());
    1574       15136 :     obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), &tip));
    1575       15136 :     obj.pushKV("initialblockdownload", active_chainstate.IsInitialBlockDownload());
    1576       15136 :     obj.pushKV("chainwork", tip.nChainWork.GetHex());
    1577       15136 :     obj.pushKV("size_on_disk", chainman.m_blockman.CalculateCurrentUsage());
    1578       15136 :     obj.pushKV("pruned", node::fPruneMode);
    1579       15136 :     if (node::fPruneMode) {
    1580           8 :         obj.pushKV("pruneheight", chainman.m_blockman.GetFirstStoredBlock(tip)->nHeight);
    1581             : 
    1582             :         // if 0, execution bypasses the whole if block.
    1583           8 :         bool automatic_pruning{args.GetIntArg("-prune", 0) != 1};
    1584           8 :         obj.pushKV("automatic_pruning",  automatic_pruning);
    1585           8 :         if (automatic_pruning) {
    1586           4 :             obj.pushKV("prune_target_size",  node::nPruneTarget);
    1587           4 :         }
    1588           8 :     }
    1589             : 
    1590       15136 :     UniValue softforks(UniValue::VOBJ);
    1591      242176 :     for (auto deploy : { /* sorted by activation block */
    1592             :                          Consensus::DEPLOYMENT_HEIGHTINCB,
    1593             :                          Consensus::DEPLOYMENT_DERSIG,
    1594             :                          Consensus::DEPLOYMENT_CLTV,
    1595             :                          Consensus::DEPLOYMENT_BIP147,
    1596             :                          Consensus::DEPLOYMENT_CSV,
    1597             :                          Consensus::DEPLOYMENT_DIP0001,
    1598             :                          Consensus::DEPLOYMENT_DIP0003,
    1599             :                          Consensus::DEPLOYMENT_DIP0008,
    1600             :                          Consensus::DEPLOYMENT_DIP0020,
    1601             :                          Consensus::DEPLOYMENT_DIP0024,
    1602             :                          Consensus::DEPLOYMENT_BRR,
    1603             :                          Consensus::DEPLOYMENT_V19,
    1604             :                          Consensus::DEPLOYMENT_V20,
    1605             :                          Consensus::DEPLOYMENT_MN_RR,
    1606             :                          Consensus::DEPLOYMENT_WITHDRAWALS,
    1607             :                         }) {
    1608      227040 :         SoftForkDescPushBack(&tip, softforks, chainman, deploy);
    1609             :     }
    1610       45408 :     for (auto ehf_deploy : { /* sorted by activation block */
    1611             :                              Consensus::DEPLOYMENT_V24,
    1612             :                              Consensus::DEPLOYMENT_TESTDUMMY }) {
    1613       30272 :         SoftForkDescPushBack(&tip, ehfSignals, softforks, chainman, ehf_deploy);
    1614             :     }
    1615       15136 :     obj.pushKV("softforks", softforks);
    1616             : 
    1617       15136 :     obj.pushKV("warnings", GetWarnings(false).original);
    1618       15136 :     return obj;
    1619       15136 : },
    1620             :     };
    1621           0 : }
    1622             : 
    1623             : /** Comparison function for sorting the getchaintips heads.  */
    1624             : struct CompareBlocksByHeight
    1625             : {
    1626         303 :     bool operator()(const CBlockIndex* a, const CBlockIndex* b) const
    1627             :     {
    1628             :         /* Make sure that unequal blocks with the same height do not compare
    1629             :            equal. Use the pointers themselves to make a distinction. */
    1630             : 
    1631         303 :         if (a->nHeight != b->nHeight)
    1632         163 :           return (a->nHeight > b->nHeight);
    1633             : 
    1634         140 :         return a < b;
    1635         303 :     }
    1636             : };
    1637             : 
    1638        6224 : static RPCHelpMan getchaintips()
    1639             : {
    1640       12448 :     return RPCHelpMan{"getchaintips",
    1641        6224 :                 "Return information about all known tips in the block tree,"
    1642             :                 " including the main chain as well as orphaned branches.\n",
    1643       18672 :                 {
    1644        6224 :                     {"count", RPCArg::Type::NUM, RPCArg::Default{INT_MAX}, "only show this much of latest tips"},
    1645        6224 :                     {"branchlen", RPCArg::Type::NUM, RPCArg::Default{-1}, "only show tips that have equal or greater length of branch"},
    1646             :                 },
    1647        6224 :                 RPCResult{
    1648        6224 :                     RPCResult::Type::ARR, "", "",
    1649       12448 :                     {{RPCResult::Type::OBJ, "", "",
    1650       49792 :                         {
    1651        6224 :                             {RPCResult::Type::NUM, "height", "height of the chain tip"},
    1652        6224 :                             {RPCResult::Type::STR_HEX, "hash", "block hash of the tip"},
    1653        6224 :                             {RPCResult::Type::NUM, "difficulty", "The difficulty"},
    1654        6224 :                             {RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the current chain (in hex)"},
    1655        6224 :                             {RPCResult::Type::NUM, "branchlen", "zero for main chain, otherwise length of branch connecting the tip to the main chain"},
    1656        6224 :                             {RPCResult::Type::STR_HEX, "forkpoint", "same as \"hash\" for the main chain"},
    1657        6224 :                             {RPCResult::Type::STR, "status", "status of the chain, \"active\" for the main chain\n"
    1658             :             "Possible values for status:\n"
    1659             :             "1.  \"invalid\"               This branch contains at least one invalid block\n"
    1660             :             "2.  \"headers-only\"          Not all blocks for this branch are available, but the headers are valid\n"
    1661             :             "3.  \"valid-headers\"         All blocks are available for this branch, but they were never fully validated\n"
    1662             :             "4.  \"valid-fork\"            This branch is not part of the active chain, but is fully validated\n"
    1663             :             "5.  \"active\"                This is the tip of the active main chain, which is certainly valid\n"
    1664             :             "6.  \"conflicting\"           This block or one of its ancestors is conflicting with ChainLocks."},
    1665             :                         }}}},
    1666        6224 :                 RPCExamples{
    1667        6224 :                     HelpExampleCli("getchaintips", "")
    1668        6224 :             + HelpExampleRpc("getchaintips", "")
    1669             :                 },
    1670        6292 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    1671             : {
    1672             : 
    1673          68 :     ChainstateManager& chainman = EnsureAnyChainman(request.context);
    1674          68 :     LOCK(cs_main);
    1675          68 :     CChain& active_chain = chainman.ActiveChain();
    1676             : 
    1677             :     /*
    1678             :      * Idea: The set of chain tips is the active chain tip, plus orphan blocks which do not have another orphan building off of them.
    1679             :      * Algorithm:
    1680             :      *  - Make one pass through BlockIndex(), picking out the orphan blocks, and also storing a set of the orphan block's pprev pointers.
    1681             :      *  - Iterate through the orphan blocks. If the block isn't pointed to by another orphan, it is a chain tip.
    1682             :      *  - Add the active chain tip
    1683             :      */
    1684          68 :     std::set<const CBlockIndex*, CompareBlocksByHeight> setTips;
    1685          68 :     std::set<const CBlockIndex*> setOrphans;
    1686          68 :     std::set<const CBlockIndex*> setPrevs;
    1687             : 
    1688       15689 :     for (const auto& [_, block_index] : chainman.BlockIndex()) {
    1689       27830 :         if (!active_chain.Contains(&block_index)) {
    1690        1706 :             setOrphans.insert(&block_index);
    1691        1706 :             setPrevs.insert(block_index.pprev);
    1692        1706 :         }
    1693             :     }
    1694             : 
    1695        1774 :     for (const auto& orphan : setOrphans) {
    1696        1706 :         if (setPrevs.erase(orphan) == 0) {
    1697         113 :             setTips.insert(orphan);
    1698         113 :         }
    1699             :     }
    1700             : 
    1701             :     // Always report the currently active tip.
    1702          68 :     setTips.insert(active_chain.Tip());
    1703             : 
    1704          68 :     int nCountMax{request.params[0].isNull() ? INT_MAX : request.params[0].getInt<int>()};
    1705          68 :     const int nBranchMin{request.params[1].isNull() ? -1: request.params[1].getInt<int>()};
    1706             : 
    1707             :     /* Construct the output array.  */
    1708          68 :     UniValue res(UniValue::VARR);
    1709         236 :     for (const CBlockIndex* block : setTips)
    1710             :     {
    1711         181 :         const CBlockIndex* pindexFork = active_chain.FindFork(block);
    1712         181 :         const int branchLen = block->nHeight - pindexFork->nHeight;
    1713         181 :         if(branchLen < nBranchMin) continue;
    1714             : 
    1715         181 :         if(nCountMax-- < 1) break;
    1716             : 
    1717         168 :         UniValue obj(UniValue::VOBJ);
    1718         168 :         obj.pushKV("height", block->nHeight);
    1719         168 :         obj.pushKV("hash", block->phashBlock->GetHex());
    1720         168 :         obj.pushKV("difficulty", GetDifficulty(block));
    1721         168 :         obj.pushKV("chainwork", block->nChainWork.GetHex());
    1722         168 :         obj.pushKV("branchlen", branchLen);
    1723         168 :         obj.pushKV("forkpoint", pindexFork->phashBlock->GetHex());
    1724             : 
    1725         168 :         std::string status;
    1726         168 :         if (active_chain.Contains(block)) {
    1727             :             // This block is part of the currently active chain.
    1728          65 :             status = "active";
    1729         168 :         } else if (block->nStatus & BLOCK_FAILED_MASK) {
    1730             :             // This block or one of its ancestors is invalid.
    1731          34 :             status = "invalid";
    1732         103 :         } else if (block->nStatus & BLOCK_CONFLICT_CHAINLOCK) {
    1733             :             // This block or one of its ancestors is conflicting with ChainLocks.
    1734          21 :             status = "conflicting";
    1735          69 :         } else if (!block->HaveTxsDownloaded()) {
    1736             :             // This block cannot be connected because full block data for it or one of its parents is missing.
    1737          44 :             status = "headers-only";
    1738          48 :         } else if (block->IsValid(BLOCK_VALID_SCRIPTS)) {
    1739             :             // This block is fully validated, but no longer part of the active chain. It was probably the active block once, but was reorganized.
    1740           4 :             status = "valid-fork";
    1741           4 :         } else if (block->IsValid(BLOCK_VALID_TREE)) {
    1742             :             // The headers for this block are valid, but it has not been validated. It was probably never part of the most-work chain.
    1743           0 :             status = "valid-headers";
    1744           0 :         } else {
    1745             :             // No clue.
    1746           0 :             status = "unknown";
    1747             :         }
    1748         168 :         obj.pushKV("status", status);
    1749             : 
    1750         168 :         res.push_back(obj);
    1751         168 :     }
    1752             : 
    1753          68 :     return res;
    1754          68 : },
    1755             :     };
    1756           0 : }
    1757             : 
    1758        6174 : static RPCHelpMan preciousblock()
    1759             : {
    1760       12348 :     return RPCHelpMan{"preciousblock",
    1761        6174 :                 "\nTreats a block as if it were received before others with the same work.\n"
    1762             :                 "\nA later preciousblock call can override the effect of an earlier one.\n"
    1763             :                 "\nThe effects of preciousblock are not retained across restarts.\n",
    1764       12348 :                 {
    1765        6174 :                     {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hash of the block to mark as precious"},
    1766             :                 },
    1767        6174 :                 RPCResult{RPCResult::Type::NONE, "", ""},
    1768        6174 :                 RPCExamples{
    1769        6174 :                     HelpExampleCli("preciousblock", "\"blockhash\"")
    1770        6174 :             + HelpExampleRpc("preciousblock", "\"blockhash\"")
    1771             :                 },
    1772        6192 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    1773             : {
    1774          18 :     uint256 hash(ParseHashV(request.params[0], "blockhash"));
    1775             :     CBlockIndex* pblockindex;
    1776             : 
    1777          18 :     ChainstateManager& chainman = EnsureAnyChainman(request.context);
    1778             :     {
    1779          18 :         LOCK(cs_main);
    1780          18 :         pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
    1781          18 :         if (!pblockindex) {
    1782           0 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
    1783             :         }
    1784          18 :     }
    1785             : 
    1786          18 :     BlockValidationState state;
    1787          18 :     chainman.ActiveChainstate().PreciousBlock(state, pblockindex);
    1788             : 
    1789          18 :     if (!state.IsValid()) {
    1790           0 :         throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString());
    1791             :     }
    1792             : 
    1793          18 :     return UniValue::VNULL;
    1794          18 : },
    1795             :     };
    1796           0 : }
    1797             : 
    1798        6367 : static RPCHelpMan invalidateblock()
    1799             : {
    1800       12734 :     return RPCHelpMan{"invalidateblock",
    1801        6367 :                 "\nPermanently marks a block as invalid, as if it violated a consensus rule.\n",
    1802       12734 :                 {
    1803        6367 :                     {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hash of the block to mark as invalid"},
    1804             :                 },
    1805        6367 :                 RPCResult{RPCResult::Type::NONE, "", ""},
    1806        6367 :                 RPCExamples{
    1807        6367 :                     HelpExampleCli("invalidateblock", "\"blockhash\"")
    1808        6367 :             + HelpExampleRpc("invalidateblock", "\"blockhash\"")
    1809             :                 },
    1810        6594 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    1811             : {
    1812         229 :     uint256 hash(ParseHashV(request.params[0], "blockhash"));
    1813         227 :     BlockValidationState state;
    1814             : 
    1815             :     CBlockIndex* pblockindex;
    1816         227 :     ChainstateManager& chainman = EnsureAnyChainman(request.context);
    1817             :     {
    1818         227 :         LOCK(cs_main);
    1819         227 :         pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
    1820         227 :         if (!pblockindex) {
    1821           2 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
    1822             :         }
    1823         227 :     }
    1824             : 
    1825         225 :     CChainState& active_chainstate = chainman.ActiveChainstate();
    1826         225 :     active_chainstate.InvalidateBlock(state, pblockindex);
    1827             : 
    1828         225 :     if (state.IsValid()) {
    1829         225 :         active_chainstate.ActivateBestChain(state);
    1830         225 :     }
    1831             : 
    1832         225 :     if (!state.IsValid()) {
    1833           0 :         throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString());
    1834             :     }
    1835             : 
    1836         225 :     return UniValue::VNULL;
    1837         229 : },
    1838             :     };
    1839           0 : }
    1840             : 
    1841        6208 : static RPCHelpMan reconsiderblock()
    1842             : {
    1843       12416 :     return RPCHelpMan{"reconsiderblock",
    1844        6208 :                 "\nRemoves invalidity status of a block, its ancestors and its descendants, reconsider them for activation.\n"
    1845             :                 "This can be used to undo the effects of invalidateblock.\n",
    1846       18624 :                 {
    1847        6208 :                     {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hash of the block to reconsider"},
    1848        6208 :                     {"ignore_chainlocks", RPCArg::Type::BOOL, RPCArg::Default{false}, "if true, existing chainlocks will be ignored"},
    1849             :                 },
    1850        6208 :                 RPCResult{RPCResult::Type::NONE, "", ""},
    1851        6208 :                 RPCExamples{
    1852        6208 :                     HelpExampleCli("reconsiderblock", "\"blockhash\"")
    1853        6208 :             + HelpExampleRpc("reconsiderblock", "\"blockhash\"")
    1854             :                 },
    1855        6276 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    1856             : {
    1857          68 :     ChainstateManager& chainman = EnsureAnyChainman(request.context);
    1858          68 :     CChainState& active_chainstate = chainman.ActiveChainstate();
    1859             : 
    1860          68 :     uint256 hash(ParseHashV(request.params[0], "blockhash"));
    1861          68 :     const bool ignore_chainlocks{request.params[1].isNull() ? false : request.params[1].get_bool()};
    1862             : 
    1863             :     {
    1864          68 :         LOCK(cs_main);
    1865          68 :         CBlockIndex* pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
    1866          68 :         if (!pblockindex) {
    1867           0 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
    1868             :         }
    1869             : 
    1870          68 :         active_chainstate.ResetBlockFailureFlags(pblockindex, ignore_chainlocks);
    1871          68 :     }
    1872             : 
    1873          68 :     BlockValidationState state;
    1874          68 :     active_chainstate.ActivateBestChain(state);
    1875             : 
    1876          68 :     if (!state.IsValid()) {
    1877           0 :         throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString());
    1878             :     }
    1879             : 
    1880          68 :     return UniValue::VNULL;
    1881          68 : },
    1882             :     };
    1883           0 : }
    1884             : 
    1885        6204 : static RPCHelpMan getchaintxstats()
    1886             : {
    1887       12408 :     return RPCHelpMan{"getchaintxstats",
    1888        6204 :                 "\nCompute statistics about the total number and rate of transactions in the chain.\n",
    1889       18612 :                 {
    1890        6204 :                     {"nblocks", RPCArg::Type::NUM, RPCArg::DefaultHint{"one month"}, "Size of the window in number of blocks"},
    1891        6204 :                     {"blockhash", RPCArg::Type::STR_HEX, RPCArg::DefaultHint{"chain tip"}, "The hash of the block that ends the window."},
    1892             :                 },
    1893        6204 :                 RPCResult{
    1894        6204 :                     RPCResult::Type::OBJ, "", "",
    1895       55836 :                     {
    1896        6204 :                         {RPCResult::Type::NUM_TIME, "time", "The timestamp for the final block in the window, expressed in " + UNIX_EPOCH_TIME},
    1897        6204 :                         {RPCResult::Type::NUM, "txcount", "The total number of transactions in the chain up to that point"},
    1898        6204 :                         {RPCResult::Type::STR_HEX, "window_final_block_hash", "The hash of the final block in the window"},
    1899        6204 :                         {RPCResult::Type::NUM, "window_final_block_height", "The height of the final block in the window."},
    1900        6204 :                         {RPCResult::Type::NUM, "window_block_count", "Size of the window in number of blocks"},
    1901        6204 :                         {RPCResult::Type::NUM, "window_tx_count", /*optional=*/true, "The number of transactions in the window. Only returned if \"window_block_count\" is > 0"},
    1902        6204 :                         {RPCResult::Type::NUM, "window_interval", /*optional=*/true, "The elapsed time in the window in seconds. Only returned if \"window_block_count\" is > 0"},
    1903        6204 :                         {RPCResult::Type::NUM, "txrate", /*optional=*/true, "The average rate of transactions per second in the window. Only returned if \"window_interval\" is > 0"},
    1904             :                     }},
    1905        6204 :                 RPCExamples{
    1906        6204 :                     HelpExampleCli("getchaintxstats", "")
    1907        6204 :             + HelpExampleRpc("getchaintxstats", "2016")
    1908             :                 },
    1909        6252 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    1910             : {
    1911          48 :     ChainstateManager& chainman = EnsureAnyChainman(request.context);
    1912             : 
    1913          48 :     CChain& active_chain = chainman.ActiveChain();
    1914             :     const CBlockIndex* pindex;
    1915          48 :     int blockcount = 30 * 24 * 60 * 60 / Params().GetConsensus().nPowTargetSpacing; // By default: 1 month
    1916             : 
    1917          48 :     if (request.params[1].isNull()) {
    1918          24 :         LOCK(cs_main);
    1919          24 :         pindex = active_chain.Tip();
    1920          52 :     } else {
    1921          24 :         uint256 hash(ParseHashV(request.params[1], "blockhash"));
    1922          12 :         LOCK(cs_main);
    1923          12 :         pindex = chainman.m_blockman.LookupBlockIndex(hash);
    1924          12 :         if (!pindex) {
    1925           4 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
    1926             :         }
    1927           8 :         if (!active_chain.Contains(pindex)) {
    1928           4 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Block is not in main chain");
    1929             :         }
    1930          12 :     }
    1931             : 
    1932          28 :     CHECK_NONFATAL(pindex != nullptr);
    1933             : 
    1934          28 :     if (request.params[0].isNull()) {
    1935           8 :         blockcount = std::max(0, std::min(blockcount, pindex->nHeight - 1));
    1936           8 :     } else {
    1937          20 :         blockcount = request.params[0].getInt<int>();
    1938             : 
    1939          20 :         if (blockcount < 0 || (blockcount > 0 && blockcount >= pindex->nHeight)) {
    1940           8 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block count: should be between 0 and the block's height - 1");
    1941             :         }
    1942             :     }
    1943             : 
    1944          12 :     const CBlockIndex& past_block{*CHECK_NONFATAL(pindex->GetAncestor(pindex->nHeight - blockcount))};
    1945          12 :     const int64_t nTimeDiff{pindex->GetMedianTimePast() - past_block.GetMedianTimePast()};
    1946          12 :     const int nTxDiff = pindex->nChainTx - past_block.nChainTx;
    1947             : 
    1948          12 :     UniValue ret(UniValue::VOBJ);
    1949          12 :     ret.pushKV("time", (int64_t)pindex->nTime);
    1950          12 :     ret.pushKV("txcount", (int64_t)pindex->nChainTx);
    1951          12 :     ret.pushKV("window_final_block_hash", pindex->GetBlockHash().GetHex());
    1952          12 :     ret.pushKV("window_final_block_height", pindex->nHeight);
    1953          12 :     ret.pushKV("window_block_count", blockcount);
    1954          12 :     if (blockcount > 0) {
    1955           8 :         ret.pushKV("window_tx_count", nTxDiff);
    1956           8 :         ret.pushKV("window_interval", nTimeDiff);
    1957           8 :         if (nTimeDiff > 0) {
    1958           8 :             ret.pushKV("txrate", ((double)nTxDiff) / nTimeDiff);
    1959           8 :         }
    1960           8 :     }
    1961             : 
    1962          12 :     return ret;
    1963          40 : },
    1964             :     };
    1965           0 : }
    1966             : 
    1967             : template<typename T>
    1968         386 : static T CalculateTruncatedMedian(std::vector<T>& scores)
    1969             : {
    1970         386 :     size_t size = scores.size();
    1971         386 :     if (size == 0) {
    1972         352 :         return 0;
    1973             :     }
    1974             : 
    1975          34 :     std::sort(scores.begin(), scores.end());
    1976          34 :     if (size % 2 == 0) {
    1977           6 :         return (scores[size / 2 - 1] + scores[size / 2]) / 2;
    1978             :     } else {
    1979          28 :         return scores[size / 2];
    1980             :     }
    1981         386 : }
    1982             : 
    1983         197 : void CalculatePercentilesBySize(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES], std::vector<std::pair<CAmount, int64_t>>& scores, int64_t total_size)
    1984             : {
    1985         197 :     if (scores.empty()) {
    1986         176 :         return;
    1987             :     }
    1988             : 
    1989          21 :     std::sort(scores.begin(), scores.end());
    1990             : 
    1991             :     // 10th, 25th, 50th, 75th, and 90th percentile weight units.
    1992         105 :     const double weights[NUM_GETBLOCKSTATS_PERCENTILES] = {
    1993         105 :         total_size / 10.0, total_size / 4.0, total_size / 2.0, (total_size * 3.0) / 4.0, (total_size * 9.0) / 10.0
    1994             :     };
    1995             : 
    1996          21 :     int64_t next_percentile_index = 0;
    1997          21 :     int64_t cumulative_weight = 0;
    1998         273 :     for (const auto& element : scores) {
    1999         252 :         cumulative_weight += element.second;
    2000         357 :         while (next_percentile_index < NUM_GETBLOCKSTATS_PERCENTILES && cumulative_weight >= weights[next_percentile_index]) {
    2001         105 :             result[next_percentile_index] = element.first;
    2002         105 :             ++next_percentile_index;
    2003             :         }
    2004             :     }
    2005             : 
    2006             :     // Fill any remaining percentiles with the last value.
    2007          21 :     for (int64_t i = next_percentile_index; i < NUM_GETBLOCKSTATS_PERCENTILES; i++) {
    2008           0 :         result[i] = scores.back().first;
    2009           0 :     }
    2010         197 : }
    2011             : 
    2012             : template<typename T>
    2013         206 : static inline bool SetHasKeys(const std::set<T>& set) {return false;}
    2014             : template<typename T, typename Tk, typename... Args>
    2015        2598 : static inline bool SetHasKeys(const std::set<T>& set, const Tk& key, const Args&... args)
    2016             : {
    2017        2598 :     return (set.count(key) != 0) || SetHasKeys(set, args...);
    2018           0 : }
    2019             : 
    2020             : // outpoint (needed for the utxo index) + nHeight + fCoinBase
    2021             : static constexpr size_t PER_UTXO_OVERHEAD = sizeof(COutPoint) + sizeof(uint32_t) + sizeof(bool);
    2022             : 
    2023        6359 : static RPCHelpMan getblockstats()
    2024             : {
    2025       12718 :     return RPCHelpMan{"getblockstats",
    2026        6359 :                 "\nCompute per block statistics for a given window. All amounts are in duffs.\n"
    2027             :                 "It won't work for some heights with pruning.\n",
    2028       19077 :                 {
    2029        6359 :                     {"hash_or_height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The block hash or height of the target block", "", {"", "string or numeric"}},
    2030       12718 :                     {"stats", RPCArg::Type::ARR, RPCArg::DefaultHint{"all values"}, "Values to plot (see result below)",
    2031       19077 :                         {
    2032        6359 :                             {"height", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Selected statistic"},
    2033        6359 :                             {"time", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Selected statistic"},
    2034             :                         },
    2035        6359 :                         "stats"},
    2036             :                 },
    2037        6359 :                 RPCResult{
    2038        6359 :             RPCResult::Type::OBJ, "", "",
    2039      178052 :             {
    2040        6359 :                 {RPCResult::Type::NUM, "avgfee", /*optional=*/true, "Average fee in the block"},
    2041        6359 :                 {RPCResult::Type::NUM, "avgfeerate", /*optional=*/true, "Average feerate (in duffs per byte)"},
    2042        6359 :                 {RPCResult::Type::NUM, "avgtxsize", /*optional=*/true, "Average transaction size"},
    2043        6359 :                 {RPCResult::Type::STR_HEX, "blockhash", /*optional=*/true, "The block hash (to check for potential reorgs)"},
    2044       12718 :                 {RPCResult::Type::ARR_FIXED, "feerate_percentiles", /*optional=*/true, "Feerates at the 10th, 25th, 50th, 75th, and 90th percentile size unit (in duffs per byte)",
    2045       38154 :                 {
    2046        6359 :                     {RPCResult::Type::NUM, "10th_percentile_feerate", "The 10th percentile feerate"},
    2047        6359 :                     {RPCResult::Type::NUM, "25th_percentile_feerate", "The 25th percentile feerate"},
    2048        6359 :                     {RPCResult::Type::NUM, "50th_percentile_feerate", "The 50th percentile feerate"},
    2049        6359 :                     {RPCResult::Type::NUM, "75th_percentile_feerate", "The 75th percentile feerate"},
    2050        6359 :                     {RPCResult::Type::NUM, "90th_percentile_feerate", "The 90th percentile feerate"},
    2051             :                 }},
    2052        6359 :                 {RPCResult::Type::NUM, "height", /*optional=*/true, "The height of the block"},
    2053        6359 :                 {RPCResult::Type::NUM, "ins", /*optional=*/true, "The number of inputs (excluding coinbase)"},
    2054        6359 :                 {RPCResult::Type::NUM, "maxfee", /*optional=*/true, "Maximum fee in the block"},
    2055        6359 :                 {RPCResult::Type::NUM, "maxfeerate", /*optional=*/true, "Maximum feerate (in duffs per virtual byte)"},
    2056        6359 :                 {RPCResult::Type::NUM, "maxtxsize", /*optional=*/true, "Maximum transaction size"},
    2057        6359 :                 {RPCResult::Type::NUM, "medianfee", /*optional=*/true, "Truncated median fee in the block"},
    2058        6359 :                 {RPCResult::Type::NUM, "mediantime", /*optional=*/true, "The block median time past"},
    2059        6359 :                 {RPCResult::Type::NUM, "mediantxsize", /*optional=*/true, "Truncated median transaction size"},
    2060        6359 :                 {RPCResult::Type::NUM, "minfee", /*optional=*/true, "Minimum fee in the block"},
    2061        6359 :                 {RPCResult::Type::NUM, "minfeerate", /*optional=*/true, "Minimum feerate (in duffs per virtual byte)"},
    2062        6359 :                 {RPCResult::Type::NUM, "mintxsize", /*optional=*/true, "Minimum transaction size"},
    2063        6359 :                 {RPCResult::Type::NUM, "outs", /*optional=*/true, "The number of outputs"},
    2064        6359 :                 {RPCResult::Type::NUM, "subsidy", /*optional=*/true, "The block subsidy"},
    2065        6359 :                 {RPCResult::Type::NUM, "time", /*optional=*/true, "The block time"},
    2066        6359 :                 {RPCResult::Type::NUM, "total_out", /*optional=*/true, "Total amount in all outputs (excluding coinbase and thus reward [ie subsidy + totalfee])"},
    2067        6359 :                 {RPCResult::Type::NUM, "total_size", /*optional=*/true, "Total size of all non-coinbase transactions"},
    2068        6359 :                 {RPCResult::Type::NUM, "totalfee", /*optional=*/true, "The fee total"},
    2069        6359 :                 {RPCResult::Type::NUM, "txs", /*optional=*/true, "The number of transactions (including coinbase)"},
    2070        6359 :                 {RPCResult::Type::NUM, "utxo_increase", /*optional=*/true, "The increase/decrease in the number of unspent outputs  (not discounting op_return and similar)"},
    2071        6359 :                 {RPCResult::Type::NUM, "utxo_size_inc", /*optional=*/true, "The increase/decrease in size for the utxo index (not discounting op_return and similar)"},
    2072        6359 :                 {RPCResult::Type::NUM, "utxo_increase_actual", /*optional=*/true, "The increase/decrease in the number of unspent outputs, not counting unspendables"},
    2073        6359 :                 {RPCResult::Type::NUM, "utxo_size_inc_actual", /*optional=*/true, "The increase/decrease in size for the utxo index, not counting unspendables"},
    2074             :             }},
    2075        6359 :                 RPCExamples{
    2076       12718 :                     HelpExampleCli("getblockstats", R"('"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09"' '["minfeerate","avgfeerate"]')") +
    2077       12718 :                     HelpExampleCli("getblockstats", R"(1000 '["minfeerate","avgfeerate"]')") +
    2078       12718 :                     HelpExampleRpc("getblockstats", R"("00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09", ["minfeerate","avgfeerate"])") +
    2079        6359 :                     HelpExampleRpc("getblockstats", R"(1000, ["minfeerate","avgfeerate"])")
    2080             :                 },
    2081        6564 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    2082             : {
    2083         205 :     if (g_txindex) {
    2084         199 :         g_txindex->BlockUntilSyncedToCurrentChain();
    2085         199 :     }
    2086             : 
    2087         205 :     ChainstateManager& chainman = EnsureAnyChainman(request.context);
    2088         205 :     const CBlockIndex& pindex{*CHECK_NONFATAL(ParseHashOrHeight(request.params[0], chainman))};
    2089             : 
    2090         205 :     std::set<std::string> stats;
    2091         205 :     if (!request.params[1].isNull()) {
    2092         174 :         const UniValue stats_univalue = request.params[1].get_array();
    2093         360 :         for (unsigned int i = 0; i < stats_univalue.size(); i++) {
    2094         186 :             const std::string stat = stats_univalue[i].get_str();
    2095         186 :             stats.insert(stat);
    2096         186 :         }
    2097         174 :     }
    2098             : 
    2099         193 :     const CBlock& block = GetBlockChecked(chainman.m_blockman, &pindex);
    2100         193 :     const CBlockUndo& blockUndo = GetUndoChecked(chainman.m_blockman, &pindex);
    2101             : 
    2102         193 :     const bool do_all = stats.size() == 0; // Calculate everything if nothing selected (default)
    2103         193 :     const bool do_mediantxsize = do_all || stats.count("mediantxsize") != 0;
    2104         193 :     const bool do_medianfee = do_all || stats.count("medianfee") != 0;
    2105         193 :     const bool do_feerate_percentiles = do_all || stats.count("feerate_percentiles") != 0;
    2106         355 :     const bool loop_inputs = do_all || do_medianfee || do_feerate_percentiles ||
    2107         162 :         SetHasKeys(stats, "utxo_increase", "utxo_increase_actual", "utxo_size_inc", "utxo_size_inc_actual", "totalfee", "avgfee", "avgfeerate", "minfee", "maxfee", "minfeerate", "maxfeerate");
    2108         193 :     const bool loop_outputs = do_all || loop_inputs || stats.count("total_out");
    2109         361 :     const bool do_calculate_size = do_all || do_mediantxsize ||
    2110         168 :         SetHasKeys(stats, "total_size", "avgtxsize", "mintxsize", "maxtxsize", "avgfeerate", "feerate_percentiles", "minfeerate", "maxfeerate");
    2111             : 
    2112         193 :     CAmount maxfee = 0;
    2113         193 :     CAmount maxfeerate = 0;
    2114         193 :     CAmount minfee = MAX_MONEY;
    2115         193 :     CAmount minfeerate = MAX_MONEY;
    2116         193 :     CAmount total_out = 0;
    2117         193 :     CAmount totalfee = 0;
    2118         193 :     int64_t inputs = 0;
    2119         193 :     int64_t maxtxsize = 0;
    2120         193 :     int64_t mintxsize = MaxBlockSize();
    2121         193 :     int64_t outputs = 0;
    2122         193 :     int64_t total_size = 0;
    2123         193 :     int64_t utxos = 0;
    2124         193 :     int64_t utxo_size_inc = 0;
    2125         193 :     int64_t utxo_size_inc_actual = 0;
    2126         193 :     std::vector<CAmount> fee_array;
    2127         193 :     std::vector<std::pair<CAmount, int64_t>> feerate_array;
    2128         193 :     std::vector<int64_t> txsize_array;
    2129             : 
    2130         630 :     for (size_t i = 0; i < block.vtx.size(); ++i) {
    2131         437 :         const auto& tx = block.vtx.at(i);
    2132         437 :         outputs += tx->vout.size();
    2133             : 
    2134         437 :         CAmount tx_total_out = 0;
    2135         437 :         if (loop_outputs) {
    2136         646 :             for (const CTxOut& out : tx->vout) {
    2137         393 :                 tx_total_out += out.nValue;
    2138             : 
    2139         393 :                 size_t out_size = GetSerializeSize(out, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD;
    2140         393 :                 utxo_size_inc += out_size;
    2141             : 
    2142             :                 // The Genesis block and the repeated BIP30 block coinbases don't change the UTXO
    2143             :                 // set counts, so they have to be excluded from the statistics
    2144         393 :                 if (pindex.nHeight == 0 || (IsBIP30Repeat(pindex) && tx->IsCoinBase())) continue;
    2145             :                 // Skip unspendable outputs since they are not included in the UTXO set
    2146         391 :                 if (out.scriptPubKey.IsUnspendable()) continue;
    2147             : 
    2148         391 :                 ++utxos;
    2149         391 :                 utxo_size_inc_actual += out_size;
    2150             :             }
    2151         253 :         }
    2152             : 
    2153         437 :         if (tx->IsCoinBase()) {
    2154         193 :             continue;
    2155             :         }
    2156             : 
    2157         244 :         inputs += tx->vin.size(); // Don't count coinbase's fake input
    2158         244 :         total_out += tx_total_out; // Don't count coinbase reward
    2159             : 
    2160         244 :         int64_t tx_size = 0;
    2161         244 :         if (do_calculate_size) {
    2162             : 
    2163         100 :             tx_size = tx->GetTotalSize();
    2164         100 :             if (do_mediantxsize) {
    2165          36 :                 txsize_array.push_back(tx_size);
    2166          36 :             }
    2167         100 :             maxtxsize = std::max(maxtxsize, tx_size);
    2168         100 :             mintxsize = std::min(mintxsize, tx_size);
    2169         100 :             total_size += tx_size;
    2170         100 :         }
    2171             : 
    2172         244 :         if (loop_inputs) {
    2173         132 :             CAmount tx_total_in = 0;
    2174         132 :             const auto& txundo = blockUndo.vtxundo.at(i - 1);
    2175         264 :             for (const Coin& coin: txundo.vprevout) {
    2176         132 :                 const CTxOut& prevoutput = coin.out;
    2177             : 
    2178         132 :                 tx_total_in += prevoutput.nValue;
    2179         132 :                 size_t prevout_size = GetSerializeSize(prevoutput, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD;
    2180         132 :                 utxo_size_inc -= prevout_size;
    2181         132 :                 utxo_size_inc_actual -= prevout_size;
    2182             :             }
    2183             : 
    2184         132 :             CAmount txfee = tx_total_in - tx_total_out;
    2185             : 
    2186         132 :             if (tx->IsPlatformTransfer()) {
    2187           3 :                 auto payload = GetTxPayload<CAssetUnlockPayload>(*tx);
    2188           3 :                 CHECK_NONFATAL(payload);
    2189           3 :                 txfee = payload->getFee();
    2190           3 :             }
    2191             : 
    2192         132 :             CHECK_NONFATAL(MoneyRange(txfee));
    2193         132 :             if (do_medianfee) {
    2194          36 :                 fee_array.push_back(txfee);
    2195          36 :             }
    2196         132 :             maxfee = std::max(maxfee, txfee);
    2197         132 :             minfee = std::min(minfee, txfee);
    2198         132 :             totalfee += txfee;
    2199             : 
    2200         132 :             CAmount feerate = tx_size ? txfee / tx_size : 0;
    2201         132 :             if (do_feerate_percentiles) {
    2202          36 :                 feerate_array.emplace_back(feerate, tx_size);
    2203          36 :             }
    2204         132 :             maxfeerate = std::max(maxfeerate, feerate);
    2205         132 :             minfeerate = std::min(minfeerate, feerate);
    2206         132 :         }
    2207         244 :     }
    2208             : 
    2209         193 :     CAmount feerate_percentiles[NUM_GETBLOCKSTATS_PERCENTILES] = { 0 };
    2210         193 :     CalculatePercentilesBySize(feerate_percentiles, feerate_array, total_size);
    2211             : 
    2212         193 :     UniValue feerates_res(UniValue::VARR);
    2213        1158 :     for (int64_t i = 0; i < NUM_GETBLOCKSTATS_PERCENTILES; i++) {
    2214         965 :         feerates_res.push_back(feerate_percentiles[i]);
    2215         965 :     }
    2216             : 
    2217         193 :     UniValue ret_all(UniValue::VOBJ);
    2218         193 :     ret_all.pushKV("avgfee", (block.vtx.size() > 1) ? totalfee / (block.vtx.size() - 1) : 0);
    2219         193 :     ret_all.pushKV("avgfeerate", total_size ? totalfee / total_size : 0); // Unit: sat/byte
    2220         193 :     ret_all.pushKV("avgtxsize", (block.vtx.size() > 1) ? total_size / (block.vtx.size() - 1) : 0);
    2221         193 :     ret_all.pushKV("blockhash", pindex.GetBlockHash().GetHex());
    2222         193 :     ret_all.pushKV("feerate_percentiles", feerates_res);
    2223         193 :     ret_all.pushKV("height", (int64_t)pindex.nHeight);
    2224         193 :     ret_all.pushKV("ins", inputs);
    2225         193 :     ret_all.pushKV("maxfee", maxfee);
    2226         193 :     ret_all.pushKV("maxfeerate", maxfeerate);
    2227         193 :     ret_all.pushKV("maxtxsize", maxtxsize);
    2228         193 :     ret_all.pushKV("medianfee", CalculateTruncatedMedian(fee_array));
    2229         193 :     ret_all.pushKV("mediantime", pindex.GetMedianTimePast());
    2230         193 :     ret_all.pushKV("mediantxsize", CalculateTruncatedMedian(txsize_array));
    2231         193 :     ret_all.pushKV("minfee", (minfee == MAX_MONEY) ? 0 : minfee);
    2232         193 :     ret_all.pushKV("minfeerate", (minfeerate == MAX_MONEY) ? 0 : minfeerate);
    2233         193 :     ret_all.pushKV("mintxsize", mintxsize == MaxBlockSize() ? 0 : mintxsize);
    2234         193 :     ret_all.pushKV("outs", outputs);
    2235         193 :     ret_all.pushKV("subsidy", GetBlockSubsidy(&pindex, Params().GetConsensus()));
    2236         193 :     ret_all.pushKV("time", pindex.GetBlockTime());
    2237         193 :     ret_all.pushKV("total_out", total_out);
    2238         193 :     ret_all.pushKV("total_size", total_size);
    2239         193 :     ret_all.pushKV("totalfee", totalfee);
    2240         193 :     ret_all.pushKV("txs", (int64_t)block.vtx.size());
    2241         193 :     ret_all.pushKV("utxo_increase", outputs - inputs);
    2242         193 :     ret_all.pushKV("utxo_size_inc", utxo_size_inc);
    2243         193 :     ret_all.pushKV("utxo_increase_actual", utxos - inputs);
    2244         193 :     ret_all.pushKV("utxo_size_inc_actual", utxo_size_inc_actual);
    2245             : 
    2246         193 :     if (do_all) {
    2247          19 :         return ret_all;
    2248             :     }
    2249             : 
    2250         174 :     UniValue ret(UniValue::VOBJ);
    2251         340 :     for (const std::string& stat : stats) {
    2252         176 :         const UniValue& value = ret_all[stat];
    2253         176 :         if (value.isNull()) {
    2254          10 :             throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid selected statistic '%s'", stat));
    2255             :         }
    2256         166 :         ret.pushKV(stat, value);
    2257             :     }
    2258         164 :     return ret;
    2259         367 : },
    2260             :     };
    2261           0 : }
    2262             : 
    2263        6162 : static RPCHelpMan getspecialtxes()
    2264             : {
    2265       12324 :     return RPCHelpMan{"getspecialtxes",
    2266        6162 :         "Returns an array of special transactions found in the specified block\n"
    2267             :         "\nIf verbosity is 0, returns tx hash for each transaction.\n"
    2268             :         "If verbosity is 1, returns hex-encoded data for each transaction.\n"
    2269             :         "If verbosity is 2, returns an Object with information for each transaction.\n",
    2270       36972 :         {
    2271        6162 :             {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
    2272        6162 :             {"type", RPCArg::Type::NUM, RPCArg::Default{-1}, "Filter special txes by type, -1 means all types"},
    2273        6162 :             {"count", RPCArg::Type::NUM, RPCArg::Default{10}, "The number of transactions to return"},
    2274        6162 :             {"skip", RPCArg::Type::NUM, RPCArg::Default{0}, "The number of transactions to skip"},
    2275        6162 :             {"verbosity", RPCArg::Type::NUM, RPCArg::Default{0}, "0 for hashes, 1 for hex-encoded data, and 2 for json object"},
    2276             :         },
    2277       24648 :         {
    2278       12324 :             RPCResult{"for verbosity = 0",
    2279        6162 :                 RPCResult::Type::ARR, "", "",
    2280        6162 :                     {{RPCResult::Type::STR_HEX, "", "The transaction id"}}},
    2281       12324 :             RPCResult{"for verbosity = 1",
    2282        6162 :                 RPCResult::Type::ARR, "", "",
    2283        6162 :                     {{RPCResult::Type::STR_HEX, "data", "A string that is serialized, hex-encoded data for the transaction"}}},
    2284       12324 :             RPCResult{"for verbosity = 2",
    2285        6162 :                 RPCResult::Type::ARR, "", "",
    2286        6162 :                     {{RPCResult::Type::ELISION, "", "The transactions in the format of the getrawtransaction RPC"}}},
    2287             :         },
    2288        6162 :         RPCExamples{
    2289        6162 :             HelpExampleCli("getspecialtxes", "\"00000000000fd08c2fb661d2fcb0d49abb3a91e5f27082ce64feed3b4dede2e2\"")
    2290        6162 :     + HelpExampleRpc("getspecialtxes", "\"00000000000fd08c2fb661d2fcb0d49abb3a91e5f27082ce64feed3b4dede2e2\"")
    2291             :         },
    2292        6168 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    2293             : {
    2294           6 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
    2295             : 
    2296           6 :     ChainstateManager& chainman = EnsureChainman(node);
    2297           6 :     LOCK(cs_main);
    2298             : 
    2299           6 :     const CTxMemPool& mempool = EnsureMemPool(node);
    2300           6 :     const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
    2301           6 :     CHECK_NONFATAL(node.chainlocks);
    2302             : 
    2303           6 :     const uint256 blockhash(ParseHashV(request.params[0], "blockhash"));
    2304             : 
    2305           6 :     int nTxType = -1;
    2306           6 :     if (!request.params[1].isNull()) {
    2307           6 :         nTxType = request.params[1].getInt<int>();
    2308           6 :     }
    2309             : 
    2310           6 :     int nCount = 10;
    2311           6 :     if (!request.params[2].isNull()) {
    2312           6 :         nCount = request.params[2].getInt<int>();
    2313           6 :         if (nCount < 0)
    2314           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
    2315           6 :     }
    2316             : 
    2317           6 :     int nSkip = 0;
    2318           6 :     if (!request.params[3].isNull()) {
    2319           6 :         nSkip = request.params[3].getInt<int>();
    2320           6 :         if (nSkip < 0)
    2321           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative skip");
    2322           6 :     }
    2323             : 
    2324           6 :     int nVerbosity = 0;
    2325           6 :     if (!request.params[4].isNull()) {
    2326           6 :         nVerbosity = request.params[4].getInt<int>();
    2327           6 :         if (nVerbosity < 0 || nVerbosity > 2) {
    2328           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Verbosity must be in range 0..2");
    2329             :         }
    2330           6 :     }
    2331             : 
    2332           6 :     const CBlockIndex* pblockindex = chainman.m_blockman.LookupBlockIndex(blockhash);
    2333           6 :     if (!pblockindex) {
    2334           0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
    2335             :     }
    2336             : 
    2337           6 :     const CBlock block = GetBlockChecked(chainman.m_blockman, pblockindex);
    2338             : 
    2339           6 :     int nTxNum = 0;
    2340           6 :     UniValue result(UniValue::VARR);
    2341             : 
    2342          12 :     for(const auto& tx : block.vtx)
    2343             :     {
    2344          12 :         if (!tx->HasExtraPayloadField()                   // ensure it's in fact a special tx
    2345           6 :             || (nTxType != -1 && tx->nType != nTxType)) { // ensure special tx type matches filter, if given
    2346           0 :             continue;
    2347             :         }
    2348             : 
    2349           6 :         nTxNum++;
    2350           6 :         if (nTxNum <= nSkip) continue;
    2351           6 :         if (nTxNum > nSkip + nCount) break;
    2352             : 
    2353           6 :         switch (nVerbosity)
    2354             :         {
    2355           0 :             case 0 : result.push_back(tx->GetHash().GetHex()); break;
    2356           0 :             case 1 : result.push_back(EncodeHexTx(*tx)); break;
    2357             :             case 2 :
    2358             :                 {
    2359           6 :                     UniValue objTx(UniValue::VOBJ);
    2360           6 :                     TxToJSON(*tx, blockhash, mempool, chainman.ActiveChainstate(), *node.chainlocks, *llmq_ctx.isman, objTx);
    2361           6 :                     result.push_back(objTx);
    2362             :                     break;
    2363           6 :                 }
    2364           0 :             default : throw JSONRPCError(RPC_INTERNAL_ERROR, "Unsupported verbosity");
    2365             :         }
    2366             :     }
    2367           6 :     return result;
    2368           6 : },
    2369             :     };
    2370           0 : }
    2371             : 
    2372             : namespace {
    2373             : //! Search for a given set of pubkey scripts
    2374        1596 : bool FindScriptPubKey(std::atomic<int>& scan_progress, const std::atomic<bool>& should_abort, int64_t& count, CCoinsViewCursor* cursor, const std::set<CScript>& needles, std::map<COutPoint, Coin>& out_results, std::function<void()>& interruption_point)
    2375             : {
    2376        1596 :     scan_progress = 0;
    2377        1596 :     count = 0;
    2378      256318 :     while (cursor->Valid()) {
    2379      254722 :         COutPoint key;
    2380      254722 :         Coin coin;
    2381      254722 :         if (!cursor->GetKey(key) || !cursor->GetValue(coin)) return false;
    2382      254722 :         if (++count % 8192 == 0) {
    2383           0 :             interruption_point();
    2384           0 :             if (should_abort) {
    2385             :                 // allow to abort the scan via the abort reference
    2386           0 :                 return false;
    2387             :             }
    2388           0 :         }
    2389      254722 :         if (count % 256 == 0) {
    2390             :             // update progress reference every 256 item
    2391         237 :             uint32_t high = 0x100 * *key.hash.begin() + *(key.hash.begin() + 1);
    2392         237 :             scan_progress = (int)(high * 100.0 / 65536.0 + 0.5);
    2393         237 :         }
    2394      254722 :         if (needles.count(coin.out.scriptPubKey)) {
    2395      196016 :             out_results.emplace(key, coin);
    2396      196016 :         }
    2397      254722 :         cursor->Next();
    2398      254722 :     }
    2399        1596 :     scan_progress = 100;
    2400        1596 :     return true;
    2401        1596 : }
    2402             : } // namespace
    2403             : 
    2404             : /** RAII object to prevent concurrency issue when scanning the txout set */
    2405             : static std::atomic<int> g_scan_progress;
    2406             : static std::atomic<bool> g_scan_in_progress;
    2407             : static std::atomic<bool> g_should_abort_scan;
    2408             : class CoinsViewScanReserver
    2409             : {
    2410             : private:
    2411        1612 :     bool m_could_reserve{false};
    2412             : public:
    2413        4836 :     explicit CoinsViewScanReserver() = default;
    2414             : 
    2415        1612 :     bool reserve() {
    2416        1612 :         CHECK_NONFATAL(!m_could_reserve);
    2417        1612 :         if (g_scan_in_progress.exchange(true)) {
    2418           0 :             return false;
    2419             :         }
    2420        1612 :         CHECK_NONFATAL(g_scan_progress == 0);
    2421        1612 :         m_could_reserve = true;
    2422        1612 :         return true;
    2423        1612 :     }
    2424             : 
    2425        3224 :     ~CoinsViewScanReserver() {
    2426        1612 :         if (m_could_reserve) {
    2427        1612 :             g_scan_in_progress = false;
    2428        1612 :             g_scan_progress = 0;
    2429        1612 :         }
    2430        3224 :     }
    2431             : };
    2432             : 
    2433        7772 : static RPCHelpMan scantxoutset()
    2434             : {
    2435             :     // scriptPubKey corresponding to mainnet address XcJSEb79KEeNbwMU7eE27aL5dhDwvjgBuH
    2436        7772 :     const std::string EXAMPLE_DESCRIPTOR_RAW = "raw(76a91411b366edfc0a8b66feebae5c2e25a7b6a5d1cf3188ac)#fm24fxxy";
    2437             : 
    2438       15544 :     return RPCHelpMan{"scantxoutset",
    2439        7772 :         "\nScans the unspent transaction output set for entries that match certain output descriptors.\n"
    2440             :         "Examples of output descriptors are:\n"
    2441             :         "    addr(<address>)                      Outputs whose scriptPubKey corresponds to the specified address (does not include P2PK)\n"
    2442             :         "    raw(<hex script>)                    Outputs whose scriptPubKey equals the specified hex scripts\n"
    2443             :         "    combo(<pubkey>)                      P2PK and P2PKH outputs for the given pubkey\n"
    2444             :         "    pkh(<pubkey>)                        P2PKH outputs for the given pubkey\n"
    2445             :         "    sh(multi(<n>,<pubkey>,<pubkey>,...)) P2SH-multisig outputs for the given threshold and pubkeys\n"
    2446             :         "\nIn the above, <pubkey> either refers to a fixed public key in hexadecimal notation, or to an xpub/xprv optionally followed by one\n"
    2447             :         "or more path elements separated by \"/\", and optionally ending in \"/*\" (unhardened), or \"/*'\" or \"/*h\" (hardened) to specify all\n"
    2448             :         "unhardened or hardened child keys.\n"
    2449             :         "In the latter case, a range needs to be specified by below if different from 1000.\n"
    2450             :         "For more information on output descriptors, see the documentation in the doc/descriptors.md file.\n",
    2451       23316 :         {
    2452        7772 :             {"action", RPCArg::Type::STR, RPCArg::Optional::NO, "The action to execute\n"
    2453             :     "                                      \"start\" for starting a scan\n"
    2454             :     "                                      \"abort\" for aborting the current scan (returns true when abort was successful)\n"
    2455             :     "                                      \"status\" for progress report (in %) of the current scan"},
    2456       15544 :             {"scanobjects", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, "Array of scan objects. Required for \"start\" action\n"
    2457             :     "                                  Every scan object is either a string descriptor or an object:",
    2458       23316 :                 {
    2459        7772 :                     {"descriptor", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "An output descriptor"},
    2460       15544 :                     {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "An object with output descriptor and metadata",
    2461       23316 :                         {
    2462        7772 :                             {"desc", RPCArg::Type::STR, RPCArg::Optional::NO, "An output descriptor"},
    2463        7772 :                             {"range", RPCArg::Type::RANGE, RPCArg::Default{1000}, "The range of HD chain indexes to explore (either end or [begin,end])"},
    2464             :                         },
    2465             :                     },
    2466             :                 },
    2467        7772 :                 "[scanobjects,...]"},
    2468             :         },
    2469       38860 :         {
    2470       54404 :             RPCResult{"when action=='start'; only returns after scan completes", RPCResult::Type::OBJ, "", "", {
    2471        7772 :                 {RPCResult::Type::BOOL, "success", "Whether the scan was completed"},
    2472        7772 :                 {RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs scanned"},
    2473        7772 :                 {RPCResult::Type::NUM, "height", "The current block height (index)"},
    2474        7772 :                 {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"},
    2475       15544 :                 {RPCResult::Type::ARR, "unspents", "",
    2476       15544 :                 {
    2477       15544 :                     {RPCResult::Type::OBJ, "", "",
    2478       62176 :                     {
    2479        7772 :                         {RPCResult::Type::STR_HEX, "txid", "The transaction id"},
    2480        7772 :                         {RPCResult::Type::NUM, "vout", "The vout value"},
    2481        7772 :                         {RPCResult::Type::STR_HEX, "scriptPubKey", "The script key"},
    2482        7772 :                         {RPCResult::Type::STR, "desc", "A specialized descriptor for the matched scriptPubKey"},
    2483        7772 :                         {RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " of the unspent output"},
    2484        7772 :                         {RPCResult::Type::BOOL, "coinbase", "Whether this is a coinbase output"},
    2485        7772 :                         {RPCResult::Type::NUM, "height", "Height of the unspent transaction output"},
    2486             :                     }},
    2487             :                 }},
    2488        7772 :                 {RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount of all found unspent outputs in " + CURRENCY_UNIT},
    2489             :             }},
    2490        7772 :             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"},
    2491       15544 :             RPCResult{"when action=='status' and a scan is currently in progress", RPCResult::Type::OBJ, "", "",
    2492       15544 :             {
    2493        7772 :                 {RPCResult::Type::NUM, "progress", "Approximate percent complete"},
    2494             :             }},
    2495        7772 :             RPCResult{"when action=='status' and no scan is in progress - possibly already completed", RPCResult::Type::NONE, "", ""},
    2496             :         },
    2497        7772 :         RPCExamples{
    2498       15544 :             HelpExampleCli("scantxoutset", "start \'[\"" + EXAMPLE_DESCRIPTOR_RAW + "\"]\'") +
    2499       15544 :             HelpExampleCli("scantxoutset", "status") +
    2500       15544 :             HelpExampleCli("scantxoutset", "abort") +
    2501       15544 :             HelpExampleRpc("scantxoutset", "\"start\", [\"" + EXAMPLE_DESCRIPTOR_RAW + "\"]") +
    2502       15544 :             HelpExampleRpc("scantxoutset", "\"status\"") +
    2503        7772 :             HelpExampleRpc("scantxoutset", "\"abort\"")
    2504             :         },
    2505        9386 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    2506             : {
    2507        1628 :     RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR});
    2508             : 
    2509        1614 :     UniValue result(UniValue::VOBJ);
    2510        1614 :     if (request.params[0].get_str() == "status") {
    2511           2 :         CoinsViewScanReserver reserver;
    2512           2 :         if (reserver.reserve()) {
    2513             :             // no scan in progress
    2514           2 :             return UniValue::VNULL;
    2515             :         }
    2516           0 :         result.pushKV("progress", g_scan_progress.load());
    2517           0 :         return result;
    2518        1614 :     } else if (request.params[0].get_str() == "abort") {
    2519           2 :         CoinsViewScanReserver reserver;
    2520           2 :         if (reserver.reserve()) {
    2521             :             // reserve was possible which means no scan was running
    2522           2 :             return false;
    2523             :         }
    2524             :         // set the abort flag
    2525           0 :         g_should_abort_scan = true;
    2526           0 :         return true;
    2527        1612 :     } else if (request.params[0].get_str() == "start") {
    2528        1608 :         CoinsViewScanReserver reserver;
    2529        1608 :         if (!reserver.reserve()) {
    2530           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan already in progress, use action \"abort\" or \"status\"");
    2531             :         }
    2532             : 
    2533        1608 :         if (request.params.size() < 2) {
    2534           2 :             throw JSONRPCError(RPC_MISC_ERROR, "scanobjects argument is required for the start action");
    2535             :         }
    2536             : 
    2537        1606 :         std::set<CScript> needles;
    2538        1606 :         std::map<CScript, std::string> descriptors;
    2539        1606 :         CAmount total_in = 0;
    2540             : 
    2541             :         // loop through the scan objects
    2542        3220 :         for (const UniValue& scanobject : request.params[1].get_array().getValues()) {
    2543        1624 :             FlatSigningProvider provider;
    2544        1624 :             auto scripts = EvalDescriptorStringOrObject(scanobject, provider);
    2545      111324 :             for (CScript& script : scripts) {
    2546      109710 :                 std::string inferred = InferDescriptor(script, provider)->ToString();
    2547      109710 :                 needles.emplace(script);
    2548      109710 :                 descriptors.emplace(std::move(script), std::move(inferred));
    2549      109710 :             }
    2550        1624 :         }
    2551             : 
    2552             :         // Scan the unspent transaction output set for inputs
    2553        1596 :         UniValue unspents(UniValue::VARR);
    2554        1596 :         std::vector<CTxOut> input_txos;
    2555        1596 :         std::map<COutPoint, Coin> coins;
    2556        1596 :         g_should_abort_scan = false;
    2557        1596 :         int64_t count = 0;
    2558        1596 :         std::unique_ptr<CCoinsViewCursor> pcursor;
    2559             :         const CBlockIndex* tip;
    2560        1596 :         NodeContext& node = EnsureAnyNodeContext(request.context);
    2561             :         {
    2562        1596 :             ChainstateManager& chainman = EnsureChainman(node);
    2563        1596 :             LOCK(cs_main);
    2564        1596 :             CChainState& active_chainstate = chainman.ActiveChainstate();
    2565        1596 :             active_chainstate.ForceFlushStateToDisk();
    2566        1596 :             pcursor = CHECK_NONFATAL(active_chainstate.CoinsDB().Cursor());
    2567        1596 :             tip = CHECK_NONFATAL(active_chainstate.m_chain.Tip());
    2568        1596 :         }
    2569        1596 :         bool res = FindScriptPubKey(g_scan_progress, g_should_abort_scan, count, pcursor.get(), needles, coins, node.rpc_interruption_point);
    2570        1596 :         result.pushKV("success", res);
    2571        1596 :         result.pushKV("txouts", count);
    2572        1596 :         result.pushKV("height", tip->nHeight);
    2573        1596 :         result.pushKV("bestblock", tip->GetBlockHash().GetHex());
    2574             : 
    2575      197612 :         for (const auto& it : coins) {
    2576      196016 :             const COutPoint& outpoint = it.first;
    2577      196016 :             const Coin& coin = it.second;
    2578      196016 :             const CTxOut& txo = coin.out;
    2579      196016 :             input_txos.push_back(txo);
    2580      196016 :             total_in += txo.nValue;
    2581             : 
    2582      196016 :             UniValue unspent(UniValue::VOBJ);
    2583      196016 :             unspent.pushKV("txid", outpoint.hash.GetHex());
    2584      196016 :             unspent.pushKV("vout", (int32_t)outpoint.n);
    2585      196016 :             unspent.pushKV("scriptPubKey", HexStr(txo.scriptPubKey));
    2586      196016 :             unspent.pushKV("desc", descriptors[txo.scriptPubKey]);
    2587      196016 :             unspent.pushKV("amount", ValueFromAmount(txo.nValue));
    2588      196016 :             unspent.pushKV("coinbase", coin.IsCoinBase());
    2589      196016 :             unspent.pushKV("height", (int32_t)coin.nHeight);
    2590             : 
    2591      196016 :             unspents.push_back(unspent);
    2592      196016 :         }
    2593        1596 :         result.pushKV("unspents", unspents);
    2594        1596 :         result.pushKV("total_amount", ValueFromAmount(total_in));
    2595        1608 :     } else {
    2596           2 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid command");
    2597             :     }
    2598        1596 :     return result;
    2599        1628 : },
    2600             :     };
    2601        7772 : }
    2602             : 
    2603        6194 : static RPCHelpMan getblockfilter()
    2604             : {
    2605       12388 :     return RPCHelpMan{"getblockfilter",
    2606        6194 :                 "\nRetrieve a BIP 157 content filter for a particular block.\n",
    2607       18582 :                 {
    2608        6194 :                     {"blockhash", RPCArg::Type::STR, RPCArg::Optional::NO, "The hash of the block"},
    2609        6194 :                     {"filtertype", RPCArg::Type::STR, RPCArg::Default{BlockFilterTypeName(BlockFilterType::BASIC_FILTER)}, "The type name of the filter"},
    2610             :                 },
    2611        6194 :                 RPCResult{
    2612        6194 :                     RPCResult::Type::OBJ, "", "",
    2613       18582 :                     {
    2614        6194 :                         {RPCResult::Type::STR_HEX, "filter", "the hex-encoded filter data"},
    2615        6194 :                         {RPCResult::Type::STR_HEX, "header", "the hex-encoded filter header"},
    2616             :                     }},
    2617        6194 :                 RPCExamples{
    2618       12388 :                     HelpExampleCli("getblockfilter", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" \"basic\"")+
    2619        6194 :                     HelpExampleRpc("getblockfilter", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\", \"basic\"")
    2620             :                 },
    2621        6232 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    2622             : {
    2623          44 :     uint256 block_hash(ParseHashV(request.params[0], "blockhash"));
    2624          38 :     std::string filtertype_name = BlockFilterTypeName(BlockFilterType::BASIC_FILTER);
    2625          38 :     if (!request.params[1].isNull()) {
    2626          36 :         filtertype_name = request.params[1].get_str();
    2627          36 :     }
    2628             : 
    2629             :     BlockFilterType filtertype;
    2630          38 :     if (!BlockFilterTypeByName(filtertype_name, filtertype)) {
    2631           2 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unknown filtertype");
    2632             :     }
    2633             : 
    2634          36 :     BlockFilterIndex* index = GetBlockFilterIndex(filtertype);
    2635          36 :     if (!index) {
    2636           2 :         throw JSONRPCError(RPC_MISC_ERROR, "Index is not enabled for filtertype " + filtertype_name);
    2637             :     }
    2638             : 
    2639             :     const CBlockIndex* block_index;
    2640             :     bool block_was_connected;
    2641             :     {
    2642          34 :         ChainstateManager& chainman = EnsureAnyChainman(request.context);
    2643          34 :         LOCK(cs_main);
    2644          34 :         block_index = chainman.m_blockman.LookupBlockIndex(block_hash);
    2645          34 :         if (!block_index) {
    2646           2 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
    2647             :         }
    2648          32 :         block_was_connected = block_index->IsValid(BLOCK_VALID_SCRIPTS);
    2649          34 :     }
    2650             : 
    2651          32 :     bool index_ready = index->BlockUntilSyncedToCurrentChain();
    2652             : 
    2653          32 :     BlockFilter filter;
    2654          32 :     uint256 filter_header;
    2655          64 :     if (!index->LookupFilter(block_index, filter) ||
    2656          32 :         !index->LookupFilterHeader(block_index, filter_header)) {
    2657             :         int err_code;
    2658           0 :         std::string errmsg = "Filter not found.";
    2659             : 
    2660           0 :         if (!block_was_connected) {
    2661           0 :             err_code = RPC_INVALID_ADDRESS_OR_KEY;
    2662           0 :             errmsg += " Block was not connected to active chain.";
    2663           0 :         } else if (!index_ready) {
    2664           0 :             err_code = RPC_MISC_ERROR;
    2665           0 :             errmsg += " Block filters are still in the process of being indexed.";
    2666           0 :         } else {
    2667           0 :             err_code = RPC_INTERNAL_ERROR;
    2668           0 :             errmsg += " This error is unexpected and indicates index corruption.";
    2669             :         }
    2670             : 
    2671           0 :         throw JSONRPCError(err_code, errmsg);
    2672           0 :     }
    2673             : 
    2674          32 :     UniValue ret(UniValue::VOBJ);
    2675          32 :     ret.pushKV("filter", HexStr(filter.GetEncodedFilter()));
    2676          32 :     ret.pushKV("header", filter_header.GetHex());
    2677          32 :     return ret;
    2678          44 : },
    2679             :     };
    2680           0 : }
    2681             : 
    2682             : /**
    2683             :  * Serialize the UTXO set to a file for loading elsewhere.
    2684             :  *
    2685             :  * @see SnapshotMetadata
    2686             :  */
    2687        6146 : static RPCHelpMan dumptxoutset()
    2688             : {
    2689        6146 :     return RPCHelpMan{
    2690        6146 :         "dumptxoutset",
    2691        6146 :         "Write the serialized UTXO set to disk.",
    2692       12292 :         {
    2693        6146 :             {"path", RPCArg::Type::STR, RPCArg::Optional::NO, "Path to the output file. If relative, will be prefixed by datadir."},
    2694             :         },
    2695        6146 :         RPCResult{
    2696        6146 :             RPCResult::Type::OBJ, "", "",
    2697       43022 :                 {
    2698        6146 :                     {RPCResult::Type::NUM, "coins_written", "the number of coins written in the snapshot"},
    2699        6146 :                     {RPCResult::Type::STR_HEX, "base_hash", "the hash of the base of the snapshot"},
    2700        6146 :                     {RPCResult::Type::NUM, "base_height", "the height of the base of the snapshot"},
    2701        6146 :                     {RPCResult::Type::STR, "path", "the absolute path that the snapshot was written to"},
    2702        6146 :                     {RPCResult::Type::STR_HEX, "txoutset_hash", "the hash of the UTXO set contents"},
    2703        6146 :                     {RPCResult::Type::NUM, "nchaintx", "the number of transactions in the chain up to and including the base block"},
    2704             :                 }
    2705             :         },
    2706        6146 :         RPCExamples{
    2707        6146 :             HelpExampleCli("dumptxoutset", "utxo.dat")
    2708             :         },
    2709        6152 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    2710             : {
    2711           6 :     const ArgsManager& args{EnsureAnyArgsman(request.context)};
    2712          10 :     const fs::path path = fsbridge::AbsPathJoin(args.GetDataDirNet(), fs::u8path(request.params[0].get_str()));
    2713             :     // Write to a temporary path and then move into `path` on completion
    2714             :     // to avoid confusion due to an interruption.
    2715           6 :     const fs::path temppath = fsbridge::AbsPathJoin(args.GetDataDirNet(), fs::u8path(request.params[0].get_str() + ".incomplete"));
    2716             : 
    2717           6 :     if (fs::exists(path)) {
    2718           4 :         throw JSONRPCError(
    2719             :             RPC_INVALID_PARAMETER,
    2720           2 :             path.utf8string() + " already exists. If you are sure this is what you want, "
    2721             :             "move it out of the way first");
    2722             :     }
    2723             : 
    2724           4 :     FILE* file{fsbridge::fopen(temppath, "wb")};
    2725           4 :     AutoFile afile{file};
    2726           4 :     if (afile.IsNull()) {
    2727           4 :         throw JSONRPCError(
    2728             :             RPC_INVALID_PARAMETER,
    2729           2 :             "Couldn't open file " + temppath.utf8string() + " for writing.");
    2730             :     }
    2731             : 
    2732           2 :     NodeContext& node = EnsureAnyNodeContext(request.context);
    2733           2 :     UniValue result = CreateUTXOSnapshot(
    2734           2 :         node, node.chainman->ActiveChainstate(), afile, path, temppath);
    2735           2 :     fs::rename(temppath, path);
    2736             : 
    2737           2 :     result.pushKV("path", path.utf8string());
    2738           2 :     return result;
    2739          10 : },
    2740             :     };
    2741           0 : }
    2742             : 
    2743          11 : UniValue CreateUTXOSnapshot(
    2744             :     NodeContext& node,
    2745             :     CChainState& chainstate,
    2746             :     AutoFile& afile,
    2747             :     const fs::path& path,
    2748             :     const fs::path& temppath)
    2749             : {
    2750          11 :     std::unique_ptr<CCoinsViewCursor> pcursor;
    2751          11 :     std::optional<CCoinsStats> maybe_stats;
    2752             :     const CBlockIndex* tip;
    2753             : 
    2754             :     {
    2755             :         // We need to lock cs_main to ensure that the coinsdb isn't written to
    2756             :         // between (i) flushing coins cache to disk (coinsdb), (ii) getting stats
    2757             :         // based upon the coinsdb, and (iii) constructing a cursor to the
    2758             :         // coinsdb for use below this block.
    2759             :         //
    2760             :         // Cursors returned by leveldb iterate over snapshots, so the contents
    2761             :         // of the pcursor will not be affected by simultaneous writes during
    2762             :         // use below this block.
    2763             :         //
    2764             :         // See discussion here:
    2765             :         //   https://github.com/bitcoin/bitcoin/pull/15606#discussion_r274479369
    2766             :         //
    2767          11 :         LOCK(::cs_main);
    2768             : 
    2769          11 :         chainstate.ForceFlushStateToDisk();
    2770             : 
    2771          11 :         maybe_stats = GetUTXOStats(&chainstate.CoinsDB(), chainstate.m_blockman, CoinStatsHashType::HASH_SERIALIZED, node.rpc_interruption_point);
    2772          11 :         if (!maybe_stats) {
    2773           0 :             throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
    2774             :         }
    2775             : 
    2776          11 :         pcursor = chainstate.CoinsDB().Cursor();
    2777          11 :         tip = CHECK_NONFATAL(chainstate.m_blockman.LookupBlockIndex(maybe_stats->hashBlock));
    2778          11 :     }
    2779             : 
    2780          11 :     LOG_TIME_SECONDS(strprintf("writing UTXO snapshot at height %s (%s) to file %s (via %s)",
    2781             :         tip->nHeight, tip->GetBlockHash().ToString(),
    2782             :         fs::PathToString(path), fs::PathToString(temppath)));
    2783             : 
    2784          11 :     SnapshotMetadata metadata{tip->GetBlockHash(), maybe_stats->coins_count, tip->nChainTx};
    2785             : 
    2786          11 :     afile << metadata;
    2787             : 
    2788          11 :     COutPoint key;
    2789          11 :     Coin coin;
    2790          11 :     unsigned int iter{0};
    2791             : 
    2792        1291 :     while (pcursor->Valid()) {
    2793        1280 :         if (iter % 5000 == 0) node.rpc_interruption_point();
    2794        1280 :         ++iter;
    2795        1280 :         if (pcursor->GetKey(key) && pcursor->GetValue(coin)) {
    2796        1280 :             afile << key;
    2797        1280 :             afile << coin;
    2798        1280 :         }
    2799             : 
    2800        1280 :         pcursor->Next();
    2801             :     }
    2802             : 
    2803          11 :     afile.fclose();
    2804             : 
    2805          11 :     UniValue result(UniValue::VOBJ);
    2806          11 :     result.pushKV("coins_written", maybe_stats->coins_count);
    2807          11 :     result.pushKV("base_hash", tip->GetBlockHash().ToString());
    2808          11 :     result.pushKV("base_height", tip->nHeight);
    2809          11 :     result.pushKV("path", path.utf8string());
    2810          11 :     result.pushKV("txoutset_hash", maybe_stats->hashSerialized.ToString());
    2811             :     // Cast required because univalue doesn't have serialization specified for
    2812             :     // `unsigned int`, nChainTx's type.
    2813          11 :     result.pushKV("nchaintx", uint64_t{tip->nChainTx});
    2814          11 :     return result;
    2815          11 : }
    2816             : 
    2817        3201 : void RegisterBlockchainRPCCommands(CRPCTable& t)
    2818             : {
    2819       95271 :     static const CRPCCommand commands[]{
    2820        3069 :         {"blockchain", &getblockchaininfo},
    2821        3069 :         {"blockchain", &getchaintxstats},
    2822        3069 :         {"blockchain", &getblockstats},
    2823        3069 :         {"blockchain", &getbestblockhash},
    2824        3069 :         {"blockchain", &getbestchainlock},
    2825        3069 :         {"blockchain", &getblockcount},
    2826        3069 :         {"blockchain", &getblock},
    2827        3069 :         {"blockchain", &getblockfrompeer},
    2828        3069 :         {"blockchain", &getblockhashes},
    2829        3069 :         {"blockchain", &getblockhash},
    2830        3069 :         {"blockchain", &getblockheader},
    2831        3069 :         {"blockchain", &getblockheaders},
    2832        3069 :         {"blockchain", &getmerkleblocks},
    2833        3069 :         {"blockchain", &getchaintips},
    2834        3069 :         {"blockchain", &getdifficulty},
    2835        3069 :         {"blockchain", &getspecialtxes},
    2836        3069 :         {"blockchain", &gettxout},
    2837        3069 :         {"blockchain", &gettxoutsetinfo},
    2838        3069 :         {"blockchain", &pruneblockchain},
    2839        3069 :         {"blockchain", &verifychain},
    2840        3069 :         {"blockchain", &preciousblock},
    2841        3069 :         {"blockchain", &scantxoutset},
    2842        3069 :         {"blockchain", &getblockfilter},
    2843        3069 :         {"hidden", &invalidateblock},
    2844        3069 :         {"hidden", &reconsiderblock},
    2845        3069 :         {"hidden", &waitfornewblock},
    2846        3069 :         {"hidden", &waitforblock},
    2847        3069 :         {"hidden", &waitforblockheight},
    2848        3069 :         {"hidden", &syncwithvalidationinterfacequeue},
    2849        3069 :         {"hidden", &dumptxoutset},
    2850             :     };
    2851       99231 :     for (const auto& c : commands) {
    2852       96030 :         t.appendCommand(c.name, &c);
    2853             :     }
    2854        3201 : }

Generated by: LCOV version 1.16