LCOV - code coverage report
Current view: top level - src/rpc - blockchain.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 72 1965 3.7 %
Date: 2024-01-03 14:57:27 Functions: 1 141 0.7 %
Branches: 165 6682 2.5 %

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

Generated by: LCOV version 1.14