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