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