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