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