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