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