LCOV - code coverage report
Current view: top level - src/rpc - blockchain.cpp (source / functions) Coverage Total Hit
Test: total_coverage.info Lines: 94.3 % 1578 1488
Test Date: 2024-07-04 05:05:02 Functions: 95.2 % 124 118
Branches: 51.4 % 6467 3327

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

Generated by: LCOV version 2.0-1