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