LCOV - code coverage report
Current view: top level - src/rpc - blockchain.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 654 1853 35.3 %
Date: 2023-09-26 12:08:55 Functions: 44 137 32.1 %

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

Generated by: LCOV version 1.14