LCOV - code coverage report
Current view: top level - src/rpc - blockchain.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 751 1917 39.2 %
Date: 2026-06-25 07:23:51 Functions: 40 116 34.5 %

          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         146 : 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           5 : double GetDifficulty(const CBlockIndex* blockindex)
      91             : {
      92           5 :     CHECK_NONFATAL(blockindex);
      93             : 
      94           5 :     return ConvertBitsToDouble(blockindex->nBits);
      95             : }
      96             : 
      97           0 : static int ComputeNextBlockAndDepth(const CBlockIndex* tip, const CBlockIndex* blockindex, const CBlockIndex*& next)
      98             : {
      99           0 :     next = tip->GetAncestor(blockindex->nHeight + 1);
     100           0 :     if (next && next->pprev == blockindex) {
     101           0 :         return tip->nHeight - blockindex->nHeight + 1;
     102             :     }
     103           0 :     next = nullptr;
     104           0 :     return blockindex == tip ? 1 : -1;
     105           0 : }
     106             : 
     107           0 : static const CBlockIndex* ParseHashOrHeight(const UniValue& param, ChainstateManager& chainman)
     108             : {
     109           0 :     LOCK(::cs_main);
     110           0 :     CChain& active_chain = chainman.ActiveChain();
     111             : 
     112           0 :     if (param.isNum()) {
     113           0 :         const int height{param.getInt<int>()};
     114           0 :         if (height < 0) {
     115           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Target block height %d is negative", height));
     116             :         }
     117           0 :         const int current_tip{active_chain.Height()};
     118           0 :         if (height > current_tip) {
     119           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Target block height %d after current tip %d", height, current_tip));
     120             :         }
     121             : 
     122           0 :         return active_chain[height];
     123             :     } else {
     124           0 :         const uint256 hash{ParseHashV(param, "hash_or_height")};
     125           0 :         const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash);
     126             : 
     127           0 :         if (!pindex) {
     128           0 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
     129             :         }
     130             : 
     131           0 :         return pindex;
     132             :     }
     133           0 : }
     134             : 
     135           0 : 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           0 :     AssertLockNotHeld(cs_main); // For performance reasons
     139             : 
     140           0 :     UniValue result(UniValue::VOBJ);
     141           0 :     result.pushKV("hash", blockindex->GetBlockHash().GetHex());
     142             :     const CBlockIndex* pnext;
     143           0 :     int confirmations = ComputeNextBlockAndDepth(tip, blockindex, pnext);
     144           0 :     result.pushKV("confirmations", confirmations);
     145           0 :     result.pushKV("height", blockindex->nHeight);
     146           0 :     result.pushKV("version", blockindex->nVersion);
     147           0 :     result.pushKV("versionHex", strprintf("%08x", blockindex->nVersion));
     148           0 :     result.pushKV("merkleroot", blockindex->hashMerkleRoot.GetHex());
     149           0 :     result.pushKV("time", (int64_t)blockindex->nTime);
     150           0 :     result.pushKV("mediantime", (int64_t)blockindex->GetMedianTimePast());
     151           0 :     result.pushKV("nonce", (uint64_t)blockindex->nNonce);
     152           0 :     result.pushKV("bits", strprintf("%08x", blockindex->nBits));
     153           0 :     result.pushKV("difficulty", GetDifficulty(blockindex));
     154           0 :     result.pushKV("chainwork", blockindex->nChainWork.GetHex());
     155           0 :     result.pushKV("nTx", (uint64_t)blockindex->nTx);
     156             : 
     157           0 :     if (blockindex->pprev)
     158           0 :         result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex());
     159           0 :     if (pnext)
     160           0 :         result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex());
     161             : 
     162           0 :     result.pushKV("chainlock", chainlocks.HasChainLock(blockindex->nHeight, blockindex->GetBlockHash()));
     163             : 
     164           0 :     return result;
     165           0 : }
     166             : 
     167           0 : 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           0 :     UniValue result = blockheaderToJSON(tip, blockindex, chainlocks);
     170             : 
     171           0 :     result.pushKV("size", (int)::GetSerializeSize(block, PROTOCOL_VERSION));
     172           0 :     UniValue txs(UniValue::VARR);
     173           0 :     switch (verbosity) {
     174             :         case TxVerbosity::SHOW_TXID:
     175           0 :             for (const CTransactionRef& tx : block.vtx) {
     176           0 :                 txs.push_back(tx->GetHash().GetHex());
     177             :             }
     178           0 :             break;
     179             :         case TxVerbosity::SHOW_DETAILS:
     180             :         case TxVerbosity::SHOW_DETAILS_AND_PREVOUT:
     181           0 :             CBlockUndo blockUndo;
     182           0 :             const bool is_not_pruned{WITH_LOCK(::cs_main, return !blockman.IsBlockPruned(blockindex))};
     183           0 :             const bool have_undo{is_not_pruned && UndoReadFromDisk(blockUndo, blockindex)};
     184             : 
     185           0 :             for (size_t i = 0; i < block.vtx.size(); ++i) {
     186           0 :                 const CTransactionRef& tx = block.vtx.at(i);
     187             :                 // coinbase transaction (i.e. i == 0) doesn't have undo data
     188           0 :                 const CTxUndo* txundo = (have_undo && i > 0) ? &blockUndo.vtxundo.at(i - 1) : nullptr;
     189           0 :                 UniValue objTx(UniValue::VOBJ);
     190           0 :                 TxToUniv(*tx, /*block_hash=*/uint256(), /*entry=*/objTx, /*include_hex=*/true, /*serialize_flags=*/0, txundo, verbosity);
     191           0 :                 bool fLocked = isman.IsLocked(tx->GetHash());
     192           0 :                 objTx.pushKV("instantlock", fLocked || result["chainlock"].get_bool());
     193           0 :                 objTx.pushKV("instantlock_internal", fLocked);
     194           0 :                 txs.push_back(objTx);
     195           0 :             }
     196             :             break;
     197           0 :     }
     198             : 
     199           0 :     result.pushKV("tx", txs);
     200           0 :     if (!block.vtx[0]->vExtraPayload.empty()) {
     201           0 :         if (const auto opt_cbTx = GetTxPayload<CCbTx>(block.vtx[0]->vExtraPayload)) {
     202           0 :             result.pushKV("cbTx", opt_cbTx->ToJson());
     203           0 :         }
     204           0 :     }
     205             : 
     206           0 :     return result;
     207           0 : }
     208             : 
     209          92 : static RPCHelpMan getblockcount()
     210             : {
     211         184 :     return RPCHelpMan{"getblockcount",
     212          92 :                 "\nReturns the height of the most-work fully-validated chain.\n"
     213             :                 "The genesis block has height 0.\n",
     214          92 :                 {},
     215          92 :                 RPCResult{
     216          92 :                     RPCResult::Type::NUM, "", "The current block count"},
     217          92 :                 RPCExamples{
     218          92 :                     HelpExampleCli("getblockcount", "")
     219          92 :             + HelpExampleRpc("getblockcount", "")
     220             :                 },
     221          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     222             : {
     223           0 :     ChainstateManager& chainman = EnsureAnyChainman(request.context);
     224           0 :     LOCK(cs_main);
     225           0 :     return chainman.ActiveChain().Height();
     226           0 : },
     227             :     };
     228           0 : }
     229             : 
     230          92 : static RPCHelpMan getbestblockhash()
     231             : {
     232         184 :     return RPCHelpMan{"getbestblockhash",
     233          92 :                 "\nReturns the hash of the best (tip) block in the most-work fully-validated chain.\n",
     234          92 :                 {},
     235          92 :                 RPCResult{
     236          92 :                     RPCResult::Type::STR_HEX, "", "the block hash, hex-encoded"},
     237          92 :                 RPCExamples{
     238          92 :                     HelpExampleCli("getbestblockhash", "")
     239          92 :             + HelpExampleRpc("getbestblockhash", "")
     240             :                 },
     241          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     242             : {
     243           0 :     ChainstateManager& chainman = EnsureAnyChainman(request.context);
     244           0 :     LOCK(cs_main);
     245           0 :     return chainman.ActiveChain().Tip()->GetBlockHash().GetHex();
     246           0 : },
     247             :     };
     248           0 : }
     249             : 
     250          92 : static RPCHelpMan getbestchainlock()
     251             : {
     252         184 :     return RPCHelpMan{"getbestchainlock",
     253          92 :         "\nReturns information about the best ChainLock. Throws an error if there is no known ChainLock yet.",
     254          92 :         {},
     255          92 :         RPCResult{
     256          92 :             RPCResult::Type::OBJ, "", "",
     257         552 :             {
     258          92 :                 {RPCResult::Type::STR_HEX, "hash", "The block hash hex-encoded"},
     259          92 :                 {RPCResult::Type::NUM, "height", "The block height or index"},
     260          92 :                 {RPCResult::Type::STR_HEX, "signature", "The ChainLock's BLS signature"},
     261          92 :                 {RPCResult::Type::BOOL, "known_block", "True if the block is known by our node"},
     262          92 :                 {RPCResult::Type::STR_HEX, "hex", "The serialized, hex-encoded data for best ChainLock"},
     263             :             }},
     264          92 :         RPCExamples{
     265          92 :             HelpExampleCli("getbestchainlock", "")
     266          92 :             + HelpExampleRpc("getbestchainlock", "")
     267             :         },
     268          92 :     [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     269             : {
     270           0 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     271             : 
     272           0 :     CHECK_NONFATAL(node.chainlocks);
     273           0 :     const chainlock::ChainLockSig clsig = node.chainlocks->GetBestChainLock();
     274           0 :     if (clsig.IsNull()) {
     275           0 :         throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to find any ChainLock");
     276             :     }
     277             : 
     278           0 :     UniValue result(UniValue::VOBJ);
     279             : 
     280           0 :     result.pushKV("blockhash", clsig.getBlockHash().GetHex());
     281           0 :     result.pushKV("height", clsig.getHeight());
     282           0 :     result.pushKV("signature", clsig.getSig().ToString());
     283             : 
     284             :     {
     285           0 :         const ChainstateManager& chainman = EnsureChainman(node);
     286           0 :         LOCK(cs_main);
     287           0 :         result.pushKV("known_block", chainman.m_blockman.LookupBlockIndex(clsig.getBlockHash()) != nullptr);
     288           0 :     }
     289             : 
     290           0 :     CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
     291           0 :     ssTx << clsig;
     292           0 :     result.pushKV("hex", HexStr(ssTx));
     293             : 
     294           0 :     return result;
     295           0 : },
     296             :     };
     297           0 : }
     298             : 
     299           0 : void RPCNotifyBlockChange(const CBlockIndex* pindex)
     300             : {
     301           0 :     if(pindex) {
     302           0 :         LOCK(cs_blockchange);
     303           0 :         latestblock.hash = pindex->GetBlockHash();
     304           0 :         latestblock.height = pindex->nHeight;
     305           0 :     }
     306           0 :     cond_blockchange.notify_all();
     307           0 : }
     308             : 
     309          92 : static RPCHelpMan waitfornewblock()
     310             : {
     311         184 :     return RPCHelpMan{"waitfornewblock",
     312          92 :                 "\nWaits for a specific new block and returns useful info about it.\n"
     313             :                 "\nReturns the current block on timeout or exit.\n",
     314         184 :                 {
     315          92 :                     {"timeout", RPCArg::Type::NUM, RPCArg::Default{0}, "Time in milliseconds to wait for a response. 0 indicates no timeout."},
     316             :                 },
     317          92 :                 RPCResult{
     318          92 :                     RPCResult::Type::OBJ, "", "",
     319         276 :                     {
     320          92 :                         {RPCResult::Type::STR_HEX, "hash", "The blockhash"},
     321          92 :                         {RPCResult::Type::NUM, "height", "Block height"},
     322             :                     }},
     323          92 :                 RPCExamples{
     324          92 :                     HelpExampleCli("waitfornewblock", "1000")
     325          92 :             + HelpExampleRpc("waitfornewblock", "1000")
     326             :                 },
     327          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     328             : {
     329           0 :     int timeout = 0;
     330           0 :     if (!request.params[0].isNull())
     331           0 :         timeout = request.params[0].getInt<int>();
     332             : 
     333           0 :     CUpdatedBlock block;
     334             :     {
     335           0 :         WAIT_LOCK(cs_blockchange, lock);
     336           0 :         block = latestblock;
     337           0 :         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           0 :             cond_blockchange.wait(lock, [&block]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); });
     341           0 :         block = latestblock;
     342           0 :     }
     343           0 :     UniValue ret(UniValue::VOBJ);
     344           0 :     ret.pushKV("hash", block.hash.GetHex());
     345           0 :     ret.pushKV("height", block.height);
     346           0 :     return ret;
     347           0 : },
     348             :     };
     349           0 : }
     350             : 
     351          92 : static RPCHelpMan waitforblock()
     352             : {
     353         184 :     return RPCHelpMan{"waitforblock",
     354          92 :                 "\nWaits for a specific new block and returns useful info about it.\n"
     355             :                 "\nReturns the current block on timeout or exit.\n",
     356         276 :                 {
     357          92 :                     {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Block hash to wait for."},
     358          92 :                     {"timeout", RPCArg::Type::NUM, RPCArg::Default{0}, "Time in milliseconds to wait for a response. 0 indicates no timeout."},
     359             :                 },
     360          92 :                 RPCResult{
     361          92 :                     RPCResult::Type::OBJ, "", "",
     362         276 :                     {
     363          92 :                         {RPCResult::Type::STR_HEX, "hash", "The blockhash"},
     364          92 :                         {RPCResult::Type::NUM, "height", "Block height"},
     365             :                     }},
     366          92 :                 RPCExamples{
     367          92 :                     HelpExampleCli("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\" 1000")
     368          92 :             + HelpExampleRpc("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\", 1000")
     369             :                 },
     370          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     371             : {
     372           0 :     int timeout = 0;
     373             : 
     374           0 :     uint256 hash(ParseHashV(request.params[0], "blockhash"));
     375             : 
     376           0 :     if (!request.params[1].isNull())
     377           0 :         timeout = request.params[1].getInt<int>();
     378             : 
     379           0 :     CUpdatedBlock block;
     380             :     {
     381           0 :         WAIT_LOCK(cs_blockchange, lock);
     382           0 :         if(timeout)
     383           0 :             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           0 :         block = latestblock;
     387           0 :     }
     388             : 
     389           0 :     UniValue ret(UniValue::VOBJ);
     390           0 :     ret.pushKV("hash", block.hash.GetHex());
     391           0 :     ret.pushKV("height", block.height);
     392           0 :     return ret;
     393           0 : },
     394             :     };
     395           0 : }
     396             : 
     397          92 : static RPCHelpMan waitforblockheight()
     398             : {
     399         184 :     return RPCHelpMan{"waitforblockheight",
     400          92 :                 "\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         276 :                 {
     404          92 :                     {"height", RPCArg::Type::NUM, RPCArg::Optional::NO, "Block height to wait for."},
     405          92 :                     {"timeout", RPCArg::Type::NUM, RPCArg::Default{0}, "Time in milliseconds to wait for a response. 0 indicates no timeout."},
     406             :                 },
     407          92 :                 RPCResult{
     408          92 :                     RPCResult::Type::OBJ, "", "",
     409         276 :                     {
     410          92 :                         {RPCResult::Type::STR_HEX, "hash", "The blockhash"},
     411          92 :                         {RPCResult::Type::NUM, "height", "Block height"},
     412             :                     }},
     413          92 :                 RPCExamples{
     414          92 :                     HelpExampleCli("waitforblockheight", "100 1000")
     415          92 :             + HelpExampleRpc("waitforblockheight", "100, 1000")
     416             :                 },
     417          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     418             : {
     419           0 :     int timeout = 0;
     420             : 
     421           0 :     int height = request.params[0].getInt<int>();
     422             : 
     423           0 :     if (!request.params[1].isNull())
     424           0 :         timeout = request.params[1].getInt<int>();
     425             : 
     426           0 :     CUpdatedBlock block;
     427             :     {
     428           0 :         WAIT_LOCK(cs_blockchange, lock);
     429           0 :         if(timeout)
     430           0 :             cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&height]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.height >= height || !IsRPCRunning();});
     431             :         else
     432           0 :             cond_blockchange.wait(lock, [&height]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.height >= height || !IsRPCRunning(); });
     433           0 :         block = latestblock;
     434           0 :     }
     435           0 :     UniValue ret(UniValue::VOBJ);
     436           0 :     ret.pushKV("hash", block.hash.GetHex());
     437           0 :     ret.pushKV("height", block.height);
     438           0 :     return ret;
     439           0 : },
     440             :     };
     441           0 : }
     442             : 
     443          92 : static RPCHelpMan syncwithvalidationinterfacequeue()
     444             : {
     445         184 :     return RPCHelpMan{"syncwithvalidationinterfacequeue",
     446          92 :                 "\nWaits for the validation interface queue to catch up on everything that was there when we entered this function.\n",
     447          92 :                 {},
     448          92 :                 RPCResult{RPCResult::Type::NONE, "", ""},
     449          92 :                 RPCExamples{
     450          92 :                     HelpExampleCli("syncwithvalidationinterfacequeue","")
     451          92 :             + HelpExampleRpc("syncwithvalidationinterfacequeue","")
     452             :                 },
     453          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     454             : {
     455           0 :     SyncWithValidationInterfaceQueue();
     456           0 :     return UniValue::VNULL;
     457           0 : },
     458             :     };
     459           0 : }
     460             : 
     461          92 : static RPCHelpMan getdifficulty()
     462             : {
     463         184 :     return RPCHelpMan{"getdifficulty",
     464          92 :                 "\nReturns the proof-of-work difficulty as a multiple of the minimum difficulty.\n",
     465          92 :                 {},
     466          92 :                 RPCResult{
     467          92 :                     RPCResult::Type::NUM, "", "the proof-of-work difficulty as a multiple of the minimum difficulty."},
     468          92 :                 RPCExamples{
     469          92 :                     HelpExampleCli("getdifficulty", "")
     470          92 :             + HelpExampleRpc("getdifficulty", "")
     471             :                 },
     472          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     473             : {
     474             : 
     475           0 :     ChainstateManager& chainman = EnsureAnyChainman(request.context);
     476           0 :     LOCK(cs_main);
     477           0 :     return GetDifficulty(chainman.ActiveChain().Tip());
     478           0 : },
     479             :     };
     480           0 : }
     481             : 
     482          92 : static RPCHelpMan getblockfrompeer()
     483             : {
     484          92 :     return RPCHelpMan{
     485          92 :         "getblockfrompeer",
     486          92 :         "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         276 :         {
     493          92 :             {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash to try to fetch"},
     494          92 :             {"peer_id", RPCArg::Type::NUM, RPCArg::Optional::NO, "The peer to fetch it from (see getpeerinfo for peer IDs)"},
     495             :         },
     496          92 :         RPCResult{RPCResult::Type::OBJ, "", /*optional=*/false, "", {}},
     497          92 :         RPCExamples{
     498          92 :             HelpExampleCli("getblockfrompeer", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" 0")
     499          92 :             + HelpExampleRpc("getblockfrompeer", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" 0")
     500             :         },
     501          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     502             : {
     503           0 :     RPCTypeCheck(request.params, {
     504           0 :         UniValue::VSTR, // blockhash
     505           0 :         UniValue::VNUM, // peer_id
     506             :     });
     507             : 
     508           0 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     509           0 :     ChainstateManager& chainman = EnsureChainman(node);
     510           0 :     PeerManager& peerman = EnsurePeerman(node);
     511             : 
     512           0 :     const uint256& block_hash{ParseHashV(request.params[0], "blockhash")};
     513           0 :     const NodeId peer_id{request.params[1].getInt<int64_t>()};
     514             : 
     515           0 :     const CBlockIndex* const index = WITH_LOCK(cs_main, return chainman.m_blockman.LookupBlockIndex(block_hash););
     516             : 
     517           0 :     if (!index) {
     518           0 :         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           0 :     if (index->nHeight > chainman.ActiveChain().Tip()->nHeight && node::fPruneMode) {
     524           0 :         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           0 :     const bool block_has_data = WITH_LOCK(::cs_main, return index->nStatus & BLOCK_HAVE_DATA);
     528           0 :     if (block_has_data) {
     529           0 :         throw JSONRPCError(RPC_MISC_ERROR, "Block already downloaded");
     530             :     }
     531           0 :     if (const auto err{peerman.FetchBlock(peer_id, *index)}) {
     532           0 :         throw JSONRPCError(RPC_MISC_ERROR, err.value());
     533             :     }
     534           0 :     return UniValue::VOBJ;
     535           0 : },
     536             :     };
     537           0 : }
     538             : 
     539          92 : static RPCHelpMan getblockhashes()
     540             : {
     541         184 :     return RPCHelpMan{"getblockhashes",
     542          92 :         "\nReturns array of hashes of blocks within the timestamp range provided.\n",
     543         276 :         {
     544          92 :             {"high", RPCArg::Type::NUM, RPCArg::Optional::NO, "The newer block timestamp"},
     545          92 :             {"low", RPCArg::Type::NUM, RPCArg::Optional::NO, "The older block timestamp"},
     546             :         },
     547          92 :         RPCResult{
     548          92 :             RPCResult::Type::ARR, "", "",
     549          92 :             {{RPCResult::Type::STR_HEX, "", "The block hash"}}},
     550          92 :         RPCExamples{
     551          92 :             HelpExampleCli("getblockhashes", "1231614698 1231024505")
     552          92 :             + HelpExampleRpc("getblockhashes", "1231614698, 1231024505")
     553             :         },
     554          92 :     [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     555             : {
     556           0 :     if (!g_timestampindex) {
     557           0 :         throw JSONRPCError(RPC_MISC_ERROR, "Timestamp index is not enabled. Start with -timestampindex to enable.");
     558             :     }
     559             : 
     560           0 :     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           0 :     unsigned int high = request.params[0].getInt<int>();
     565           0 :     unsigned int low = request.params[1].getInt<int>();
     566           0 :     std::vector<uint256> blockHashes;
     567             : 
     568           0 :     if (!g_timestampindex->GetBlockHashes(high, low, blockHashes)) {
     569           0 :         throw JSONRPCError(RPC_MISC_ERROR, "Failed to read timestamp index.");
     570             :     }
     571             : 
     572           0 :     UniValue result(UniValue::VARR);
     573           0 :     for (const auto& hash : blockHashes) {
     574           0 :         result.push_back(hash.GetHex());
     575             :     }
     576             : 
     577           0 :     return result;
     578           0 : },
     579             :     };
     580           0 : }
     581             : 
     582          92 : static RPCHelpMan getblockhash()
     583             : {
     584         184 :     return RPCHelpMan{"getblockhash",
     585          92 :                 "\nReturns hash of block in best-block-chain at height provided.\n",
     586         184 :                 {
     587          92 :                     {"height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The height index"},
     588             :                 },
     589          92 :                 RPCResult{
     590          92 :                     RPCResult::Type::STR_HEX, "", "The block hash"},
     591          92 :                 RPCExamples{
     592          92 :                     HelpExampleCli("getblockhash", "1000")
     593          92 :             + HelpExampleRpc("getblockhash", "1000")
     594             :                 },
     595          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     596             : {
     597           0 :     ChainstateManager& chainman = EnsureAnyChainman(request.context);
     598           0 :     LOCK(cs_main);
     599           0 :     const CChain& active_chain = chainman.ActiveChain();
     600             : 
     601           0 :     int nHeight = request.params[0].getInt<int>();
     602           0 :     if (nHeight < 0 || nHeight > active_chain.Height())
     603           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range");
     604             : 
     605           0 :     const CBlockIndex* pblockindex = active_chain[nHeight];
     606           0 :     return pblockindex->GetBlockHash().GetHex();
     607           0 : },
     608             :     };
     609           0 : }
     610             : 
     611          92 : static RPCHelpMan getblockheader()
     612             : {
     613         184 :     return RPCHelpMan{"getblockheader",
     614          92 :                 "\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         276 :                 {
     617          92 :                     {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
     618          92 :                     {"verbose", RPCArg::Type::BOOL, RPCArg::Default{true}, "true for a json object, false for the hex-encoded data"},
     619             :                 },
     620         276 :                 {
     621         184 :                     RPCResult{"for verbose = true",
     622          92 :                         RPCResult::Type::OBJ, "", "",
     623        1564 :                         {
     624          92 :                             {RPCResult::Type::STR_HEX, "hash", "the block hash (same as provided)"},
     625          92 :                             {RPCResult::Type::NUM, "confirmations", "The number of confirmations, or -1 if the block is not on the main chain"},
     626          92 :                             {RPCResult::Type::NUM, "height", "The block height or index"},
     627          92 :                             {RPCResult::Type::NUM, "version", "The block version"},
     628          92 :                             {RPCResult::Type::STR_HEX, "versionHex", "The block version formatted in hexadecimal"},
     629          92 :                             {RPCResult::Type::STR_HEX, "merkleroot", "The merkle root"},
     630          92 :                             {RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME},
     631          92 :                             {RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME},
     632          92 :                             {RPCResult::Type::NUM, "nonce", "The nonce"},
     633          92 :                             {RPCResult::Type::STR_HEX, "bits", "The bits"},
     634          92 :                             {RPCResult::Type::NUM, "difficulty", "The difficulty"},
     635          92 :                             {RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the current chain"},
     636          92 :                             {RPCResult::Type::NUM, "nTx", "The number of transactions in the block"},
     637          92 :                             {RPCResult::Type::STR_HEX, "previousblockhash", /*optional=*/true, "The hash of the previous block (if available)"},
     638          92 :                             {RPCResult::Type::STR_HEX, "nextblockhash", /*optional=*/true, "The hash of the next block (if available)"},
     639          92 :                             {RPCResult::Type::BOOL, "chainlock", "The state of the block ChainLock"},
     640             :                         }},
     641         184 :                     RPCResult{"for verbose=false",
     642          92 :                         RPCResult::Type::STR_HEX, "", "A string that is serialized, hex-encoded data for block 'hash'"},
     643             :                 },
     644          92 :                 RPCExamples{
     645          92 :                     HelpExampleCli("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
     646          92 :             + HelpExampleRpc("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
     647             :                 },
     648          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     649             : {
     650           0 :     uint256 hash(ParseHashV(request.params[0], "hash"));
     651           0 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     652             : 
     653           0 :     bool fVerbose = true;
     654           0 :     if (!request.params[1].isNull())
     655           0 :         fVerbose = request.params[1].get_bool();
     656             : 
     657             :     const CBlockIndex* pblockindex;
     658             :     const CBlockIndex* tip;
     659             :     {
     660           0 :         ChainstateManager& chainman = EnsureChainman(node);
     661           0 :         LOCK(cs_main);
     662           0 :         pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
     663           0 :         tip = chainman.ActiveChain().Tip();
     664           0 :     }
     665             : 
     666           0 :     if (!pblockindex) {
     667           0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
     668             :     }
     669             : 
     670           0 :     if (!fVerbose)
     671             :     {
     672           0 :         CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
     673           0 :         ssBlock << pblockindex->GetBlockHeader();
     674           0 :         std::string strHex = HexStr(ssBlock);
     675           0 :         return strHex;
     676           0 :     }
     677             : 
     678           0 :     CHECK_NONFATAL(node.chainlocks);
     679           0 :     return blockheaderToJSON(tip, pblockindex, *node.chainlocks);
     680           0 : },
     681             :     };
     682           0 : }
     683             : 
     684          92 : static RPCHelpMan getblockheaders()
     685             : {
     686         184 :     return RPCHelpMan{"getblockheaders",
     687          92 :         "\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         368 :         {
     691          92 :             {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
     692          92 :             {"count", RPCArg::Type::NUM, RPCArg::Default{int{MAX_HEADERS_UNCOMPRESSED_RESULT}}, ""},
     693          92 :             {"verbose", RPCArg::Type::BOOL, RPCArg::Default{true}, "true for a json object, false for the hex-encoded data"},
     694             :         },
     695         276 :         {
     696         184 :             RPCResult{"for verbose = true",
     697          92 :                 RPCResult::Type::ARR, "", "",
     698         184 :                     {{RPCResult::Type::OBJ, "", "",
     699        1564 :                     {
     700          92 :                         {RPCResult::Type::STR_HEX, "hash", "The block hash (same as provided)"},
     701          92 :                         {RPCResult::Type::NUM, "confirmations", "The number of confirmations, or -1 if the block is not on the main chain"},
     702          92 :                         {RPCResult::Type::NUM, "height", "The block height or index"},
     703          92 :                         {RPCResult::Type::NUM, "version", "The block version"},
     704          92 :                         {RPCResult::Type::STR_HEX, "versionHex", "The block version formatted in hexadecimal"},
     705          92 :                         {RPCResult::Type::STR_HEX, "merkleroot", "The merkle root"},
     706          92 :                         {RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME},
     707          92 :                         {RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME},
     708          92 :                         {RPCResult::Type::NUM, "nonce", "The nonce"},
     709          92 :                         {RPCResult::Type::STR_HEX, "bits", "The bits"},
     710          92 :                         {RPCResult::Type::NUM, "difficulty", "The difficulty"},
     711          92 :                         {RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the current chain"},
     712          92 :                         {RPCResult::Type::NUM, "nTx", "The number of transactions in the block"},
     713          92 :                         {RPCResult::Type::STR_HEX, "previousblockhash", /*optional=*/true, "The hash of the previous block (if available)"},
     714          92 :                         {RPCResult::Type::STR_HEX, "nextblockhash", /*optional=*/true, "The hash of the next block (if available)"},
     715          92 :                         {RPCResult::Type::BOOL, "chainlock", "The state of the block ChainLock"},
     716             :                     }},
     717             :                 }},
     718         184 :             RPCResult{"for verbose=false",
     719          92 :                 RPCResult::Type::ARR, "", "",
     720          92 :                     {{RPCResult::Type::STR_HEX, "", "A string that is serialized, hex-encoded data for block 'hash'"}}},
     721             :         },
     722          92 :         RPCExamples{
     723          92 :             HelpExampleCli("getblockheaders", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" 2000")
     724          92 :     + HelpExampleRpc("getblockheaders", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" 2000")
     725             :         },
     726          92 :     [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     727             : {
     728             : 
     729           0 :     uint256 hash(ParseHashV(request.params[0], "blockhash"));
     730             : 
     731           0 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     732             : 
     733           0 :     ChainstateManager& chainman = EnsureChainman(node);
     734             : 
     735           0 :     CChainState& active_chainstate = chainman.ActiveChainstate();
     736           0 :     CChain& active_chain = active_chainstate.m_chain;
     737             : 
     738             :     const CBlockIndex* pblockindex;
     739             :     const CBlockIndex* tip;
     740             :     {
     741           0 :         LOCK(cs_main);
     742           0 :         pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
     743           0 :         tip = active_chain.Tip();
     744           0 :     }
     745             : 
     746           0 :     if (!pblockindex) {
     747           0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
     748             :     }
     749             : 
     750           0 :     int nCount = MAX_HEADERS_UNCOMPRESSED_RESULT;
     751           0 :     if (!request.params[1].isNull())
     752           0 :         nCount = request.params[1].getInt<int>();
     753             : 
     754           0 :     if (nCount <= 0 || nCount > (int)MAX_HEADERS_UNCOMPRESSED_RESULT)
     755           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Count is out of range");
     756             : 
     757           0 :     bool fVerbose = true;
     758           0 :     if (!request.params[2].isNull())
     759           0 :         fVerbose = request.params[2].get_bool();
     760             : 
     761           0 :     UniValue arrHeaders(UniValue::VARR);
     762             : 
     763           0 :     if (!fVerbose)
     764             :     {
     765           0 :         for (; pblockindex; pblockindex = active_chain.Next(pblockindex))
     766             :         {
     767           0 :             CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
     768           0 :             ssBlock << pblockindex->GetBlockHeader();
     769           0 :             std::string strHex = HexStr(ssBlock);
     770           0 :             arrHeaders.push_back(strHex);
     771           0 :             if (--nCount <= 0)
     772           0 :                 break;
     773           0 :         }
     774           0 :         return arrHeaders;
     775             :     }
     776             : 
     777           0 :     CHECK_NONFATAL(node.chainlocks);
     778           0 :     for (; pblockindex; pblockindex = active_chain.Next(pblockindex))
     779             :     {
     780           0 :         arrHeaders.push_back(blockheaderToJSON(tip, pblockindex, *node.chainlocks));
     781           0 :         if (--nCount <= 0)
     782           0 :             break;
     783           0 :     }
     784             : 
     785           0 :     return arrHeaders;
     786           0 : },
     787             :     };
     788           0 : }
     789             : 
     790           0 : static CBlock GetBlockChecked(BlockManager& blockman, const CBlockIndex* pblockindex)
     791             : {
     792           0 :     CBlock block;
     793             :     {
     794           0 :         LOCK(cs_main);
     795           0 :         if (blockman.IsBlockPruned(pblockindex)) {
     796           0 :             throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)");
     797             :         }
     798           0 :     }
     799             : 
     800           0 :     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           0 :         throw JSONRPCError(RPC_MISC_ERROR, "Block not found on disk");
     805             :     }
     806             : 
     807           0 :     return block;
     808           0 : }
     809             : 
     810           0 : static CBlockUndo GetUndoChecked(BlockManager& blockman, const CBlockIndex* pblockindex)
     811             : {
     812           0 :     CBlockUndo blockUndo;
     813             : 
     814             :     // The Genesis block does not have undo data
     815           0 :     if (pblockindex->nHeight == 0) return blockUndo;
     816             : 
     817             :     {
     818           0 :         LOCK(cs_main);
     819           0 :         if (blockman.IsBlockPruned(pblockindex)) {
     820           0 :             throw JSONRPCError(RPC_MISC_ERROR, "Undo data not available (pruned data)");
     821             :         }
     822           0 :     }
     823             : 
     824           0 :     if (!UndoReadFromDisk(blockUndo, pblockindex)) {
     825           0 :         throw JSONRPCError(RPC_MISC_ERROR, "Can't read undo data from disk");
     826             :     }
     827             : 
     828           0 :     return blockUndo;
     829           0 : }
     830             : 
     831          92 : static RPCHelpMan getmerkleblocks()
     832             : {
     833         184 :     return RPCHelpMan{"getmerkleblocks",
     834          92 :         "\nReturns an array of hex-encoded merkleblocks for <count> blocks starting from <hash> which match <filter>.\n",
     835         368 :         {
     836          92 :             {"filter", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex-encoded bloom filter"},
     837          92 :             {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
     838          92 :             {"count", RPCArg::Type::NUM, RPCArg::Default{int{MAX_HEADERS_UNCOMPRESSED_RESULT}}, ""},
     839             :         },
     840          92 :         RPCResult{
     841          92 :                 RPCResult::Type::ARR, "", "",
     842          92 :                     {{RPCResult::Type::STR_HEX, "", "A string that is serialized, hex-encoded data for a merkleblock"}}},
     843          92 :         RPCExamples{
     844          92 :             HelpExampleCli("getmerkleblocks", "\"2303028005802040100040000008008400048141010000f8400420800080025004000004130000000000000001\" \"00000000007e1432d2af52e8463278bf556b55cf5049262f25634557e2e91202\" 2000")
     845          92 :     + HelpExampleRpc("getmerkleblocks", "\"2303028005802040100040000008008400048141010000f8400420800080025004000004130000000000000001\" \"00000000007e1432d2af52e8463278bf556b55cf5049262f25634557e2e91202\" 2000")
     846             :         },
     847          92 :     [&](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         146 : const RPCResult getblock_vin{
     907         146 :     RPCResult::Type::ARR, "vin", "",
     908         292 :     {
     909         292 :         {RPCResult::Type::OBJ, "", "",
     910         438 :         {
     911         146 :             {RPCResult::Type::ELISION, "", "The same output as verbosity = 2"},
     912         292 :             {RPCResult::Type::OBJ, "prevout", "(Only if undo information is available)",
     913         730 :             {
     914         146 :                 {RPCResult::Type::BOOL, "generated", "Coinbase or not"},
     915         146 :                 {RPCResult::Type::NUM, "height", "The height of the prevout"},
     916         146 :                 {RPCResult::Type::STR_AMOUNT, "value", "The value in " + CURRENCY_UNIT},
     917         292 :                 {RPCResult::Type::OBJ, "scriptPubKey", "",
     918         876 :                 {
     919         146 :                     {RPCResult::Type::STR, "asm", "Disassembly of the public key script"},
     920         146 :                     {RPCResult::Type::STR, "desc", "Inferred descriptor for the output"},
     921         146 :                     {RPCResult::Type::STR_HEX, "hex", "The raw public key script bytes, hex-encoded"},
     922         146 :                     {RPCResult::Type::STR, "address", /*optional=*/true, "The Dash address (only if a well-defined address exists)"},
     923         146 :                     {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"},
     924             :                 }},
     925             :             }},
     926             :         }},
     927             :     }
     928             : };
     929             : 
     930          92 : static RPCHelpMan getblock()
     931             : {
     932         184 :     return RPCHelpMan{"getblock",
     933          92 :                 "\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         276 :                 {
     938          92 :                     {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
     939          92 :                     {"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         460 :                 {
     942         184 :                     RPCResult{"for verbosity = 0",
     943          92 :                 RPCResult::Type::STR_HEX, "", "A string that is serialized, hex-encoded data for block 'hash'"},
     944         184 :                     RPCResult{"for verbosity = 1",
     945          92 :                 RPCResult::Type::OBJ, "", "",
     946        1840 :                 {
     947          92 :                     {RPCResult::Type::STR_HEX, "hash", "the block hash (same as provided)"},
     948          92 :                     {RPCResult::Type::NUM, "confirmations", "The number of confirmations, or -1 if the block is not on the main chain"},
     949          92 :                     {RPCResult::Type::NUM, "height", "The block height or index"},
     950          92 :                     {RPCResult::Type::NUM, "version", "The block version"},
     951          92 :                     {RPCResult::Type::STR_HEX, "versionHex", "The block version formatted in hexadecimal"},
     952          92 :                     {RPCResult::Type::STR_HEX, "merkleroot", "The merkle root"},
     953          92 :                     {RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME},
     954          92 :                     {RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME},
     955          92 :                     {RPCResult::Type::NUM, "nonce", "The nonce"},
     956          92 :                     {RPCResult::Type::STR_HEX, "bits", "The bits"},
     957          92 :                     {RPCResult::Type::NUM, "difficulty", "The difficulty"},
     958          92 :                     {RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the current chain"},
     959          92 :                     {RPCResult::Type::NUM, "nTx", "The number of transactions in the block"},
     960          92 :                     {RPCResult::Type::STR_HEX, "previousblockhash", /*optional=*/true, "The hash of the previous block (if available)"},
     961          92 :                     {RPCResult::Type::STR_HEX, "nextblockhash", /*optional=*/true, "The hash of the next block (if available)"},
     962          92 :                     {RPCResult::Type::BOOL, "chainlock", "The state of the block ChainLock"},
     963          92 :                     {RPCResult::Type::NUM, "size", "The block size"},
     964         184 :                     {RPCResult::Type::ARR, "tx", "The transaction ids",
     965          92 :                         {{RPCResult::Type::STR_HEX, "", "The transaction id"}}},
     966          92 :                     CCbTx::GetJsonHelp(/*key=*/"cbTx", /*optional=*/true),
     967             :                 }},
     968         184 :                     RPCResult{"for verbosity = 2",
     969          92 :                 RPCResult::Type::OBJ, "", "",
     970         276 :                 {
     971          92 :                     {RPCResult::Type::ELISION, "", "Same output as verbosity = 1"},
     972         184 :                     {RPCResult::Type::ARR, "tx", "",
     973         184 :                     {
     974         184 :                         {RPCResult::Type::OBJ, "", "",
     975         276 :                         {
     976          92 :                             {RPCResult::Type::ELISION, "", "The transactions in the format of the getrawtransaction RPC. Different from verbosity = 1 \"tx\" result"},
     977          92 :                             {RPCResult::Type::NUM, "fee", "The transaction fee in " + CURRENCY_UNIT + ", omitted if block undo data is not available"},
     978             :                         }},
     979             :                     }},
     980             :                 }},
     981         184 :                     RPCResult{"for verbosity = 3",
     982          92 :                 RPCResult::Type::OBJ, "", "",
     983         276 :                 {
     984          92 :                     {RPCResult::Type::ELISION, "", "Same output as verbosity = 2"},
     985         184 :                     {RPCResult::Type::ARR, "tx", "",
     986         184 :                     {
     987         184 :                         {RPCResult::Type::OBJ, "", "",
     988          92 :                         {
     989          92 :                             getblock_vin,
     990             :                         }},
     991             :                     }},
     992             :                 }},
     993             :         },
     994          92 :                 RPCExamples{
     995          92 :                     HelpExampleCli("getblock", "\"00000000000fd08c2fb661d2fcb0d49abb3a91e5f27082ce64feed3b4dede2e2\"")
     996          92 :             + HelpExampleRpc("getblock", "\"00000000000fd08c2fb661d2fcb0d49abb3a91e5f27082ce64feed3b4dede2e2\"")
     997             :                 },
     998          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     999             : {
    1000           0 :     uint256 hash(ParseHashV(request.params[0], "blockhash"));
    1001             : 
    1002           0 :     int verbosity = 1;
    1003           0 :     if (!request.params[1].isNull()) {
    1004           0 :         if (request.params[1].isBool()) {
    1005           0 :             verbosity = request.params[1].get_bool() ? 1 : 0;
    1006           0 :         } else {
    1007           0 :             verbosity = request.params[1].getInt<int>();
    1008             :         }
    1009           0 :     }
    1010             : 
    1011           0 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
    1012             : 
    1013             :     const CBlockIndex* pblockindex;
    1014             :     const CBlockIndex* tip;
    1015           0 :     ChainstateManager& chainman = EnsureChainman(node);
    1016             :     {
    1017           0 :         LOCK(cs_main);
    1018           0 :         pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
    1019           0 :         tip = chainman.ActiveChain().Tip();
    1020             : 
    1021           0 :         if (!pblockindex) {
    1022           0 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
    1023             :         }
    1024           0 :     }
    1025             : 
    1026           0 :     const CBlock block{GetBlockChecked(chainman.m_blockman, pblockindex)};
    1027             : 
    1028           0 :     if (verbosity <= 0)
    1029             :     {
    1030           0 :         CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
    1031           0 :         ssBlock << block;
    1032           0 :         std::string strHex = HexStr(ssBlock);
    1033           0 :         return strHex;
    1034           0 :     }
    1035             : 
    1036           0 :     const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
    1037           0 :     CHECK_NONFATAL(node.chainlocks);
    1038             :     TxVerbosity tx_verbosity;
    1039           0 :     if (verbosity == 1) {
    1040           0 :         tx_verbosity = TxVerbosity::SHOW_TXID;
    1041           0 :     } else if (verbosity == 2) {
    1042           0 :         tx_verbosity = TxVerbosity::SHOW_DETAILS;
    1043           0 :     } else {
    1044           0 :         tx_verbosity = TxVerbosity::SHOW_DETAILS_AND_PREVOUT;
    1045             :     }
    1046             : 
    1047           0 :     return blockToJSON(chainman.m_blockman, block, tip, pblockindex, *node.chainlocks, *llmq_ctx.isman, tx_verbosity);
    1048           0 : },
    1049             :     };
    1050           0 : }
    1051             : 
    1052          92 : static RPCHelpMan pruneblockchain()
    1053             : {
    1054         184 :     return RPCHelpMan{"pruneblockchain", "",
    1055         184 :                 {
    1056          92 :                     {"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          92 :                 RPCResult{
    1060          92 :                     RPCResult::Type::NUM, "", "Height of the last block pruned"},
    1061          92 :                 RPCExamples{
    1062          92 :                     HelpExampleCli("pruneblockchain", "1000")
    1063          92 :             + HelpExampleRpc("pruneblockchain", "1000")
    1064             :                 },
    1065          92 :         [&](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           0 : CoinStatsHashType ParseHashType(const std::string& hash_type_input)
    1112             : {
    1113           0 :     if (hash_type_input == "hash_serialized_2") {
    1114           0 :         return CoinStatsHashType::HASH_SERIALIZED;
    1115           0 :     } else if (hash_type_input == "muhash") {
    1116           0 :         return CoinStatsHashType::MUHASH;
    1117           0 :     } else if (hash_type_input == "none") {
    1118           0 :         return CoinStatsHashType::NONE;
    1119             :     } else {
    1120           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("'%s' is not a valid hash_type", hash_type_input));
    1121             :     }
    1122           0 : }
    1123             : 
    1124           9 : 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           9 :     if ((hash_type == kernel::CoinStatsHashType::MUHASH || hash_type == kernel::CoinStatsHashType::NONE) && g_coin_stats_index && index_requested) {
    1132           0 :         if (pindex) {
    1133           0 :             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           9 :     CHECK_NONFATAL(!pindex || pindex->GetBlockHash() == view->GetBestBlock());
    1145             : 
    1146           9 :     return kernel::ComputeUTXOStats(hash_type, view, blockman, interruption_point);
    1147           9 : }
    1148             : 
    1149          92 : static RPCHelpMan gettxoutsetinfo()
    1150             : {
    1151         184 :     return RPCHelpMan{"gettxoutsetinfo",
    1152          92 :                 "\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         368 :                 {
    1155          92 :                     {"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          92 :                     {"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          92 :                     {"use_index", RPCArg::Type::BOOL, RPCArg::Default{true}, "Use coinstatsindex, if available."},
    1158             :                 },
    1159          92 :                 RPCResult{
    1160          92 :                     RPCResult::Type::OBJ, "", "",
    1161        1104 :                     {
    1162          92 :                         {RPCResult::Type::NUM, "height", "The block height (index) of the returned statistics"},
    1163          92 :                         {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at which these statistics are calculated"},
    1164          92 :                         {RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs"},
    1165          92 :                         {RPCResult::Type::NUM, "bogosize", "Database-independent, meaningless metric indicating the UTXO set size"},
    1166          92 :                         {RPCResult::Type::STR_HEX, "hash_serialized_2", /*optional=*/true, "The serialized hash (only present if 'hash_serialized_2' hash_type is chosen)"},
    1167          92 :                         {RPCResult::Type::STR_HEX, "muhash", /*optional=*/true, "The serialized hash (only present if 'muhash' hash_type is chosen)"},
    1168          92 :                         {RPCResult::Type::NUM, "transactions", /*optional=*/true, "The number of transactions with unspent outputs (not available when coinstatsindex is used)"},
    1169          92 :                         {RPCResult::Type::NUM, "disk_size", /*optional=*/true, "The estimated size of the chainstate on disk (not available when coinstatsindex is used)"},
    1170          92 :                         {RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount of coins in the UTXO set"},
    1171          92 :                         {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         184 :                         {RPCResult::Type::OBJ, "block_info", /*optional=*/true, "Info on amounts in the block at this block height (only available if coinstatsindex is used)",
    1173         552 :                         {
    1174          92 :                             {RPCResult::Type::STR_AMOUNT, "prevout_spent", "Total amount of all prevouts spent in this block"},
    1175          92 :                             {RPCResult::Type::STR_AMOUNT, "coinbase", "Coinbase subsidy amount of this block"},
    1176          92 :                             {RPCResult::Type::STR_AMOUNT, "new_outputs_ex_coinbase", "Total amount of new outputs created by this block"},
    1177          92 :                             {RPCResult::Type::STR_AMOUNT, "unspendable", "Total amount of unspendable outputs created in this block"},
    1178         184 :                             {RPCResult::Type::OBJ, "unspendables", "Detailed view of the unspendable categories",
    1179         460 :                             {
    1180          92 :                                 {RPCResult::Type::STR_AMOUNT, "genesis_block", "The unspendable amount of the Genesis block subsidy"},
    1181          92 :                                 {RPCResult::Type::STR_AMOUNT, "bip30", "Transactions overridden by duplicates (no longer possible with BIP30)"},
    1182          92 :                                 {RPCResult::Type::STR_AMOUNT, "scripts", "Amounts sent to scripts that are unspendable (for example OP_RETURN outputs)"},
    1183          92 :                                 {RPCResult::Type::STR_AMOUNT, "unclaimed_rewards", "Fee rewards that miners did not claim in their coinbase transaction"},
    1184             :                             }}
    1185             :                         }},
    1186             :                     }},
    1187          92 :                 RPCExamples{
    1188         184 :                     HelpExampleCli("gettxoutsetinfo", "") +
    1189         184 :                     HelpExampleCli("gettxoutsetinfo", R"("none")") +
    1190         184 :                     HelpExampleCli("gettxoutsetinfo", R"("none" 1000)") +
    1191         184 :                     HelpExampleCli("gettxoutsetinfo", R"("none" '"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09"')") +
    1192         184 :                     HelpExampleCli("-named gettxoutsetinfo", R"(hash_type='muhash' use_index='false')") +
    1193         184 :                     HelpExampleRpc("gettxoutsetinfo", "") +
    1194         184 :                     HelpExampleRpc("gettxoutsetinfo", R"("none")") +
    1195         184 :                     HelpExampleRpc("gettxoutsetinfo", R"("none", 1000)") +
    1196          92 :                     HelpExampleRpc("gettxoutsetinfo", R"("none", "00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09")")
    1197             :                 },
    1198          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    1199             : {
    1200           0 :     UniValue ret(UniValue::VOBJ);
    1201             : 
    1202           0 :     const CBlockIndex* pindex{nullptr};
    1203           0 :     const CoinStatsHashType hash_type{request.params[0].isNull() ? CoinStatsHashType::HASH_SERIALIZED : ParseHashType(request.params[0].get_str())};
    1204           0 :     bool index_requested = request.params[2].isNull() || request.params[2].get_bool();
    1205             : 
    1206           0 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
    1207           0 :     ChainstateManager& chainman = EnsureChainman(node);
    1208           0 :     CChainState& active_chainstate = chainman.ActiveChainstate();
    1209           0 :     active_chainstate.ForceFlushStateToDisk();
    1210             : 
    1211             :     CCoinsView* coins_view;
    1212             :     BlockManager* blockman;
    1213             :     {
    1214           0 :         LOCK(::cs_main);
    1215           0 :         coins_view = &active_chainstate.CoinsDB();
    1216           0 :         blockman = &active_chainstate.m_blockman;
    1217           0 :         pindex = blockman->LookupBlockIndex(coins_view->GetBestBlock());
    1218           0 :     }
    1219             : 
    1220           0 :     if (!request.params[1].isNull()) {
    1221           0 :         if (!g_coin_stats_index) {
    1222           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Querying specific block heights requires coinstatsindex");
    1223             :         }
    1224             : 
    1225           0 :         if (hash_type == CoinStatsHashType::HASH_SERIALIZED) {
    1226           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "hash_serialized_2 hash type cannot be queried for a specific block");
    1227             :         }
    1228             : 
    1229           0 :         if (!index_requested) {
    1230           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot set use_index to false when querying for a specific block");
    1231             :         }
    1232           0 :         pindex = ParseHashOrHeight(request.params[1], chainman);
    1233           0 :     }
    1234             : 
    1235           0 :     if (index_requested && g_coin_stats_index) {
    1236           0 :         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           0 :     }
    1246             : 
    1247           0 :     const std::optional<CCoinsStats> maybe_stats = GetUTXOStats(coins_view, *blockman, hash_type, node.rpc_interruption_point, pindex, index_requested);
    1248           0 :     if (maybe_stats.has_value()) {
    1249           0 :         const CCoinsStats& stats = maybe_stats.value();
    1250           0 :         ret.pushKV("height", (int64_t)stats.nHeight);
    1251           0 :         ret.pushKV("bestblock", stats.hashBlock.GetHex());
    1252           0 :         ret.pushKV("txouts", (int64_t)stats.nTransactionOutputs);
    1253           0 :         ret.pushKV("bogosize", (int64_t)stats.nBogoSize);
    1254           0 :         if (hash_type == CoinStatsHashType::HASH_SERIALIZED) {
    1255           0 :             ret.pushKV("hash_serialized_2", stats.hashSerialized.GetHex());
    1256           0 :         }
    1257           0 :         if (hash_type == CoinStatsHashType::MUHASH) {
    1258           0 :             ret.pushKV("muhash", stats.hashSerialized.GetHex());
    1259           0 :         }
    1260           0 :         CHECK_NONFATAL(stats.total_amount.has_value());
    1261           0 :         ret.pushKV("total_amount", ValueFromAmount(stats.total_amount.value()));
    1262           0 :         if (!stats.index_used) {
    1263           0 :             ret.pushKV("transactions", static_cast<int64_t>(stats.nTransactions));
    1264           0 :             ret.pushKV("disk_size", stats.nDiskSize);
    1265           0 :         } else {
    1266           0 :             ret.pushKV("total_unspendable_amount", ValueFromAmount(stats.total_unspendable_amount));
    1267             : 
    1268           0 :             CCoinsStats prev_stats{};
    1269           0 :             if (pindex->nHeight > 0) {
    1270           0 :                 const std::optional<CCoinsStats> maybe_prev_stats = GetUTXOStats(coins_view, *blockman, hash_type, node.rpc_interruption_point, pindex->pprev, index_requested);
    1271           0 :                 if (!maybe_prev_stats) {
    1272           0 :                     throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
    1273             :                 }
    1274           0 :                 prev_stats = maybe_prev_stats.value();
    1275           0 :             }
    1276             : 
    1277           0 :             UniValue block_info(UniValue::VOBJ);
    1278           0 :             block_info.pushKV("prevout_spent", ValueFromAmount(stats.total_prevout_spent_amount - prev_stats.total_prevout_spent_amount));
    1279           0 :             block_info.pushKV("coinbase", ValueFromAmount(stats.total_coinbase_amount - prev_stats.total_coinbase_amount));
    1280           0 :             block_info.pushKV("new_outputs_ex_coinbase", ValueFromAmount(stats.total_new_outputs_ex_coinbase_amount - prev_stats.total_new_outputs_ex_coinbase_amount));
    1281           0 :             block_info.pushKV("unspendable", ValueFromAmount(stats.total_unspendable_amount - prev_stats.total_unspendable_amount));
    1282             : 
    1283           0 :             UniValue unspendables(UniValue::VOBJ);
    1284           0 :             unspendables.pushKV("genesis_block", ValueFromAmount(stats.total_unspendables_genesis_block - prev_stats.total_unspendables_genesis_block));
    1285           0 :             unspendables.pushKV("bip30", ValueFromAmount(stats.total_unspendables_bip30 - prev_stats.total_unspendables_bip30));
    1286           0 :             unspendables.pushKV("scripts", ValueFromAmount(stats.total_unspendables_scripts - prev_stats.total_unspendables_scripts));
    1287           0 :             unspendables.pushKV("unclaimed_rewards", ValueFromAmount(stats.total_unspendables_unclaimed_rewards - prev_stats.total_unspendables_unclaimed_rewards));
    1288           0 :             block_info.pushKV("unspendables", unspendables);
    1289             : 
    1290           0 :             ret.pushKV("block_info", block_info);
    1291           0 :         }
    1292           0 :     } else {
    1293           0 :         throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
    1294             :     }
    1295           0 :     return ret;
    1296           0 : },
    1297             :     };
    1298           0 : }
    1299             : 
    1300          92 : static RPCHelpMan gettxout()
    1301             : {
    1302         184 :     return RPCHelpMan{"gettxout",
    1303          92 :         "\nReturns details about an unspent transaction output.\n",
    1304         368 :         {
    1305          92 :             {"txid", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction id"},
    1306          92 :             {"n", RPCArg::Type::NUM, RPCArg::Optional::NO, "vout number"},
    1307          92 :             {"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         276 :         {
    1310          92 :             RPCResult{"If the UTXO was not found", RPCResult::Type::NONE, "", ""},
    1311         552 :             RPCResult{"Otherwise", RPCResult::Type::OBJ, "", "", {
    1312          92 :                 {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"},
    1313          92 :                 {RPCResult::Type::NUM, "confirmations", "The number of confirmations"},
    1314          92 :                 {RPCResult::Type::STR_AMOUNT, "value", "The transaction value in " + CURRENCY_UNIT},
    1315         552 :                 {RPCResult::Type::OBJ, "scriptPubKey", "", {
    1316          92 :                     {RPCResult::Type::STR, "asm", "Disassembly of the public key script"},
    1317          92 :                     {RPCResult::Type::STR, "desc", "Inferred descriptor for the output"},
    1318          92 :                     {RPCResult::Type::STR_HEX, "hex", "The raw public key script bytes, hex-encoded"},
    1319          92 :                     {RPCResult::Type::STR_HEX, "type", "The type, eg pubkeyhash"},
    1320          92 :                     {RPCResult::Type::STR, "address", /*optional=*/ true, "Dash address (only if a well-defined address exists)"},
    1321             :                 }},
    1322          92 :                 {RPCResult::Type::BOOL, "coinbase", "Coinbase or not"},
    1323             :             }},
    1324             :         },
    1325          92 :         RPCExamples{
    1326             :             "\nGet unspent transactions\n"
    1327          92 :             + HelpExampleCli("listunspent", "") +
    1328             :             "\nView the details\n"
    1329          92 :             + HelpExampleCli("gettxout", "\"txid\" 1") +
    1330             :             "\nAs a JSON-RPC call\n"
    1331          92 :             + HelpExampleRpc("gettxout", "\"txid\", 1")
    1332             :                 },
    1333          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    1334             : {
    1335             : 
    1336           0 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
    1337             : 
    1338           0 :     ChainstateManager& chainman = EnsureChainman(node);
    1339           0 :     LOCK(cs_main);
    1340             : 
    1341           0 :     CChainState& active_chainstate = chainman.ActiveChainstate();
    1342             : 
    1343           0 :     UniValue ret(UniValue::VOBJ);
    1344             : 
    1345           0 :     uint256 hash(ParseHashV(request.params[0], "txid"));
    1346           0 :     COutPoint out{hash, request.params[1].getInt<uint32_t>()};
    1347           0 :     bool fMempool = true;
    1348           0 :     if (!request.params[2].isNull())
    1349           0 :         fMempool = request.params[2].get_bool();
    1350             : 
    1351           0 :     Coin coin;
    1352           0 :     CCoinsViewCache* coins_view = &active_chainstate.CoinsTip();
    1353             : 
    1354           0 :     if (fMempool) {
    1355           0 :         const CTxMemPool& mempool = EnsureMemPool(node);
    1356           0 :         LOCK(mempool.cs);
    1357           0 :         CCoinsViewMemPool view(coins_view, mempool);
    1358           0 :         if (!view.GetCoin(out, coin) || mempool.isSpent(out)) {
    1359           0 :             return UniValue::VNULL;
    1360             :         }
    1361           0 :     } else {
    1362           0 :         if (!coins_view->GetCoin(out, coin)) {
    1363           0 :             return UniValue::VNULL;
    1364             :         }
    1365             :     }
    1366             : 
    1367           0 :     const CBlockIndex* pindex = active_chainstate.m_blockman.LookupBlockIndex(coins_view->GetBestBlock());
    1368           0 :     ret.pushKV("bestblock", pindex->GetBlockHash().GetHex());
    1369           0 :     if (coin.nHeight == MEMPOOL_HEIGHT) {
    1370           0 :         ret.pushKV("confirmations", 0);
    1371           0 :     } else {
    1372           0 :         ret.pushKV("confirmations", (int64_t)(pindex->nHeight - coin.nHeight + 1));
    1373             :     }
    1374           0 :     ret.pushKV("value", ValueFromAmount(coin.out.nValue));
    1375           0 :     UniValue o(UniValue::VOBJ);
    1376           0 :     ScriptToUniv(coin.out.scriptPubKey, /*out=*/o, /*include_hex=*/true, /*include_address=*/true);
    1377           0 :     ret.pushKV("scriptPubKey", o);
    1378           0 :     ret.pushKV("coinbase", (bool)coin.fCoinBase);
    1379             : 
    1380           0 :     return ret;
    1381           0 : },
    1382             :     };
    1383           0 : }
    1384             : 
    1385          92 : static RPCHelpMan verifychain()
    1386             : {
    1387         184 :     return RPCHelpMan{"verifychain",
    1388          92 :                 "\nVerifies blockchain database.\n",
    1389         276 :                 {
    1390         184 :                     {"checklevel", RPCArg::Type::NUM, RPCArg::DefaultHint{strprintf("%d, range=0-4", DEFAULT_CHECKLEVEL)},
    1391          92 :                         strprintf("How thorough the block verification is:\n - %s", MakeUnorderedList(CHECKLEVEL_DOC))},
    1392          92 :                     {"nblocks", RPCArg::Type::NUM, RPCArg::DefaultHint{strprintf("%d, 0=all", DEFAULT_CHECKBLOCKS)}, "The number of blocks to check."},
    1393             :                 },
    1394          92 :                 RPCResult{
    1395          92 :                     RPCResult::Type::BOOL, "", "Verified or not"},
    1396          92 :                 RPCExamples{
    1397          92 :                     HelpExampleCli("verifychain", "")
    1398          92 :             + HelpExampleRpc("verifychain", "")
    1399             :                 },
    1400          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    1401             : {
    1402           0 :     const int check_level{request.params[0].isNull() ? DEFAULT_CHECKLEVEL : request.params[0].getInt<int>()};
    1403           0 :     const int check_depth{request.params[1].isNull() ? DEFAULT_CHECKBLOCKS : request.params[1].getInt<int>()};
    1404             : 
    1405           0 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
    1406             : 
    1407           0 :     ChainstateManager& chainman = EnsureChainman(node);
    1408           0 :     LOCK(cs_main);
    1409             : 
    1410           0 :     CChainState& active_chainstate = chainman.ActiveChainstate();
    1411           0 :     return CVerifyDB().VerifyDB(
    1412           0 :         active_chainstate, Params().GetConsensus(), active_chainstate.CoinsTip(), *CHECK_NONFATAL(node.evodb), check_level, check_depth);
    1413           0 : },
    1414             :     };
    1415           0 : }
    1416             : 
    1417           0 : static void SoftForkDescPushBack(const CBlockIndex* active_chain_tip, UniValue& softforks, const ChainstateManager& chainman, Consensus::BuriedDeployment dep)
    1418             : {
    1419             :     // For buried deployments.
    1420             : 
    1421           0 :     if (!DeploymentEnabled(chainman, dep)) return;
    1422             : 
    1423           0 :     UniValue rv(UniValue::VOBJ);
    1424           0 :     rv.pushKV("type", "buried");
    1425             :     // getblockchaininfo reports the softfork as active from when the chain height is
    1426             :     // one below the activation height
    1427           0 :     rv.pushKV("active", DeploymentActiveAfter(active_chain_tip, chainman.GetConsensus(), dep));
    1428           0 :     rv.pushKV("height", chainman.GetConsensus().DeploymentHeight(dep));
    1429           0 :     softforks.pushKV(DeploymentName(dep), rv);
    1430           0 : }
    1431             : 
    1432           0 : 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           0 :     if (!DeploymentEnabled(chainman, id)) return;
    1437             : 
    1438           0 :     UniValue bip9(UniValue::VOBJ);
    1439           0 :     const ThresholdState thresholdState = chainman.m_versionbitscache.State(active_chain_tip, chainman.GetConsensus(), id);
    1440           0 :     switch (thresholdState) {
    1441           0 :     case ThresholdState::DEFINED: bip9.pushKV("status", "defined"); break;
    1442           0 :     case ThresholdState::STARTED: bip9.pushKV("status", "started"); break;
    1443           0 :     case ThresholdState::LOCKED_IN: bip9.pushKV("status", "locked_in"); break;
    1444           0 :     case ThresholdState::ACTIVE: bip9.pushKV("status", "active"); break;
    1445           0 :     case ThresholdState::FAILED: bip9.pushKV("status", "failed"); break;
    1446             :     }
    1447           0 :     const bool has_signal = (ThresholdState::STARTED == thresholdState || ThresholdState::LOCKED_IN == thresholdState);
    1448           0 :     if (has_signal) {
    1449           0 :         bip9.pushKV("bit", chainman.GetConsensus().vDeployments[id].bit);
    1450           0 :     }
    1451           0 :     bip9.pushKV("start_time", chainman.GetConsensus().vDeployments[id].nStartTime);
    1452           0 :     bip9.pushKV("timeout", chainman.GetConsensus().vDeployments[id].nTimeout);
    1453           0 :     bip9.pushKV("min_activation_height", chainman.GetConsensus().vDeployments[id].min_activation_height);
    1454           0 :     bip9.pushKV("ehf", chainman.GetConsensus().vDeployments[id].useEHF);
    1455           0 :     if (auto it = signals.find(chainman.GetConsensus().vDeployments[id].bit); it != signals.end()) {
    1456           0 :         bip9.pushKV("ehf_height", it->second);
    1457           0 :     }
    1458           0 :     int64_t since_height = chainman.m_versionbitscache.StateSinceHeight(active_chain_tip, chainman.GetConsensus(), id);
    1459           0 :     bip9.pushKV("since", since_height);
    1460           0 :     if (has_signal) {
    1461           0 :         UniValue statsUV(UniValue::VOBJ);
    1462           0 :         BIP9Stats statsStruct = chainman.m_versionbitscache.Statistics(active_chain_tip, chainman.GetConsensus(), id);
    1463           0 :         statsUV.pushKV("period", statsStruct.period);
    1464           0 :         statsUV.pushKV("elapsed", statsStruct.elapsed);
    1465           0 :         statsUV.pushKV("count", statsStruct.count);
    1466           0 :         if (ThresholdState::LOCKED_IN != thresholdState) {
    1467           0 :             statsUV.pushKV("threshold", statsStruct.threshold);
    1468           0 :             statsUV.pushKV("possible", statsStruct.possible);
    1469           0 :         }
    1470           0 :         bip9.pushKV("statistics", statsUV);
    1471           0 :     }
    1472           0 :     if (ThresholdState::LOCKED_IN == thresholdState) {
    1473           0 :         bip9.pushKV("activation_height", since_height + static_cast<int>(chainman.GetConsensus().vDeployments[id].nWindowSize));
    1474           0 :     }
    1475           0 :     bip9.pushKV("min_activation_height", chainman.GetConsensus().vDeployments[id].min_activation_height);
    1476             : 
    1477           0 :     UniValue rv(UniValue::VOBJ);
    1478           0 :     rv.pushKV("type", "bip9");
    1479           0 :     rv.pushKV("bip9", bip9);
    1480           0 :     if (ThresholdState::ACTIVE == thresholdState) {
    1481           0 :         rv.pushKV("height", since_height);
    1482           0 :     }
    1483           0 :     rv.pushKV("active", ThresholdState::ACTIVE == thresholdState);
    1484             : 
    1485           0 :     softforks.pushKV(DeploymentName(id), rv);
    1486           0 : }
    1487             : 
    1488          92 : RPCHelpMan getblockchaininfo()
    1489             : {
    1490         184 :     return RPCHelpMan{"getblockchaininfo",
    1491          92 :         "Returns an object containing various state info regarding blockchain processing.\n",
    1492          92 :         {},
    1493          92 :         RPCResult{
    1494          92 :             RPCResult::Type::OBJ, "", "",
    1495        1656 :             {
    1496          92 :                 {RPCResult::Type::STR, "chain", "current network name (main, test, regtest) and "
    1497             :                                                 "devnet or devnet-<name> for \"-devnet\" and \"-devnet=<name>\" respectively\n"},
    1498          92 :                 {RPCResult::Type::NUM, "blocks", "the height of the most-work fully-validated chain. The genesis block has height 0"},
    1499          92 :                 {RPCResult::Type::NUM, "headers", "the current number of headers we have validated"},
    1500          92 :                 {RPCResult::Type::STR, "bestblockhash", "the hash of the currently best block"},
    1501          92 :                 {RPCResult::Type::NUM, "difficulty", "the current difficulty"},
    1502          92 :                 {RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME},
    1503          92 :                 {RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME},
    1504          92 :                 {RPCResult::Type::NUM, "verificationprogress", "estimate of verification progress [0..1]"},
    1505          92 :                 {RPCResult::Type::BOOL, "initialblockdownload", "(debug information) estimate of whether this node is in Initial Block Download mode"},
    1506          92 :                 {RPCResult::Type::STR_HEX, "chainwork", "total amount of work in active chain, in hexadecimal"},
    1507          92 :                 {RPCResult::Type::NUM, "size_on_disk", "the estimated size of the block and undo files on disk"},
    1508          92 :                 {RPCResult::Type::BOOL, "pruned", "if the blocks are subject to pruning"},
    1509          92 :                 {RPCResult::Type::NUM, "pruneheight", /*optional=*/true, "height of the last block pruned, plus one (only present if pruning is enabled)"},
    1510          92 :                 {RPCResult::Type::BOOL, "automatic_pruning", /*optional=*/true, "whether automatic pruning is enabled (only present if pruning is enabled)"},
    1511          92 :                 {RPCResult::Type::NUM, "prune_target_size", /*optional=*/true, "the target size used by pruning (only present if automatic pruning is enabled)"},
    1512         184 :                 {RPCResult::Type::OBJ_DYN, "softforks", "status of softforks in progress",
    1513         184 :                 {
    1514         184 :                     {RPCResult::Type::OBJ, "xxxx", "name of the softfork",
    1515         460 :                     {
    1516          92 :                         {RPCResult::Type::STR, "type", "one of \"buried\", \"bip9\""},
    1517         184 :                         {RPCResult::Type::OBJ, "bip9", /*optional=*/true, "status of bip9 softforks (only for \"bip9\" type)",
    1518        1012 :                         {
    1519          92 :                             {RPCResult::Type::STR, "status", "one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\""},
    1520          92 :                             {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          92 :                             {RPCResult::Type::NUM_TIME, "start_time", "the minimum median time past of a block at which the bit gains its meaning"},
    1522          92 :                             {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          92 :                             {RPCResult::Type::BOOL, "ehf", "returns true for EHF activated forks"},
    1524          92 :                             {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          92 :                             {RPCResult::Type::NUM, "since", "height of the first block to which the status applies"},
    1526          92 :                             {RPCResult::Type::NUM, "activation_height", "expected activation height for this softfork (only for \"locked_in\" status)"},
    1527          92 :                             {RPCResult::Type::NUM, "min_activation_height", "minimum height of blocks for which the rules may be enforced"},
    1528         184 :                             {RPCResult::Type::OBJ, "statistics", /*optional=*/true, "numeric statistics about signalling for a softfork (only for \"started\" and \"locked_in\" status)",
    1529         552 :                             {
    1530          92 :                                 {RPCResult::Type::NUM, "period", "the length in blocks of the signalling period"},
    1531          92 :                                 {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          92 :                                 {RPCResult::Type::NUM, "elapsed", "the number of blocks elapsed since the beginning of the current period"},
    1533          92 :                                 {RPCResult::Type::NUM, "count", "the number of blocks with the version bit set in the current period"},
    1534          92 :                                 {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          92 :                         {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          92 :                         {RPCResult::Type::BOOL, "active", "true if the rules are enforced for the mempool and the next block"},
    1539             :                     }},
    1540             :                 }},
    1541          92 :                 {RPCResult::Type::STR, "warnings", "any network and blockchain warnings"},
    1542             :             }},
    1543          92 :         RPCExamples{
    1544          92 :             HelpExampleCli("getblockchaininfo", "")
    1545          92 :     + HelpExampleRpc("getblockchaininfo", "")
    1546             :         },
    1547          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    1548             : {
    1549             : 
    1550           0 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
    1551           0 :     const ArgsManager& args{EnsureArgsman(node)};
    1552           0 :     ChainstateManager& chainman = EnsureChainman(node);
    1553             : 
    1554           0 :     LOCK(cs_main);
    1555           0 :     CChainState& active_chainstate = chainman.ActiveChainstate();
    1556             : 
    1557           0 :     const CBlockIndex& tip{*CHECK_NONFATAL(active_chainstate.m_chain.Tip())};
    1558           0 :     const int height{tip.nHeight};
    1559             : 
    1560           0 :     const auto ehfSignals{active_chainstate.ChainHelper().GetSignalsStage(&tip)};
    1561             : 
    1562           0 :     UniValue obj(UniValue::VOBJ);
    1563           0 :     if (args.IsArgSet("-devnet")) {
    1564           0 :         obj.pushKV("chain", args.GetDevNetName());
    1565           0 :     } else {
    1566           0 :         obj.pushKV("chain", Params().NetworkIDString());
    1567             :     }
    1568           0 :     obj.pushKV("blocks", height);
    1569           0 :     obj.pushKV("headers", chainman.m_best_header ? chainman.m_best_header->nHeight : -1);
    1570           0 :     obj.pushKV("bestblockhash", tip.GetBlockHash().GetHex());
    1571           0 :     obj.pushKV("difficulty", GetDifficulty(&tip));
    1572           0 :     obj.pushKV("time", tip.GetBlockTime());
    1573           0 :     obj.pushKV("mediantime", tip.GetMedianTimePast());
    1574           0 :     obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), &tip));
    1575           0 :     obj.pushKV("initialblockdownload", active_chainstate.IsInitialBlockDownload());
    1576           0 :     obj.pushKV("chainwork", tip.nChainWork.GetHex());
    1577           0 :     obj.pushKV("size_on_disk", chainman.m_blockman.CalculateCurrentUsage());
    1578           0 :     obj.pushKV("pruned", node::fPruneMode);
    1579           0 :     if (node::fPruneMode) {
    1580           0 :         obj.pushKV("pruneheight", chainman.m_blockman.GetFirstStoredBlock(tip)->nHeight);
    1581             : 
    1582             :         // if 0, execution bypasses the whole if block.
    1583           0 :         bool automatic_pruning{args.GetIntArg("-prune", 0) != 1};
    1584           0 :         obj.pushKV("automatic_pruning",  automatic_pruning);
    1585           0 :         if (automatic_pruning) {
    1586           0 :             obj.pushKV("prune_target_size",  node::nPruneTarget);
    1587           0 :         }
    1588           0 :     }
    1589             : 
    1590           0 :     UniValue softforks(UniValue::VOBJ);
    1591           0 :     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           0 :         SoftForkDescPushBack(&tip, softforks, chainman, deploy);
    1609             :     }
    1610           0 :     for (auto ehf_deploy : { /* sorted by activation block */
    1611             :                              Consensus::DEPLOYMENT_V24,
    1612             :                              Consensus::DEPLOYMENT_TESTDUMMY }) {
    1613           0 :         SoftForkDescPushBack(&tip, ehfSignals, softforks, chainman, ehf_deploy);
    1614             :     }
    1615           0 :     obj.pushKV("softforks", softforks);
    1616             : 
    1617           0 :     obj.pushKV("warnings", GetWarnings(false).original);
    1618           0 :     return obj;
    1619           0 : },
    1620             :     };
    1621           0 : }
    1622             : 
    1623             : /** Comparison function for sorting the getchaintips heads.  */
    1624             : struct CompareBlocksByHeight
    1625             : {
    1626           0 :     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           0 :         if (a->nHeight != b->nHeight)
    1632           0 :           return (a->nHeight > b->nHeight);
    1633             : 
    1634           0 :         return a < b;
    1635           0 :     }
    1636             : };
    1637             : 
    1638          92 : static RPCHelpMan getchaintips()
    1639             : {
    1640         184 :     return RPCHelpMan{"getchaintips",
    1641          92 :                 "Return information about all known tips in the block tree,"
    1642             :                 " including the main chain as well as orphaned branches.\n",
    1643         276 :                 {
    1644          92 :                     {"count", RPCArg::Type::NUM, RPCArg::Default{INT_MAX}, "only show this much of latest tips"},
    1645          92 :                     {"branchlen", RPCArg::Type::NUM, RPCArg::Default{-1}, "only show tips that have equal or greater length of branch"},
    1646             :                 },
    1647          92 :                 RPCResult{
    1648          92 :                     RPCResult::Type::ARR, "", "",
    1649         184 :                     {{RPCResult::Type::OBJ, "", "",
    1650         736 :                         {
    1651          92 :                             {RPCResult::Type::NUM, "height", "height of the chain tip"},
    1652          92 :                             {RPCResult::Type::STR_HEX, "hash", "block hash of the tip"},
    1653          92 :                             {RPCResult::Type::NUM, "difficulty", "The difficulty"},
    1654          92 :                             {RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the current chain (in hex)"},
    1655          92 :                             {RPCResult::Type::NUM, "branchlen", "zero for main chain, otherwise length of branch connecting the tip to the main chain"},
    1656          92 :                             {RPCResult::Type::STR_HEX, "forkpoint", "same as \"hash\" for the main chain"},
    1657          92 :                             {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          92 :                 RPCExamples{
    1667          92 :                     HelpExampleCli("getchaintips", "")
    1668          92 :             + HelpExampleRpc("getchaintips", "")
    1669             :                 },
    1670          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    1671             : {
    1672             : 
    1673           0 :     ChainstateManager& chainman = EnsureAnyChainman(request.context);
    1674           0 :     LOCK(cs_main);
    1675           0 :     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           0 :     std::set<const CBlockIndex*, CompareBlocksByHeight> setTips;
    1685           0 :     std::set<const CBlockIndex*> setOrphans;
    1686           0 :     std::set<const CBlockIndex*> setPrevs;
    1687             : 
    1688           0 :     for (const auto& [_, block_index] : chainman.BlockIndex()) {
    1689           0 :         if (!active_chain.Contains(&block_index)) {
    1690           0 :             setOrphans.insert(&block_index);
    1691           0 :             setPrevs.insert(block_index.pprev);
    1692           0 :         }
    1693             :     }
    1694             : 
    1695           0 :     for (const auto& orphan : setOrphans) {
    1696           0 :         if (setPrevs.erase(orphan) == 0) {
    1697           0 :             setTips.insert(orphan);
    1698           0 :         }
    1699             :     }
    1700             : 
    1701             :     // Always report the currently active tip.
    1702           0 :     setTips.insert(active_chain.Tip());
    1703             : 
    1704           0 :     int nCountMax{request.params[0].isNull() ? INT_MAX : request.params[0].getInt<int>()};
    1705           0 :     const int nBranchMin{request.params[1].isNull() ? -1: request.params[1].getInt<int>()};
    1706             : 
    1707             :     /* Construct the output array.  */
    1708           0 :     UniValue res(UniValue::VARR);
    1709           0 :     for (const CBlockIndex* block : setTips)
    1710             :     {
    1711           0 :         const CBlockIndex* pindexFork = active_chain.FindFork(block);
    1712           0 :         const int branchLen = block->nHeight - pindexFork->nHeight;
    1713           0 :         if(branchLen < nBranchMin) continue;
    1714             : 
    1715           0 :         if(nCountMax-- < 1) break;
    1716             : 
    1717           0 :         UniValue obj(UniValue::VOBJ);
    1718           0 :         obj.pushKV("height", block->nHeight);
    1719           0 :         obj.pushKV("hash", block->phashBlock->GetHex());
    1720           0 :         obj.pushKV("difficulty", GetDifficulty(block));
    1721           0 :         obj.pushKV("chainwork", block->nChainWork.GetHex());
    1722           0 :         obj.pushKV("branchlen", branchLen);
    1723           0 :         obj.pushKV("forkpoint", pindexFork->phashBlock->GetHex());
    1724             : 
    1725           0 :         std::string status;
    1726           0 :         if (active_chain.Contains(block)) {
    1727             :             // This block is part of the currently active chain.
    1728           0 :             status = "active";
    1729           0 :         } else if (block->nStatus & BLOCK_FAILED_MASK) {
    1730             :             // This block or one of its ancestors is invalid.
    1731           0 :             status = "invalid";
    1732           0 :         } else if (block->nStatus & BLOCK_CONFLICT_CHAINLOCK) {
    1733             :             // This block or one of its ancestors is conflicting with ChainLocks.
    1734           0 :             status = "conflicting";
    1735           0 :         } else if (!block->HaveTxsDownloaded()) {
    1736             :             // This block cannot be connected because full block data for it or one of its parents is missing.
    1737           0 :             status = "headers-only";
    1738           0 :         } 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           0 :             status = "valid-fork";
    1741           0 :         } 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           0 :         obj.pushKV("status", status);
    1749             : 
    1750           0 :         res.push_back(obj);
    1751           0 :     }
    1752             : 
    1753           0 :     return res;
    1754           0 : },
    1755             :     };
    1756           0 : }
    1757             : 
    1758          92 : static RPCHelpMan preciousblock()
    1759             : {
    1760         184 :     return RPCHelpMan{"preciousblock",
    1761          92 :                 "\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         184 :                 {
    1765          92 :                     {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hash of the block to mark as precious"},
    1766             :                 },
    1767          92 :                 RPCResult{RPCResult::Type::NONE, "", ""},
    1768          92 :                 RPCExamples{
    1769          92 :                     HelpExampleCli("preciousblock", "\"blockhash\"")
    1770          92 :             + HelpExampleRpc("preciousblock", "\"blockhash\"")
    1771             :                 },
    1772          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    1773             : {
    1774           0 :     uint256 hash(ParseHashV(request.params[0], "blockhash"));
    1775             :     CBlockIndex* pblockindex;
    1776             : 
    1777           0 :     ChainstateManager& chainman = EnsureAnyChainman(request.context);
    1778             :     {
    1779           0 :         LOCK(cs_main);
    1780           0 :         pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
    1781           0 :         if (!pblockindex) {
    1782           0 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
    1783             :         }
    1784           0 :     }
    1785             : 
    1786           0 :     BlockValidationState state;
    1787           0 :     chainman.ActiveChainstate().PreciousBlock(state, pblockindex);
    1788             : 
    1789           0 :     if (!state.IsValid()) {
    1790           0 :         throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString());
    1791             :     }
    1792             : 
    1793           0 :     return UniValue::VNULL;
    1794           0 : },
    1795             :     };
    1796           0 : }
    1797             : 
    1798          92 : static RPCHelpMan invalidateblock()
    1799             : {
    1800         184 :     return RPCHelpMan{"invalidateblock",
    1801          92 :                 "\nPermanently marks a block as invalid, as if it violated a consensus rule.\n",
    1802         184 :                 {
    1803          92 :                     {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hash of the block to mark as invalid"},
    1804             :                 },
    1805          92 :                 RPCResult{RPCResult::Type::NONE, "", ""},
    1806          92 :                 RPCExamples{
    1807          92 :                     HelpExampleCli("invalidateblock", "\"blockhash\"")
    1808          92 :             + HelpExampleRpc("invalidateblock", "\"blockhash\"")
    1809             :                 },
    1810          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    1811             : {
    1812           0 :     uint256 hash(ParseHashV(request.params[0], "blockhash"));
    1813           0 :     BlockValidationState state;
    1814             : 
    1815             :     CBlockIndex* pblockindex;
    1816           0 :     ChainstateManager& chainman = EnsureAnyChainman(request.context);
    1817             :     {
    1818           0 :         LOCK(cs_main);
    1819           0 :         pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
    1820           0 :         if (!pblockindex) {
    1821           0 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
    1822             :         }
    1823           0 :     }
    1824             : 
    1825           0 :     CChainState& active_chainstate = chainman.ActiveChainstate();
    1826           0 :     active_chainstate.InvalidateBlock(state, pblockindex);
    1827             : 
    1828           0 :     if (state.IsValid()) {
    1829           0 :         active_chainstate.ActivateBestChain(state);
    1830           0 :     }
    1831             : 
    1832           0 :     if (!state.IsValid()) {
    1833           0 :         throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString());
    1834             :     }
    1835             : 
    1836           0 :     return UniValue::VNULL;
    1837           0 : },
    1838             :     };
    1839           0 : }
    1840             : 
    1841          92 : static RPCHelpMan reconsiderblock()
    1842             : {
    1843         184 :     return RPCHelpMan{"reconsiderblock",
    1844          92 :                 "\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         276 :                 {
    1847          92 :                     {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hash of the block to reconsider"},
    1848          92 :                     {"ignore_chainlocks", RPCArg::Type::BOOL, RPCArg::Default{false}, "if true, existing chainlocks will be ignored"},
    1849             :                 },
    1850          92 :                 RPCResult{RPCResult::Type::NONE, "", ""},
    1851          92 :                 RPCExamples{
    1852          92 :                     HelpExampleCli("reconsiderblock", "\"blockhash\"")
    1853          92 :             + HelpExampleRpc("reconsiderblock", "\"blockhash\"")
    1854             :                 },
    1855          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    1856             : {
    1857           0 :     ChainstateManager& chainman = EnsureAnyChainman(request.context);
    1858           0 :     CChainState& active_chainstate = chainman.ActiveChainstate();
    1859             : 
    1860           0 :     uint256 hash(ParseHashV(request.params[0], "blockhash"));
    1861           0 :     const bool ignore_chainlocks{request.params[1].isNull() ? false : request.params[1].get_bool()};
    1862             : 
    1863             :     {
    1864           0 :         LOCK(cs_main);
    1865           0 :         CBlockIndex* pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
    1866           0 :         if (!pblockindex) {
    1867           0 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
    1868             :         }
    1869             : 
    1870           0 :         active_chainstate.ResetBlockFailureFlags(pblockindex, ignore_chainlocks);
    1871           0 :     }
    1872             : 
    1873           0 :     BlockValidationState state;
    1874           0 :     active_chainstate.ActivateBestChain(state);
    1875             : 
    1876           0 :     if (!state.IsValid()) {
    1877           0 :         throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString());
    1878             :     }
    1879             : 
    1880           0 :     return UniValue::VNULL;
    1881           0 : },
    1882             :     };
    1883           0 : }
    1884             : 
    1885          92 : static RPCHelpMan getchaintxstats()
    1886             : {
    1887         184 :     return RPCHelpMan{"getchaintxstats",
    1888          92 :                 "\nCompute statistics about the total number and rate of transactions in the chain.\n",
    1889         276 :                 {
    1890          92 :                     {"nblocks", RPCArg::Type::NUM, RPCArg::DefaultHint{"one month"}, "Size of the window in number of blocks"},
    1891          92 :                     {"blockhash", RPCArg::Type::STR_HEX, RPCArg::DefaultHint{"chain tip"}, "The hash of the block that ends the window."},
    1892             :                 },
    1893          92 :                 RPCResult{
    1894          92 :                     RPCResult::Type::OBJ, "", "",
    1895         828 :                     {
    1896          92 :                         {RPCResult::Type::NUM_TIME, "time", "The timestamp for the final block in the window, expressed in " + UNIX_EPOCH_TIME},
    1897          92 :                         {RPCResult::Type::NUM, "txcount", "The total number of transactions in the chain up to that point"},
    1898          92 :                         {RPCResult::Type::STR_HEX, "window_final_block_hash", "The hash of the final block in the window"},
    1899          92 :                         {RPCResult::Type::NUM, "window_final_block_height", "The height of the final block in the window."},
    1900          92 :                         {RPCResult::Type::NUM, "window_block_count", "Size of the window in number of blocks"},
    1901          92 :                         {RPCResult::Type::NUM, "window_tx_count", /*optional=*/true, "The number of transactions in the window. Only returned if \"window_block_count\" is > 0"},
    1902          92 :                         {RPCResult::Type::NUM, "window_interval", /*optional=*/true, "The elapsed time in the window in seconds. Only returned if \"window_block_count\" is > 0"},
    1903          92 :                         {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          92 :                 RPCExamples{
    1906          92 :                     HelpExampleCli("getchaintxstats", "")
    1907          92 :             + HelpExampleRpc("getchaintxstats", "2016")
    1908             :                 },
    1909          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    1910             : {
    1911           0 :     ChainstateManager& chainman = EnsureAnyChainman(request.context);
    1912             : 
    1913           0 :     CChain& active_chain = chainman.ActiveChain();
    1914             :     const CBlockIndex* pindex;
    1915           0 :     int blockcount = 30 * 24 * 60 * 60 / Params().GetConsensus().nPowTargetSpacing; // By default: 1 month
    1916             : 
    1917           0 :     if (request.params[1].isNull()) {
    1918           0 :         LOCK(cs_main);
    1919           0 :         pindex = active_chain.Tip();
    1920           0 :     } else {
    1921           0 :         uint256 hash(ParseHashV(request.params[1], "blockhash"));
    1922           0 :         LOCK(cs_main);
    1923           0 :         pindex = chainman.m_blockman.LookupBlockIndex(hash);
    1924           0 :         if (!pindex) {
    1925           0 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
    1926             :         }
    1927           0 :         if (!active_chain.Contains(pindex)) {
    1928           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Block is not in main chain");
    1929             :         }
    1930           0 :     }
    1931             : 
    1932           0 :     CHECK_NONFATAL(pindex != nullptr);
    1933             : 
    1934           0 :     if (request.params[0].isNull()) {
    1935           0 :         blockcount = std::max(0, std::min(blockcount, pindex->nHeight - 1));
    1936           0 :     } else {
    1937           0 :         blockcount = request.params[0].getInt<int>();
    1938             : 
    1939           0 :         if (blockcount < 0 || (blockcount > 0 && blockcount >= pindex->nHeight)) {
    1940           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block count: should be between 0 and the block's height - 1");
    1941             :         }
    1942             :     }
    1943             : 
    1944           0 :     const CBlockIndex& past_block{*CHECK_NONFATAL(pindex->GetAncestor(pindex->nHeight - blockcount))};
    1945           0 :     const int64_t nTimeDiff{pindex->GetMedianTimePast() - past_block.GetMedianTimePast()};
    1946           0 :     const int nTxDiff = pindex->nChainTx - past_block.nChainTx;
    1947             : 
    1948           0 :     UniValue ret(UniValue::VOBJ);
    1949           0 :     ret.pushKV("time", (int64_t)pindex->nTime);
    1950           0 :     ret.pushKV("txcount", (int64_t)pindex->nChainTx);
    1951           0 :     ret.pushKV("window_final_block_hash", pindex->GetBlockHash().GetHex());
    1952           0 :     ret.pushKV("window_final_block_height", pindex->nHeight);
    1953           0 :     ret.pushKV("window_block_count", blockcount);
    1954           0 :     if (blockcount > 0) {
    1955           0 :         ret.pushKV("window_tx_count", nTxDiff);
    1956           0 :         ret.pushKV("window_interval", nTimeDiff);
    1957           0 :         if (nTimeDiff > 0) {
    1958           0 :             ret.pushKV("txrate", ((double)nTxDiff) / nTimeDiff);
    1959           0 :         }
    1960           0 :     }
    1961             : 
    1962           0 :     return ret;
    1963           0 : },
    1964             :     };
    1965           0 : }
    1966             : 
    1967             : template<typename T>
    1968           0 : static T CalculateTruncatedMedian(std::vector<T>& scores)
    1969             : {
    1970           0 :     size_t size = scores.size();
    1971           0 :     if (size == 0) {
    1972           0 :         return 0;
    1973             :     }
    1974             : 
    1975           0 :     std::sort(scores.begin(), scores.end());
    1976           0 :     if (size % 2 == 0) {
    1977           0 :         return (scores[size / 2 - 1] + scores[size / 2]) / 2;
    1978             :     } else {
    1979           0 :         return scores[size / 2];
    1980             :     }
    1981           0 : }
    1982             : 
    1983           4 : void CalculatePercentilesBySize(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES], std::vector<std::pair<CAmount, int64_t>>& scores, int64_t total_size)
    1984             : {
    1985           4 :     if (scores.empty()) {
    1986           0 :         return;
    1987             :     }
    1988             : 
    1989           4 :     std::sort(scores.begin(), scores.end());
    1990             : 
    1991             :     // 10th, 25th, 50th, 75th, and 90th percentile weight units.
    1992          20 :     const double weights[NUM_GETBLOCKSTATS_PERCENTILES] = {
    1993          20 :         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           4 :     int64_t next_percentile_index = 0;
    1997           4 :     int64_t cumulative_weight = 0;
    1998         220 :     for (const auto& element : scores) {
    1999         216 :         cumulative_weight += element.second;
    2000         236 :         while (next_percentile_index < NUM_GETBLOCKSTATS_PERCENTILES && cumulative_weight >= weights[next_percentile_index]) {
    2001          20 :             result[next_percentile_index] = element.first;
    2002          20 :             ++next_percentile_index;
    2003             :         }
    2004             :     }
    2005             : 
    2006             :     // Fill any remaining percentiles with the last value.
    2007           4 :     for (int64_t i = next_percentile_index; i < NUM_GETBLOCKSTATS_PERCENTILES; i++) {
    2008           0 :         result[i] = scores.back().first;
    2009           0 :     }
    2010           4 : }
    2011             : 
    2012             : template<typename T>
    2013           0 : static inline bool SetHasKeys(const std::set<T>& set) {return false;}
    2014             : template<typename T, typename Tk, typename... Args>
    2015           0 : static inline bool SetHasKeys(const std::set<T>& set, const Tk& key, const Args&... args)
    2016             : {
    2017           0 :     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          92 : static RPCHelpMan getblockstats()
    2024             : {
    2025         184 :     return RPCHelpMan{"getblockstats",
    2026          92 :                 "\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         276 :                 {
    2029          92 :                     {"hash_or_height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The block hash or height of the target block", "", {"", "string or numeric"}},
    2030         184 :                     {"stats", RPCArg::Type::ARR, RPCArg::DefaultHint{"all values"}, "Values to plot (see result below)",
    2031         276 :                         {
    2032          92 :                             {"height", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Selected statistic"},
    2033          92 :                             {"time", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Selected statistic"},
    2034             :                         },
    2035          92 :                         "stats"},
    2036             :                 },
    2037          92 :                 RPCResult{
    2038          92 :             RPCResult::Type::OBJ, "", "",
    2039        2576 :             {
    2040          92 :                 {RPCResult::Type::NUM, "avgfee", /*optional=*/true, "Average fee in the block"},
    2041          92 :                 {RPCResult::Type::NUM, "avgfeerate", /*optional=*/true, "Average feerate (in duffs per byte)"},
    2042          92 :                 {RPCResult::Type::NUM, "avgtxsize", /*optional=*/true, "Average transaction size"},
    2043          92 :                 {RPCResult::Type::STR_HEX, "blockhash", /*optional=*/true, "The block hash (to check for potential reorgs)"},
    2044         184 :                 {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         552 :                 {
    2046          92 :                     {RPCResult::Type::NUM, "10th_percentile_feerate", "The 10th percentile feerate"},
    2047          92 :                     {RPCResult::Type::NUM, "25th_percentile_feerate", "The 25th percentile feerate"},
    2048          92 :                     {RPCResult::Type::NUM, "50th_percentile_feerate", "The 50th percentile feerate"},
    2049          92 :                     {RPCResult::Type::NUM, "75th_percentile_feerate", "The 75th percentile feerate"},
    2050          92 :                     {RPCResult::Type::NUM, "90th_percentile_feerate", "The 90th percentile feerate"},
    2051             :                 }},
    2052          92 :                 {RPCResult::Type::NUM, "height", /*optional=*/true, "The height of the block"},
    2053          92 :                 {RPCResult::Type::NUM, "ins", /*optional=*/true, "The number of inputs (excluding coinbase)"},
    2054          92 :                 {RPCResult::Type::NUM, "maxfee", /*optional=*/true, "Maximum fee in the block"},
    2055          92 :                 {RPCResult::Type::NUM, "maxfeerate", /*optional=*/true, "Maximum feerate (in duffs per virtual byte)"},
    2056          92 :                 {RPCResult::Type::NUM, "maxtxsize", /*optional=*/true, "Maximum transaction size"},
    2057          92 :                 {RPCResult::Type::NUM, "medianfee", /*optional=*/true, "Truncated median fee in the block"},
    2058          92 :                 {RPCResult::Type::NUM, "mediantime", /*optional=*/true, "The block median time past"},
    2059          92 :                 {RPCResult::Type::NUM, "mediantxsize", /*optional=*/true, "Truncated median transaction size"},
    2060          92 :                 {RPCResult::Type::NUM, "minfee", /*optional=*/true, "Minimum fee in the block"},
    2061          92 :                 {RPCResult::Type::NUM, "minfeerate", /*optional=*/true, "Minimum feerate (in duffs per virtual byte)"},
    2062          92 :                 {RPCResult::Type::NUM, "mintxsize", /*optional=*/true, "Minimum transaction size"},
    2063          92 :                 {RPCResult::Type::NUM, "outs", /*optional=*/true, "The number of outputs"},
    2064          92 :                 {RPCResult::Type::NUM, "subsidy", /*optional=*/true, "The block subsidy"},
    2065          92 :                 {RPCResult::Type::NUM, "time", /*optional=*/true, "The block time"},
    2066          92 :                 {RPCResult::Type::NUM, "total_out", /*optional=*/true, "Total amount in all outputs (excluding coinbase and thus reward [ie subsidy + totalfee])"},
    2067          92 :                 {RPCResult::Type::NUM, "total_size", /*optional=*/true, "Total size of all non-coinbase transactions"},
    2068          92 :                 {RPCResult::Type::NUM, "totalfee", /*optional=*/true, "The fee total"},
    2069          92 :                 {RPCResult::Type::NUM, "txs", /*optional=*/true, "The number of transactions (including coinbase)"},
    2070          92 :                 {RPCResult::Type::NUM, "utxo_increase", /*optional=*/true, "The increase/decrease in the number of unspent outputs  (not discounting op_return and similar)"},
    2071          92 :                 {RPCResult::Type::NUM, "utxo_size_inc", /*optional=*/true, "The increase/decrease in size for the utxo index (not discounting op_return and similar)"},
    2072          92 :                 {RPCResult::Type::NUM, "utxo_increase_actual", /*optional=*/true, "The increase/decrease in the number of unspent outputs, not counting unspendables"},
    2073          92 :                 {RPCResult::Type::NUM, "utxo_size_inc_actual", /*optional=*/true, "The increase/decrease in size for the utxo index, not counting unspendables"},
    2074             :             }},
    2075          92 :                 RPCExamples{
    2076         184 :                     HelpExampleCli("getblockstats", R"('"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09"' '["minfeerate","avgfeerate"]')") +
    2077         184 :                     HelpExampleCli("getblockstats", R"(1000 '["minfeerate","avgfeerate"]')") +
    2078         184 :                     HelpExampleRpc("getblockstats", R"("00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09", ["minfeerate","avgfeerate"])") +
    2079          92 :                     HelpExampleRpc("getblockstats", R"(1000, ["minfeerate","avgfeerate"])")
    2080             :                 },
    2081          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    2082             : {
    2083           0 :     if (g_txindex) {
    2084           0 :         g_txindex->BlockUntilSyncedToCurrentChain();
    2085           0 :     }
    2086             : 
    2087           0 :     ChainstateManager& chainman = EnsureAnyChainman(request.context);
    2088           0 :     const CBlockIndex& pindex{*CHECK_NONFATAL(ParseHashOrHeight(request.params[0], chainman))};
    2089             : 
    2090           0 :     std::set<std::string> stats;
    2091           0 :     if (!request.params[1].isNull()) {
    2092           0 :         const UniValue stats_univalue = request.params[1].get_array();
    2093           0 :         for (unsigned int i = 0; i < stats_univalue.size(); i++) {
    2094           0 :             const std::string stat = stats_univalue[i].get_str();
    2095           0 :             stats.insert(stat);
    2096           0 :         }
    2097           0 :     }
    2098             : 
    2099           0 :     const CBlock& block = GetBlockChecked(chainman.m_blockman, &pindex);
    2100           0 :     const CBlockUndo& blockUndo = GetUndoChecked(chainman.m_blockman, &pindex);
    2101             : 
    2102           0 :     const bool do_all = stats.size() == 0; // Calculate everything if nothing selected (default)
    2103           0 :     const bool do_mediantxsize = do_all || stats.count("mediantxsize") != 0;
    2104           0 :     const bool do_medianfee = do_all || stats.count("medianfee") != 0;
    2105           0 :     const bool do_feerate_percentiles = do_all || stats.count("feerate_percentiles") != 0;
    2106           0 :     const bool loop_inputs = do_all || do_medianfee || do_feerate_percentiles ||
    2107           0 :         SetHasKeys(stats, "utxo_increase", "utxo_increase_actual", "utxo_size_inc", "utxo_size_inc_actual", "totalfee", "avgfee", "avgfeerate", "minfee", "maxfee", "minfeerate", "maxfeerate");
    2108           0 :     const bool loop_outputs = do_all || loop_inputs || stats.count("total_out");
    2109           0 :     const bool do_calculate_size = do_all || do_mediantxsize ||
    2110           0 :         SetHasKeys(stats, "total_size", "avgtxsize", "mintxsize", "maxtxsize", "avgfeerate", "feerate_percentiles", "minfeerate", "maxfeerate");
    2111             : 
    2112           0 :     CAmount maxfee = 0;
    2113           0 :     CAmount maxfeerate = 0;
    2114           0 :     CAmount minfee = MAX_MONEY;
    2115           0 :     CAmount minfeerate = MAX_MONEY;
    2116           0 :     CAmount total_out = 0;
    2117           0 :     CAmount totalfee = 0;
    2118           0 :     int64_t inputs = 0;
    2119           0 :     int64_t maxtxsize = 0;
    2120           0 :     int64_t mintxsize = MaxBlockSize();
    2121           0 :     int64_t outputs = 0;
    2122           0 :     int64_t total_size = 0;
    2123           0 :     int64_t utxos = 0;
    2124           0 :     int64_t utxo_size_inc = 0;
    2125           0 :     int64_t utxo_size_inc_actual = 0;
    2126           0 :     std::vector<CAmount> fee_array;
    2127           0 :     std::vector<std::pair<CAmount, int64_t>> feerate_array;
    2128           0 :     std::vector<int64_t> txsize_array;
    2129             : 
    2130           0 :     for (size_t i = 0; i < block.vtx.size(); ++i) {
    2131           0 :         const auto& tx = block.vtx.at(i);
    2132           0 :         outputs += tx->vout.size();
    2133             : 
    2134           0 :         CAmount tx_total_out = 0;
    2135           0 :         if (loop_outputs) {
    2136           0 :             for (const CTxOut& out : tx->vout) {
    2137           0 :                 tx_total_out += out.nValue;
    2138             : 
    2139           0 :                 size_t out_size = GetSerializeSize(out, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD;
    2140           0 :                 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           0 :                 if (pindex.nHeight == 0 || (IsBIP30Repeat(pindex) && tx->IsCoinBase())) continue;
    2145             :                 // Skip unspendable outputs since they are not included in the UTXO set
    2146           0 :                 if (out.scriptPubKey.IsUnspendable()) continue;
    2147             : 
    2148           0 :                 ++utxos;
    2149           0 :                 utxo_size_inc_actual += out_size;
    2150             :             }
    2151           0 :         }
    2152             : 
    2153           0 :         if (tx->IsCoinBase()) {
    2154           0 :             continue;
    2155             :         }
    2156             : 
    2157           0 :         inputs += tx->vin.size(); // Don't count coinbase's fake input
    2158           0 :         total_out += tx_total_out; // Don't count coinbase reward
    2159             : 
    2160           0 :         int64_t tx_size = 0;
    2161           0 :         if (do_calculate_size) {
    2162             : 
    2163           0 :             tx_size = tx->GetTotalSize();
    2164           0 :             if (do_mediantxsize) {
    2165           0 :                 txsize_array.push_back(tx_size);
    2166           0 :             }
    2167           0 :             maxtxsize = std::max(maxtxsize, tx_size);
    2168           0 :             mintxsize = std::min(mintxsize, tx_size);
    2169           0 :             total_size += tx_size;
    2170           0 :         }
    2171             : 
    2172           0 :         if (loop_inputs) {
    2173           0 :             CAmount tx_total_in = 0;
    2174           0 :             const auto& txundo = blockUndo.vtxundo.at(i - 1);
    2175           0 :             for (const Coin& coin: txundo.vprevout) {
    2176           0 :                 const CTxOut& prevoutput = coin.out;
    2177             : 
    2178           0 :                 tx_total_in += prevoutput.nValue;
    2179           0 :                 size_t prevout_size = GetSerializeSize(prevoutput, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD;
    2180           0 :                 utxo_size_inc -= prevout_size;
    2181           0 :                 utxo_size_inc_actual -= prevout_size;
    2182             :             }
    2183             : 
    2184           0 :             CAmount txfee = tx_total_in - tx_total_out;
    2185             : 
    2186           0 :             if (tx->IsPlatformTransfer()) {
    2187           0 :                 auto payload = GetTxPayload<CAssetUnlockPayload>(*tx);
    2188           0 :                 CHECK_NONFATAL(payload);
    2189           0 :                 txfee = payload->getFee();
    2190           0 :             }
    2191             : 
    2192           0 :             CHECK_NONFATAL(MoneyRange(txfee));
    2193           0 :             if (do_medianfee) {
    2194           0 :                 fee_array.push_back(txfee);
    2195           0 :             }
    2196           0 :             maxfee = std::max(maxfee, txfee);
    2197           0 :             minfee = std::min(minfee, txfee);
    2198           0 :             totalfee += txfee;
    2199             : 
    2200           0 :             CAmount feerate = tx_size ? txfee / tx_size : 0;
    2201           0 :             if (do_feerate_percentiles) {
    2202           0 :                 feerate_array.emplace_back(feerate, tx_size);
    2203           0 :             }
    2204           0 :             maxfeerate = std::max(maxfeerate, feerate);
    2205           0 :             minfeerate = std::min(minfeerate, feerate);
    2206           0 :         }
    2207           0 :     }
    2208             : 
    2209           0 :     CAmount feerate_percentiles[NUM_GETBLOCKSTATS_PERCENTILES] = { 0 };
    2210           0 :     CalculatePercentilesBySize(feerate_percentiles, feerate_array, total_size);
    2211             : 
    2212           0 :     UniValue feerates_res(UniValue::VARR);
    2213           0 :     for (int64_t i = 0; i < NUM_GETBLOCKSTATS_PERCENTILES; i++) {
    2214           0 :         feerates_res.push_back(feerate_percentiles[i]);
    2215           0 :     }
    2216             : 
    2217           0 :     UniValue ret_all(UniValue::VOBJ);
    2218           0 :     ret_all.pushKV("avgfee", (block.vtx.size() > 1) ? totalfee / (block.vtx.size() - 1) : 0);
    2219           0 :     ret_all.pushKV("avgfeerate", total_size ? totalfee / total_size : 0); // Unit: sat/byte
    2220           0 :     ret_all.pushKV("avgtxsize", (block.vtx.size() > 1) ? total_size / (block.vtx.size() - 1) : 0);
    2221           0 :     ret_all.pushKV("blockhash", pindex.GetBlockHash().GetHex());
    2222           0 :     ret_all.pushKV("feerate_percentiles", feerates_res);
    2223           0 :     ret_all.pushKV("height", (int64_t)pindex.nHeight);
    2224           0 :     ret_all.pushKV("ins", inputs);
    2225           0 :     ret_all.pushKV("maxfee", maxfee);
    2226           0 :     ret_all.pushKV("maxfeerate", maxfeerate);
    2227           0 :     ret_all.pushKV("maxtxsize", maxtxsize);
    2228           0 :     ret_all.pushKV("medianfee", CalculateTruncatedMedian(fee_array));
    2229           0 :     ret_all.pushKV("mediantime", pindex.GetMedianTimePast());
    2230           0 :     ret_all.pushKV("mediantxsize", CalculateTruncatedMedian(txsize_array));
    2231           0 :     ret_all.pushKV("minfee", (minfee == MAX_MONEY) ? 0 : minfee);
    2232           0 :     ret_all.pushKV("minfeerate", (minfeerate == MAX_MONEY) ? 0 : minfeerate);
    2233           0 :     ret_all.pushKV("mintxsize", mintxsize == MaxBlockSize() ? 0 : mintxsize);
    2234           0 :     ret_all.pushKV("outs", outputs);
    2235           0 :     ret_all.pushKV("subsidy", GetBlockSubsidy(&pindex, Params().GetConsensus()));
    2236           0 :     ret_all.pushKV("time", pindex.GetBlockTime());
    2237           0 :     ret_all.pushKV("total_out", total_out);
    2238           0 :     ret_all.pushKV("total_size", total_size);
    2239           0 :     ret_all.pushKV("totalfee", totalfee);
    2240           0 :     ret_all.pushKV("txs", (int64_t)block.vtx.size());
    2241           0 :     ret_all.pushKV("utxo_increase", outputs - inputs);
    2242           0 :     ret_all.pushKV("utxo_size_inc", utxo_size_inc);
    2243           0 :     ret_all.pushKV("utxo_increase_actual", utxos - inputs);
    2244           0 :     ret_all.pushKV("utxo_size_inc_actual", utxo_size_inc_actual);
    2245             : 
    2246           0 :     if (do_all) {
    2247           0 :         return ret_all;
    2248             :     }
    2249             : 
    2250           0 :     UniValue ret(UniValue::VOBJ);
    2251           0 :     for (const std::string& stat : stats) {
    2252           0 :         const UniValue& value = ret_all[stat];
    2253           0 :         if (value.isNull()) {
    2254           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid selected statistic '%s'", stat));
    2255             :         }
    2256           0 :         ret.pushKV(stat, value);
    2257             :     }
    2258           0 :     return ret;
    2259           0 : },
    2260             :     };
    2261           0 : }
    2262             : 
    2263          92 : static RPCHelpMan getspecialtxes()
    2264             : {
    2265         184 :     return RPCHelpMan{"getspecialtxes",
    2266          92 :         "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         552 :         {
    2271          92 :             {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
    2272          92 :             {"type", RPCArg::Type::NUM, RPCArg::Default{-1}, "Filter special txes by type, -1 means all types"},
    2273          92 :             {"count", RPCArg::Type::NUM, RPCArg::Default{10}, "The number of transactions to return"},
    2274          92 :             {"skip", RPCArg::Type::NUM, RPCArg::Default{0}, "The number of transactions to skip"},
    2275          92 :             {"verbosity", RPCArg::Type::NUM, RPCArg::Default{0}, "0 for hashes, 1 for hex-encoded data, and 2 for json object"},
    2276             :         },
    2277         368 :         {
    2278         184 :             RPCResult{"for verbosity = 0",
    2279          92 :                 RPCResult::Type::ARR, "", "",
    2280          92 :                     {{RPCResult::Type::STR_HEX, "", "The transaction id"}}},
    2281         184 :             RPCResult{"for verbosity = 1",
    2282          92 :                 RPCResult::Type::ARR, "", "",
    2283          92 :                     {{RPCResult::Type::STR_HEX, "data", "A string that is serialized, hex-encoded data for the transaction"}}},
    2284         184 :             RPCResult{"for verbosity = 2",
    2285          92 :                 RPCResult::Type::ARR, "", "",
    2286          92 :                     {{RPCResult::Type::ELISION, "", "The transactions in the format of the getrawtransaction RPC"}}},
    2287             :         },
    2288          92 :         RPCExamples{
    2289          92 :             HelpExampleCli("getspecialtxes", "\"00000000000fd08c2fb661d2fcb0d49abb3a91e5f27082ce64feed3b4dede2e2\"")
    2290          92 :     + HelpExampleRpc("getspecialtxes", "\"00000000000fd08c2fb661d2fcb0d49abb3a91e5f27082ce64feed3b4dede2e2\"")
    2291             :         },
    2292          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    2293             : {
    2294           0 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
    2295             : 
    2296           0 :     ChainstateManager& chainman = EnsureChainman(node);
    2297           0 :     LOCK(cs_main);
    2298             : 
    2299           0 :     const CTxMemPool& mempool = EnsureMemPool(node);
    2300           0 :     const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
    2301           0 :     CHECK_NONFATAL(node.chainlocks);
    2302             : 
    2303           0 :     const uint256 blockhash(ParseHashV(request.params[0], "blockhash"));
    2304             : 
    2305           0 :     int nTxType = -1;
    2306           0 :     if (!request.params[1].isNull()) {
    2307           0 :         nTxType = request.params[1].getInt<int>();
    2308           0 :     }
    2309             : 
    2310           0 :     int nCount = 10;
    2311           0 :     if (!request.params[2].isNull()) {
    2312           0 :         nCount = request.params[2].getInt<int>();
    2313           0 :         if (nCount < 0)
    2314           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
    2315           0 :     }
    2316             : 
    2317           0 :     int nSkip = 0;
    2318           0 :     if (!request.params[3].isNull()) {
    2319           0 :         nSkip = request.params[3].getInt<int>();
    2320           0 :         if (nSkip < 0)
    2321           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative skip");
    2322           0 :     }
    2323             : 
    2324           0 :     int nVerbosity = 0;
    2325           0 :     if (!request.params[4].isNull()) {
    2326           0 :         nVerbosity = request.params[4].getInt<int>();
    2327           0 :         if (nVerbosity < 0 || nVerbosity > 2) {
    2328           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Verbosity must be in range 0..2");
    2329             :         }
    2330           0 :     }
    2331             : 
    2332           0 :     const CBlockIndex* pblockindex = chainman.m_blockman.LookupBlockIndex(blockhash);
    2333           0 :     if (!pblockindex) {
    2334           0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
    2335             :     }
    2336             : 
    2337           0 :     const CBlock block = GetBlockChecked(chainman.m_blockman, pblockindex);
    2338             : 
    2339           0 :     int nTxNum = 0;
    2340           0 :     UniValue result(UniValue::VARR);
    2341             : 
    2342           0 :     for(const auto& tx : block.vtx)
    2343             :     {
    2344           0 :         if (!tx->HasExtraPayloadField()                   // ensure it's in fact a special tx
    2345           0 :             || (nTxType != -1 && tx->nType != nTxType)) { // ensure special tx type matches filter, if given
    2346           0 :             continue;
    2347             :         }
    2348             : 
    2349           0 :         nTxNum++;
    2350           0 :         if (nTxNum <= nSkip) continue;
    2351           0 :         if (nTxNum > nSkip + nCount) break;
    2352             : 
    2353           0 :         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           0 :                     UniValue objTx(UniValue::VOBJ);
    2360           0 :                     TxToJSON(*tx, blockhash, mempool, chainman.ActiveChainstate(), *node.chainlocks, *llmq_ctx.isman, objTx);
    2361           0 :                     result.push_back(objTx);
    2362             :                     break;
    2363           0 :                 }
    2364           0 :             default : throw JSONRPCError(RPC_INTERNAL_ERROR, "Unsupported verbosity");
    2365             :         }
    2366             :     }
    2367           0 :     return result;
    2368           0 : },
    2369             :     };
    2370           0 : }
    2371             : 
    2372             : namespace {
    2373             : //! Search for a given set of pubkey scripts
    2374           0 : 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           0 :     scan_progress = 0;
    2377           0 :     count = 0;
    2378           0 :     while (cursor->Valid()) {
    2379           0 :         COutPoint key;
    2380           0 :         Coin coin;
    2381           0 :         if (!cursor->GetKey(key) || !cursor->GetValue(coin)) return false;
    2382           0 :         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           0 :         if (count % 256 == 0) {
    2390             :             // update progress reference every 256 item
    2391           0 :             uint32_t high = 0x100 * *key.hash.begin() + *(key.hash.begin() + 1);
    2392           0 :             scan_progress = (int)(high * 100.0 / 65536.0 + 0.5);
    2393           0 :         }
    2394           0 :         if (needles.count(coin.out.scriptPubKey)) {
    2395           0 :             out_results.emplace(key, coin);
    2396           0 :         }
    2397           0 :         cursor->Next();
    2398           0 :     }
    2399           0 :     scan_progress = 100;
    2400           0 :     return true;
    2401           0 : }
    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           0 :     bool m_could_reserve{false};
    2412             : public:
    2413           0 :     explicit CoinsViewScanReserver() = default;
    2414             : 
    2415           0 :     bool reserve() {
    2416           0 :         CHECK_NONFATAL(!m_could_reserve);
    2417           0 :         if (g_scan_in_progress.exchange(true)) {
    2418           0 :             return false;
    2419             :         }
    2420           0 :         CHECK_NONFATAL(g_scan_progress == 0);
    2421           0 :         m_could_reserve = true;
    2422           0 :         return true;
    2423           0 :     }
    2424             : 
    2425           0 :     ~CoinsViewScanReserver() {
    2426           0 :         if (m_could_reserve) {
    2427           0 :             g_scan_in_progress = false;
    2428           0 :             g_scan_progress = 0;
    2429           0 :         }
    2430           0 :     }
    2431             : };
    2432             : 
    2433          92 : static RPCHelpMan scantxoutset()
    2434             : {
    2435             :     // scriptPubKey corresponding to mainnet address XcJSEb79KEeNbwMU7eE27aL5dhDwvjgBuH
    2436          92 :     const std::string EXAMPLE_DESCRIPTOR_RAW = "raw(76a91411b366edfc0a8b66feebae5c2e25a7b6a5d1cf3188ac)#fm24fxxy";
    2437             : 
    2438         184 :     return RPCHelpMan{"scantxoutset",
    2439          92 :         "\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         276 :         {
    2452          92 :             {"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         184 :             {"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         276 :                 {
    2459          92 :                     {"descriptor", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "An output descriptor"},
    2460         184 :                     {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "An object with output descriptor and metadata",
    2461         276 :                         {
    2462          92 :                             {"desc", RPCArg::Type::STR, RPCArg::Optional::NO, "An output descriptor"},
    2463          92 :                             {"range", RPCArg::Type::RANGE, RPCArg::Default{1000}, "The range of HD chain indexes to explore (either end or [begin,end])"},
    2464             :                         },
    2465             :                     },
    2466             :                 },
    2467          92 :                 "[scanobjects,...]"},
    2468             :         },
    2469         460 :         {
    2470         644 :             RPCResult{"when action=='start'; only returns after scan completes", RPCResult::Type::OBJ, "", "", {
    2471          92 :                 {RPCResult::Type::BOOL, "success", "Whether the scan was completed"},
    2472          92 :                 {RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs scanned"},
    2473          92 :                 {RPCResult::Type::NUM, "height", "The current block height (index)"},
    2474          92 :                 {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"},
    2475         184 :                 {RPCResult::Type::ARR, "unspents", "",
    2476         184 :                 {
    2477         184 :                     {RPCResult::Type::OBJ, "", "",
    2478         736 :                     {
    2479          92 :                         {RPCResult::Type::STR_HEX, "txid", "The transaction id"},
    2480          92 :                         {RPCResult::Type::NUM, "vout", "The vout value"},
    2481          92 :                         {RPCResult::Type::STR_HEX, "scriptPubKey", "The script key"},
    2482          92 :                         {RPCResult::Type::STR, "desc", "A specialized descriptor for the matched scriptPubKey"},
    2483          92 :                         {RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " of the unspent output"},
    2484          92 :                         {RPCResult::Type::BOOL, "coinbase", "Whether this is a coinbase output"},
    2485          92 :                         {RPCResult::Type::NUM, "height", "Height of the unspent transaction output"},
    2486             :                     }},
    2487             :                 }},
    2488          92 :                 {RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount of all found unspent outputs in " + CURRENCY_UNIT},
    2489             :             }},
    2490          92 :             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         184 :             RPCResult{"when action=='status' and a scan is currently in progress", RPCResult::Type::OBJ, "", "",
    2492         184 :             {
    2493          92 :                 {RPCResult::Type::NUM, "progress", "Approximate percent complete"},
    2494             :             }},
    2495          92 :             RPCResult{"when action=='status' and no scan is in progress - possibly already completed", RPCResult::Type::NONE, "", ""},
    2496             :         },
    2497          92 :         RPCExamples{
    2498         184 :             HelpExampleCli("scantxoutset", "start \'[\"" + EXAMPLE_DESCRIPTOR_RAW + "\"]\'") +
    2499         184 :             HelpExampleCli("scantxoutset", "status") +
    2500         184 :             HelpExampleCli("scantxoutset", "abort") +
    2501         184 :             HelpExampleRpc("scantxoutset", "\"start\", [\"" + EXAMPLE_DESCRIPTOR_RAW + "\"]") +
    2502         184 :             HelpExampleRpc("scantxoutset", "\"status\"") +
    2503          92 :             HelpExampleRpc("scantxoutset", "\"abort\"")
    2504             :         },
    2505          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    2506             : {
    2507           0 :     RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR});
    2508             : 
    2509           0 :     UniValue result(UniValue::VOBJ);
    2510           0 :     if (request.params[0].get_str() == "status") {
    2511           0 :         CoinsViewScanReserver reserver;
    2512           0 :         if (reserver.reserve()) {
    2513             :             // no scan in progress
    2514           0 :             return UniValue::VNULL;
    2515             :         }
    2516           0 :         result.pushKV("progress", g_scan_progress.load());
    2517           0 :         return result;
    2518           0 :     } else if (request.params[0].get_str() == "abort") {
    2519           0 :         CoinsViewScanReserver reserver;
    2520           0 :         if (reserver.reserve()) {
    2521             :             // reserve was possible which means no scan was running
    2522           0 :             return false;
    2523             :         }
    2524             :         // set the abort flag
    2525           0 :         g_should_abort_scan = true;
    2526           0 :         return true;
    2527           0 :     } else if (request.params[0].get_str() == "start") {
    2528           0 :         CoinsViewScanReserver reserver;
    2529           0 :         if (!reserver.reserve()) {
    2530           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan already in progress, use action \"abort\" or \"status\"");
    2531             :         }
    2532             : 
    2533           0 :         if (request.params.size() < 2) {
    2534           0 :             throw JSONRPCError(RPC_MISC_ERROR, "scanobjects argument is required for the start action");
    2535             :         }
    2536             : 
    2537           0 :         std::set<CScript> needles;
    2538           0 :         std::map<CScript, std::string> descriptors;
    2539           0 :         CAmount total_in = 0;
    2540             : 
    2541             :         // loop through the scan objects
    2542           0 :         for (const UniValue& scanobject : request.params[1].get_array().getValues()) {
    2543           0 :             FlatSigningProvider provider;
    2544           0 :             auto scripts = EvalDescriptorStringOrObject(scanobject, provider);
    2545           0 :             for (CScript& script : scripts) {
    2546           0 :                 std::string inferred = InferDescriptor(script, provider)->ToString();
    2547           0 :                 needles.emplace(script);
    2548           0 :                 descriptors.emplace(std::move(script), std::move(inferred));
    2549           0 :             }
    2550           0 :         }
    2551             : 
    2552             :         // Scan the unspent transaction output set for inputs
    2553           0 :         UniValue unspents(UniValue::VARR);
    2554           0 :         std::vector<CTxOut> input_txos;
    2555           0 :         std::map<COutPoint, Coin> coins;
    2556           0 :         g_should_abort_scan = false;
    2557           0 :         int64_t count = 0;
    2558           0 :         std::unique_ptr<CCoinsViewCursor> pcursor;
    2559             :         const CBlockIndex* tip;
    2560           0 :         NodeContext& node = EnsureAnyNodeContext(request.context);
    2561             :         {
    2562           0 :             ChainstateManager& chainman = EnsureChainman(node);
    2563           0 :             LOCK(cs_main);
    2564           0 :             CChainState& active_chainstate = chainman.ActiveChainstate();
    2565           0 :             active_chainstate.ForceFlushStateToDisk();
    2566           0 :             pcursor = CHECK_NONFATAL(active_chainstate.CoinsDB().Cursor());
    2567           0 :             tip = CHECK_NONFATAL(active_chainstate.m_chain.Tip());
    2568           0 :         }
    2569           0 :         bool res = FindScriptPubKey(g_scan_progress, g_should_abort_scan, count, pcursor.get(), needles, coins, node.rpc_interruption_point);
    2570           0 :         result.pushKV("success", res);
    2571           0 :         result.pushKV("txouts", count);
    2572           0 :         result.pushKV("height", tip->nHeight);
    2573           0 :         result.pushKV("bestblock", tip->GetBlockHash().GetHex());
    2574             : 
    2575           0 :         for (const auto& it : coins) {
    2576           0 :             const COutPoint& outpoint = it.first;
    2577           0 :             const Coin& coin = it.second;
    2578           0 :             const CTxOut& txo = coin.out;
    2579           0 :             input_txos.push_back(txo);
    2580           0 :             total_in += txo.nValue;
    2581             : 
    2582           0 :             UniValue unspent(UniValue::VOBJ);
    2583           0 :             unspent.pushKV("txid", outpoint.hash.GetHex());
    2584           0 :             unspent.pushKV("vout", (int32_t)outpoint.n);
    2585           0 :             unspent.pushKV("scriptPubKey", HexStr(txo.scriptPubKey));
    2586           0 :             unspent.pushKV("desc", descriptors[txo.scriptPubKey]);
    2587           0 :             unspent.pushKV("amount", ValueFromAmount(txo.nValue));
    2588           0 :             unspent.pushKV("coinbase", coin.IsCoinBase());
    2589           0 :             unspent.pushKV("height", (int32_t)coin.nHeight);
    2590             : 
    2591           0 :             unspents.push_back(unspent);
    2592           0 :         }
    2593           0 :         result.pushKV("unspents", unspents);
    2594           0 :         result.pushKV("total_amount", ValueFromAmount(total_in));
    2595           0 :     } else {
    2596           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid command");
    2597             :     }
    2598           0 :     return result;
    2599           0 : },
    2600             :     };
    2601          92 : }
    2602             : 
    2603          92 : static RPCHelpMan getblockfilter()
    2604             : {
    2605         184 :     return RPCHelpMan{"getblockfilter",
    2606          92 :                 "\nRetrieve a BIP 157 content filter for a particular block.\n",
    2607         276 :                 {
    2608          92 :                     {"blockhash", RPCArg::Type::STR, RPCArg::Optional::NO, "The hash of the block"},
    2609          92 :                     {"filtertype", RPCArg::Type::STR, RPCArg::Default{BlockFilterTypeName(BlockFilterType::BASIC_FILTER)}, "The type name of the filter"},
    2610             :                 },
    2611          92 :                 RPCResult{
    2612          92 :                     RPCResult::Type::OBJ, "", "",
    2613         276 :                     {
    2614          92 :                         {RPCResult::Type::STR_HEX, "filter", "the hex-encoded filter data"},
    2615          92 :                         {RPCResult::Type::STR_HEX, "header", "the hex-encoded filter header"},
    2616             :                     }},
    2617          92 :                 RPCExamples{
    2618         184 :                     HelpExampleCli("getblockfilter", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" \"basic\"")+
    2619          92 :                     HelpExampleRpc("getblockfilter", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\", \"basic\"")
    2620             :                 },
    2621          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    2622             : {
    2623           0 :     uint256 block_hash(ParseHashV(request.params[0], "blockhash"));
    2624           0 :     std::string filtertype_name = BlockFilterTypeName(BlockFilterType::BASIC_FILTER);
    2625           0 :     if (!request.params[1].isNull()) {
    2626           0 :         filtertype_name = request.params[1].get_str();
    2627           0 :     }
    2628             : 
    2629             :     BlockFilterType filtertype;
    2630           0 :     if (!BlockFilterTypeByName(filtertype_name, filtertype)) {
    2631           0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unknown filtertype");
    2632             :     }
    2633             : 
    2634           0 :     BlockFilterIndex* index = GetBlockFilterIndex(filtertype);
    2635           0 :     if (!index) {
    2636           0 :         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           0 :         ChainstateManager& chainman = EnsureAnyChainman(request.context);
    2643           0 :         LOCK(cs_main);
    2644           0 :         block_index = chainman.m_blockman.LookupBlockIndex(block_hash);
    2645           0 :         if (!block_index) {
    2646           0 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
    2647             :         }
    2648           0 :         block_was_connected = block_index->IsValid(BLOCK_VALID_SCRIPTS);
    2649           0 :     }
    2650             : 
    2651           0 :     bool index_ready = index->BlockUntilSyncedToCurrentChain();
    2652             : 
    2653           0 :     BlockFilter filter;
    2654           0 :     uint256 filter_header;
    2655           0 :     if (!index->LookupFilter(block_index, filter) ||
    2656           0 :         !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           0 :     UniValue ret(UniValue::VOBJ);
    2675           0 :     ret.pushKV("filter", HexStr(filter.GetEncodedFilter()));
    2676           0 :     ret.pushKV("header", filter_header.GetHex());
    2677           0 :     return ret;
    2678           0 : },
    2679             :     };
    2680           0 : }
    2681             : 
    2682             : /**
    2683             :  * Serialize the UTXO set to a file for loading elsewhere.
    2684             :  *
    2685             :  * @see SnapshotMetadata
    2686             :  */
    2687          92 : static RPCHelpMan dumptxoutset()
    2688             : {
    2689          92 :     return RPCHelpMan{
    2690          92 :         "dumptxoutset",
    2691          92 :         "Write the serialized UTXO set to disk.",
    2692         184 :         {
    2693          92 :             {"path", RPCArg::Type::STR, RPCArg::Optional::NO, "Path to the output file. If relative, will be prefixed by datadir."},
    2694             :         },
    2695          92 :         RPCResult{
    2696          92 :             RPCResult::Type::OBJ, "", "",
    2697         644 :                 {
    2698          92 :                     {RPCResult::Type::NUM, "coins_written", "the number of coins written in the snapshot"},
    2699          92 :                     {RPCResult::Type::STR_HEX, "base_hash", "the hash of the base of the snapshot"},
    2700          92 :                     {RPCResult::Type::NUM, "base_height", "the height of the base of the snapshot"},
    2701          92 :                     {RPCResult::Type::STR, "path", "the absolute path that the snapshot was written to"},
    2702          92 :                     {RPCResult::Type::STR_HEX, "txoutset_hash", "the hash of the UTXO set contents"},
    2703          92 :                     {RPCResult::Type::NUM, "nchaintx", "the number of transactions in the chain up to and including the base block"},
    2704             :                 }
    2705             :         },
    2706          92 :         RPCExamples{
    2707          92 :             HelpExampleCli("dumptxoutset", "utxo.dat")
    2708             :         },
    2709          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    2710             : {
    2711           0 :     const ArgsManager& args{EnsureAnyArgsman(request.context)};
    2712           0 :     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           0 :     const fs::path temppath = fsbridge::AbsPathJoin(args.GetDataDirNet(), fs::u8path(request.params[0].get_str() + ".incomplete"));
    2716             : 
    2717           0 :     if (fs::exists(path)) {
    2718           0 :         throw JSONRPCError(
    2719             :             RPC_INVALID_PARAMETER,
    2720           0 :             path.utf8string() + " already exists. If you are sure this is what you want, "
    2721             :             "move it out of the way first");
    2722             :     }
    2723             : 
    2724           0 :     FILE* file{fsbridge::fopen(temppath, "wb")};
    2725           0 :     AutoFile afile{file};
    2726           0 :     if (afile.IsNull()) {
    2727           0 :         throw JSONRPCError(
    2728             :             RPC_INVALID_PARAMETER,
    2729           0 :             "Couldn't open file " + temppath.utf8string() + " for writing.");
    2730             :     }
    2731             : 
    2732           0 :     NodeContext& node = EnsureAnyNodeContext(request.context);
    2733           0 :     UniValue result = CreateUTXOSnapshot(
    2734           0 :         node, node.chainman->ActiveChainstate(), afile, path, temppath);
    2735           0 :     fs::rename(temppath, path);
    2736             : 
    2737           0 :     result.pushKV("path", path.utf8string());
    2738           0 :     return result;
    2739           0 : },
    2740             :     };
    2741           0 : }
    2742             : 
    2743           9 : UniValue CreateUTXOSnapshot(
    2744             :     NodeContext& node,
    2745             :     CChainState& chainstate,
    2746             :     AutoFile& afile,
    2747             :     const fs::path& path,
    2748             :     const fs::path& temppath)
    2749             : {
    2750           9 :     std::unique_ptr<CCoinsViewCursor> pcursor;
    2751           9 :     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           9 :         LOCK(::cs_main);
    2768             : 
    2769           9 :         chainstate.ForceFlushStateToDisk();
    2770             : 
    2771           9 :         maybe_stats = GetUTXOStats(&chainstate.CoinsDB(), chainstate.m_blockman, CoinStatsHashType::HASH_SERIALIZED, node.rpc_interruption_point);
    2772           9 :         if (!maybe_stats) {
    2773           0 :             throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
    2774             :         }
    2775             : 
    2776           9 :         pcursor = chainstate.CoinsDB().Cursor();
    2777           9 :         tip = CHECK_NONFATAL(chainstate.m_blockman.LookupBlockIndex(maybe_stats->hashBlock));
    2778           9 :     }
    2779             : 
    2780           9 :     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           9 :     SnapshotMetadata metadata{tip->GetBlockHash(), maybe_stats->coins_count, tip->nChainTx};
    2785             : 
    2786           9 :     afile << metadata;
    2787             : 
    2788           9 :     COutPoint key;
    2789           9 :     Coin coin;
    2790           9 :     unsigned int iter{0};
    2791             : 
    2792        1089 :     while (pcursor->Valid()) {
    2793        1080 :         if (iter % 5000 == 0) node.rpc_interruption_point();
    2794        1080 :         ++iter;
    2795        1080 :         if (pcursor->GetKey(key) && pcursor->GetValue(coin)) {
    2796        1080 :             afile << key;
    2797        1080 :             afile << coin;
    2798        1080 :         }
    2799             : 
    2800        1080 :         pcursor->Next();
    2801             :     }
    2802             : 
    2803           9 :     afile.fclose();
    2804             : 
    2805           9 :     UniValue result(UniValue::VOBJ);
    2806           9 :     result.pushKV("coins_written", maybe_stats->coins_count);
    2807           9 :     result.pushKV("base_hash", tip->GetBlockHash().ToString());
    2808           9 :     result.pushKV("base_height", tip->nHeight);
    2809           9 :     result.pushKV("path", path.utf8string());
    2810           9 :     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           9 :     result.pushKV("nchaintx", uint64_t{tip->nChainTx});
    2814           9 :     return result;
    2815           9 : }
    2816             : 
    2817         178 : void RegisterBlockchainRPCCommands(CRPCTable& t)
    2818             : {
    2819        1558 :     static const CRPCCommand commands[]{
    2820          46 :         {"blockchain", &getblockchaininfo},
    2821          46 :         {"blockchain", &getchaintxstats},
    2822          46 :         {"blockchain", &getblockstats},
    2823          46 :         {"blockchain", &getbestblockhash},
    2824          46 :         {"blockchain", &getbestchainlock},
    2825          46 :         {"blockchain", &getblockcount},
    2826          46 :         {"blockchain", &getblock},
    2827          46 :         {"blockchain", &getblockfrompeer},
    2828          46 :         {"blockchain", &getblockhashes},
    2829          46 :         {"blockchain", &getblockhash},
    2830          46 :         {"blockchain", &getblockheader},
    2831          46 :         {"blockchain", &getblockheaders},
    2832          46 :         {"blockchain", &getmerkleblocks},
    2833          46 :         {"blockchain", &getchaintips},
    2834          46 :         {"blockchain", &getdifficulty},
    2835          46 :         {"blockchain", &getspecialtxes},
    2836          46 :         {"blockchain", &gettxout},
    2837          46 :         {"blockchain", &gettxoutsetinfo},
    2838          46 :         {"blockchain", &pruneblockchain},
    2839          46 :         {"blockchain", &verifychain},
    2840          46 :         {"blockchain", &preciousblock},
    2841          46 :         {"blockchain", &scantxoutset},
    2842          46 :         {"blockchain", &getblockfilter},
    2843          46 :         {"hidden", &invalidateblock},
    2844          46 :         {"hidden", &reconsiderblock},
    2845          46 :         {"hidden", &waitfornewblock},
    2846          46 :         {"hidden", &waitforblock},
    2847          46 :         {"hidden", &waitforblockheight},
    2848          46 :         {"hidden", &syncwithvalidationinterfacequeue},
    2849          46 :         {"hidden", &dumptxoutset},
    2850             :     };
    2851        5518 :     for (const auto& c : commands) {
    2852        5340 :         t.appendCommand(c.name, &c);
    2853             :     }
    2854         178 : }

Generated by: LCOV version 1.16