LCOV - code coverage report
Current view: top level - src/rpc - blockchain.cpp (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 64.9 % 1890 1226
Test Date: 2024-09-01 05:20:30 Functions: 85.4 % 130 111
Branches: 36.0 % 6020 2168

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

Generated by: LCOV version 2.0-1