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