LCOV - code coverage report
Current view: top level - src/rpc - blockchain.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 695 1969 35.3 %
Date: 2023-11-10 23:46:46 Functions: 31 144 21.5 %
Branches: 1542 6682 23.1 %

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

Generated by: LCOV version 1.14