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 146 : 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 5 : double GetDifficulty(const CBlockIndex* blockindex)
91 : {
92 5 : CHECK_NONFATAL(blockindex);
93 :
94 5 : return ConvertBitsToDouble(blockindex->nBits);
95 : }
96 :
97 0 : static int ComputeNextBlockAndDepth(const CBlockIndex* tip, const CBlockIndex* blockindex, const CBlockIndex*& next)
98 : {
99 0 : next = tip->GetAncestor(blockindex->nHeight + 1);
100 0 : if (next && next->pprev == blockindex) {
101 0 : return tip->nHeight - blockindex->nHeight + 1;
102 : }
103 0 : next = nullptr;
104 0 : return blockindex == tip ? 1 : -1;
105 0 : }
106 :
107 0 : static const CBlockIndex* ParseHashOrHeight(const UniValue& param, ChainstateManager& chainman)
108 : {
109 0 : LOCK(::cs_main);
110 0 : CChain& active_chain = chainman.ActiveChain();
111 :
112 0 : if (param.isNum()) {
113 0 : const int height{param.getInt<int>()};
114 0 : if (height < 0) {
115 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Target block height %d is negative", height));
116 : }
117 0 : const int current_tip{active_chain.Height()};
118 0 : if (height > current_tip) {
119 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Target block height %d after current tip %d", height, current_tip));
120 : }
121 :
122 0 : return active_chain[height];
123 : } else {
124 0 : const uint256 hash{ParseHashV(param, "hash_or_height")};
125 0 : const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash);
126 :
127 0 : if (!pindex) {
128 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
129 : }
130 :
131 0 : return pindex;
132 : }
133 0 : }
134 :
135 0 : 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 0 : AssertLockNotHeld(cs_main); // For performance reasons
139 :
140 0 : UniValue result(UniValue::VOBJ);
141 0 : result.pushKV("hash", blockindex->GetBlockHash().GetHex());
142 : const CBlockIndex* pnext;
143 0 : int confirmations = ComputeNextBlockAndDepth(tip, blockindex, pnext);
144 0 : result.pushKV("confirmations", confirmations);
145 0 : result.pushKV("height", blockindex->nHeight);
146 0 : result.pushKV("version", blockindex->nVersion);
147 0 : result.pushKV("versionHex", strprintf("%08x", blockindex->nVersion));
148 0 : result.pushKV("merkleroot", blockindex->hashMerkleRoot.GetHex());
149 0 : result.pushKV("time", (int64_t)blockindex->nTime);
150 0 : result.pushKV("mediantime", (int64_t)blockindex->GetMedianTimePast());
151 0 : result.pushKV("nonce", (uint64_t)blockindex->nNonce);
152 0 : result.pushKV("bits", strprintf("%08x", blockindex->nBits));
153 0 : result.pushKV("difficulty", GetDifficulty(blockindex));
154 0 : result.pushKV("chainwork", blockindex->nChainWork.GetHex());
155 0 : result.pushKV("nTx", (uint64_t)blockindex->nTx);
156 :
157 0 : if (blockindex->pprev)
158 0 : result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex());
159 0 : if (pnext)
160 0 : result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex());
161 :
162 0 : result.pushKV("chainlock", chainlocks.HasChainLock(blockindex->nHeight, blockindex->GetBlockHash()));
163 :
164 0 : return result;
165 0 : }
166 :
167 0 : 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 0 : UniValue result = blockheaderToJSON(tip, blockindex, chainlocks);
170 :
171 0 : result.pushKV("size", (int)::GetSerializeSize(block, PROTOCOL_VERSION));
172 0 : UniValue txs(UniValue::VARR);
173 0 : switch (verbosity) {
174 : case TxVerbosity::SHOW_TXID:
175 0 : for (const CTransactionRef& tx : block.vtx) {
176 0 : txs.push_back(tx->GetHash().GetHex());
177 : }
178 0 : break;
179 : case TxVerbosity::SHOW_DETAILS:
180 : case TxVerbosity::SHOW_DETAILS_AND_PREVOUT:
181 0 : CBlockUndo blockUndo;
182 0 : const bool is_not_pruned{WITH_LOCK(::cs_main, return !blockman.IsBlockPruned(blockindex))};
183 0 : const bool have_undo{is_not_pruned && UndoReadFromDisk(blockUndo, blockindex)};
184 :
185 0 : for (size_t i = 0; i < block.vtx.size(); ++i) {
186 0 : const CTransactionRef& tx = block.vtx.at(i);
187 : // coinbase transaction (i.e. i == 0) doesn't have undo data
188 0 : const CTxUndo* txundo = (have_undo && i > 0) ? &blockUndo.vtxundo.at(i - 1) : nullptr;
189 0 : UniValue objTx(UniValue::VOBJ);
190 0 : TxToUniv(*tx, /*block_hash=*/uint256(), /*entry=*/objTx, /*include_hex=*/true, /*serialize_flags=*/0, txundo, verbosity);
191 0 : bool fLocked = isman.IsLocked(tx->GetHash());
192 0 : objTx.pushKV("instantlock", fLocked || result["chainlock"].get_bool());
193 0 : objTx.pushKV("instantlock_internal", fLocked);
194 0 : txs.push_back(objTx);
195 0 : }
196 : break;
197 0 : }
198 :
199 0 : result.pushKV("tx", txs);
200 0 : if (!block.vtx[0]->vExtraPayload.empty()) {
201 0 : if (const auto opt_cbTx = GetTxPayload<CCbTx>(block.vtx[0]->vExtraPayload)) {
202 0 : result.pushKV("cbTx", opt_cbTx->ToJson());
203 0 : }
204 0 : }
205 :
206 0 : return result;
207 0 : }
208 :
209 92 : static RPCHelpMan getblockcount()
210 : {
211 184 : return RPCHelpMan{"getblockcount",
212 92 : "\nReturns the height of the most-work fully-validated chain.\n"
213 : "The genesis block has height 0.\n",
214 92 : {},
215 92 : RPCResult{
216 92 : RPCResult::Type::NUM, "", "The current block count"},
217 92 : RPCExamples{
218 92 : HelpExampleCli("getblockcount", "")
219 92 : + HelpExampleRpc("getblockcount", "")
220 : },
221 92 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
222 : {
223 0 : ChainstateManager& chainman = EnsureAnyChainman(request.context);
224 0 : LOCK(cs_main);
225 0 : return chainman.ActiveChain().Height();
226 0 : },
227 : };
228 0 : }
229 :
230 92 : static RPCHelpMan getbestblockhash()
231 : {
232 184 : return RPCHelpMan{"getbestblockhash",
233 92 : "\nReturns the hash of the best (tip) block in the most-work fully-validated chain.\n",
234 92 : {},
235 92 : RPCResult{
236 92 : RPCResult::Type::STR_HEX, "", "the block hash, hex-encoded"},
237 92 : RPCExamples{
238 92 : HelpExampleCli("getbestblockhash", "")
239 92 : + HelpExampleRpc("getbestblockhash", "")
240 : },
241 92 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
242 : {
243 0 : ChainstateManager& chainman = EnsureAnyChainman(request.context);
244 0 : LOCK(cs_main);
245 0 : return chainman.ActiveChain().Tip()->GetBlockHash().GetHex();
246 0 : },
247 : };
248 0 : }
249 :
250 92 : static RPCHelpMan getbestchainlock()
251 : {
252 184 : return RPCHelpMan{"getbestchainlock",
253 92 : "\nReturns information about the best ChainLock. Throws an error if there is no known ChainLock yet.",
254 92 : {},
255 92 : RPCResult{
256 92 : RPCResult::Type::OBJ, "", "",
257 552 : {
258 92 : {RPCResult::Type::STR_HEX, "hash", "The block hash hex-encoded"},
259 92 : {RPCResult::Type::NUM, "height", "The block height or index"},
260 92 : {RPCResult::Type::STR_HEX, "signature", "The ChainLock's BLS signature"},
261 92 : {RPCResult::Type::BOOL, "known_block", "True if the block is known by our node"},
262 92 : {RPCResult::Type::STR_HEX, "hex", "The serialized, hex-encoded data for best ChainLock"},
263 : }},
264 92 : RPCExamples{
265 92 : HelpExampleCli("getbestchainlock", "")
266 92 : + HelpExampleRpc("getbestchainlock", "")
267 : },
268 92 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
269 : {
270 0 : const NodeContext& node = EnsureAnyNodeContext(request.context);
271 :
272 0 : CHECK_NONFATAL(node.chainlocks);
273 0 : const chainlock::ChainLockSig clsig = node.chainlocks->GetBestChainLock();
274 0 : if (clsig.IsNull()) {
275 0 : throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to find any ChainLock");
276 : }
277 :
278 0 : UniValue result(UniValue::VOBJ);
279 :
280 0 : result.pushKV("blockhash", clsig.getBlockHash().GetHex());
281 0 : result.pushKV("height", clsig.getHeight());
282 0 : result.pushKV("signature", clsig.getSig().ToString());
283 :
284 : {
285 0 : const ChainstateManager& chainman = EnsureChainman(node);
286 0 : LOCK(cs_main);
287 0 : result.pushKV("known_block", chainman.m_blockman.LookupBlockIndex(clsig.getBlockHash()) != nullptr);
288 0 : }
289 :
290 0 : CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
291 0 : ssTx << clsig;
292 0 : result.pushKV("hex", HexStr(ssTx));
293 :
294 0 : return result;
295 0 : },
296 : };
297 0 : }
298 :
299 0 : void RPCNotifyBlockChange(const CBlockIndex* pindex)
300 : {
301 0 : if(pindex) {
302 0 : LOCK(cs_blockchange);
303 0 : latestblock.hash = pindex->GetBlockHash();
304 0 : latestblock.height = pindex->nHeight;
305 0 : }
306 0 : cond_blockchange.notify_all();
307 0 : }
308 :
309 92 : static RPCHelpMan waitfornewblock()
310 : {
311 184 : return RPCHelpMan{"waitfornewblock",
312 92 : "\nWaits for a specific new block and returns useful info about it.\n"
313 : "\nReturns the current block on timeout or exit.\n",
314 184 : {
315 92 : {"timeout", RPCArg::Type::NUM, RPCArg::Default{0}, "Time in milliseconds to wait for a response. 0 indicates no timeout."},
316 : },
317 92 : RPCResult{
318 92 : RPCResult::Type::OBJ, "", "",
319 276 : {
320 92 : {RPCResult::Type::STR_HEX, "hash", "The blockhash"},
321 92 : {RPCResult::Type::NUM, "height", "Block height"},
322 : }},
323 92 : RPCExamples{
324 92 : HelpExampleCli("waitfornewblock", "1000")
325 92 : + HelpExampleRpc("waitfornewblock", "1000")
326 : },
327 92 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
328 : {
329 0 : int timeout = 0;
330 0 : if (!request.params[0].isNull())
331 0 : timeout = request.params[0].getInt<int>();
332 :
333 0 : CUpdatedBlock block;
334 : {
335 0 : WAIT_LOCK(cs_blockchange, lock);
336 0 : block = latestblock;
337 0 : 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 0 : cond_blockchange.wait(lock, [&block]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); });
341 0 : block = latestblock;
342 0 : }
343 0 : UniValue ret(UniValue::VOBJ);
344 0 : ret.pushKV("hash", block.hash.GetHex());
345 0 : ret.pushKV("height", block.height);
346 0 : return ret;
347 0 : },
348 : };
349 0 : }
350 :
351 92 : static RPCHelpMan waitforblock()
352 : {
353 184 : return RPCHelpMan{"waitforblock",
354 92 : "\nWaits for a specific new block and returns useful info about it.\n"
355 : "\nReturns the current block on timeout or exit.\n",
356 276 : {
357 92 : {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Block hash to wait for."},
358 92 : {"timeout", RPCArg::Type::NUM, RPCArg::Default{0}, "Time in milliseconds to wait for a response. 0 indicates no timeout."},
359 : },
360 92 : RPCResult{
361 92 : RPCResult::Type::OBJ, "", "",
362 276 : {
363 92 : {RPCResult::Type::STR_HEX, "hash", "The blockhash"},
364 92 : {RPCResult::Type::NUM, "height", "Block height"},
365 : }},
366 92 : RPCExamples{
367 92 : HelpExampleCli("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\" 1000")
368 92 : + HelpExampleRpc("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\", 1000")
369 : },
370 92 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
371 : {
372 0 : int timeout = 0;
373 :
374 0 : uint256 hash(ParseHashV(request.params[0], "blockhash"));
375 :
376 0 : if (!request.params[1].isNull())
377 0 : timeout = request.params[1].getInt<int>();
378 :
379 0 : CUpdatedBlock block;
380 : {
381 0 : WAIT_LOCK(cs_blockchange, lock);
382 0 : if(timeout)
383 0 : 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 0 : block = latestblock;
387 0 : }
388 :
389 0 : UniValue ret(UniValue::VOBJ);
390 0 : ret.pushKV("hash", block.hash.GetHex());
391 0 : ret.pushKV("height", block.height);
392 0 : return ret;
393 0 : },
394 : };
395 0 : }
396 :
397 92 : static RPCHelpMan waitforblockheight()
398 : {
399 184 : return RPCHelpMan{"waitforblockheight",
400 92 : "\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 276 : {
404 92 : {"height", RPCArg::Type::NUM, RPCArg::Optional::NO, "Block height to wait for."},
405 92 : {"timeout", RPCArg::Type::NUM, RPCArg::Default{0}, "Time in milliseconds to wait for a response. 0 indicates no timeout."},
406 : },
407 92 : RPCResult{
408 92 : RPCResult::Type::OBJ, "", "",
409 276 : {
410 92 : {RPCResult::Type::STR_HEX, "hash", "The blockhash"},
411 92 : {RPCResult::Type::NUM, "height", "Block height"},
412 : }},
413 92 : RPCExamples{
414 92 : HelpExampleCli("waitforblockheight", "100 1000")
415 92 : + HelpExampleRpc("waitforblockheight", "100, 1000")
416 : },
417 92 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
418 : {
419 0 : int timeout = 0;
420 :
421 0 : int height = request.params[0].getInt<int>();
422 :
423 0 : if (!request.params[1].isNull())
424 0 : timeout = request.params[1].getInt<int>();
425 :
426 0 : CUpdatedBlock block;
427 : {
428 0 : WAIT_LOCK(cs_blockchange, lock);
429 0 : if(timeout)
430 0 : cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&height]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.height >= height || !IsRPCRunning();});
431 : else
432 0 : cond_blockchange.wait(lock, [&height]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.height >= height || !IsRPCRunning(); });
433 0 : block = latestblock;
434 0 : }
435 0 : UniValue ret(UniValue::VOBJ);
436 0 : ret.pushKV("hash", block.hash.GetHex());
437 0 : ret.pushKV("height", block.height);
438 0 : return ret;
439 0 : },
440 : };
441 0 : }
442 :
443 92 : static RPCHelpMan syncwithvalidationinterfacequeue()
444 : {
445 184 : return RPCHelpMan{"syncwithvalidationinterfacequeue",
446 92 : "\nWaits for the validation interface queue to catch up on everything that was there when we entered this function.\n",
447 92 : {},
448 92 : RPCResult{RPCResult::Type::NONE, "", ""},
449 92 : RPCExamples{
450 92 : HelpExampleCli("syncwithvalidationinterfacequeue","")
451 92 : + HelpExampleRpc("syncwithvalidationinterfacequeue","")
452 : },
453 92 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
454 : {
455 0 : SyncWithValidationInterfaceQueue();
456 0 : return UniValue::VNULL;
457 0 : },
458 : };
459 0 : }
460 :
461 92 : static RPCHelpMan getdifficulty()
462 : {
463 184 : return RPCHelpMan{"getdifficulty",
464 92 : "\nReturns the proof-of-work difficulty as a multiple of the minimum difficulty.\n",
465 92 : {},
466 92 : RPCResult{
467 92 : RPCResult::Type::NUM, "", "the proof-of-work difficulty as a multiple of the minimum difficulty."},
468 92 : RPCExamples{
469 92 : HelpExampleCli("getdifficulty", "")
470 92 : + HelpExampleRpc("getdifficulty", "")
471 : },
472 92 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
473 : {
474 :
475 0 : ChainstateManager& chainman = EnsureAnyChainman(request.context);
476 0 : LOCK(cs_main);
477 0 : return GetDifficulty(chainman.ActiveChain().Tip());
478 0 : },
479 : };
480 0 : }
481 :
482 92 : static RPCHelpMan getblockfrompeer()
483 : {
484 92 : return RPCHelpMan{
485 92 : "getblockfrompeer",
486 92 : "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 276 : {
493 92 : {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash to try to fetch"},
494 92 : {"peer_id", RPCArg::Type::NUM, RPCArg::Optional::NO, "The peer to fetch it from (see getpeerinfo for peer IDs)"},
495 : },
496 92 : RPCResult{RPCResult::Type::OBJ, "", /*optional=*/false, "", {}},
497 92 : RPCExamples{
498 92 : HelpExampleCli("getblockfrompeer", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" 0")
499 92 : + HelpExampleRpc("getblockfrompeer", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" 0")
500 : },
501 92 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
502 : {
503 0 : RPCTypeCheck(request.params, {
504 0 : UniValue::VSTR, // blockhash
505 0 : UniValue::VNUM, // peer_id
506 : });
507 :
508 0 : const NodeContext& node = EnsureAnyNodeContext(request.context);
509 0 : ChainstateManager& chainman = EnsureChainman(node);
510 0 : PeerManager& peerman = EnsurePeerman(node);
511 :
512 0 : const uint256& block_hash{ParseHashV(request.params[0], "blockhash")};
513 0 : const NodeId peer_id{request.params[1].getInt<int64_t>()};
514 :
515 0 : const CBlockIndex* const index = WITH_LOCK(cs_main, return chainman.m_blockman.LookupBlockIndex(block_hash););
516 :
517 0 : if (!index) {
518 0 : 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 0 : if (index->nHeight > chainman.ActiveChain().Tip()->nHeight && node::fPruneMode) {
524 0 : 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 0 : const bool block_has_data = WITH_LOCK(::cs_main, return index->nStatus & BLOCK_HAVE_DATA);
528 0 : if (block_has_data) {
529 0 : throw JSONRPCError(RPC_MISC_ERROR, "Block already downloaded");
530 : }
531 0 : if (const auto err{peerman.FetchBlock(peer_id, *index)}) {
532 0 : throw JSONRPCError(RPC_MISC_ERROR, err.value());
533 : }
534 0 : return UniValue::VOBJ;
535 0 : },
536 : };
537 0 : }
538 :
539 92 : static RPCHelpMan getblockhashes()
540 : {
541 184 : return RPCHelpMan{"getblockhashes",
542 92 : "\nReturns array of hashes of blocks within the timestamp range provided.\n",
543 276 : {
544 92 : {"high", RPCArg::Type::NUM, RPCArg::Optional::NO, "The newer block timestamp"},
545 92 : {"low", RPCArg::Type::NUM, RPCArg::Optional::NO, "The older block timestamp"},
546 : },
547 92 : RPCResult{
548 92 : RPCResult::Type::ARR, "", "",
549 92 : {{RPCResult::Type::STR_HEX, "", "The block hash"}}},
550 92 : RPCExamples{
551 92 : HelpExampleCli("getblockhashes", "1231614698 1231024505")
552 92 : + HelpExampleRpc("getblockhashes", "1231614698, 1231024505")
553 : },
554 92 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
555 : {
556 0 : if (!g_timestampindex) {
557 0 : throw JSONRPCError(RPC_MISC_ERROR, "Timestamp index is not enabled. Start with -timestampindex to enable.");
558 : }
559 :
560 0 : 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 0 : unsigned int high = request.params[0].getInt<int>();
565 0 : unsigned int low = request.params[1].getInt<int>();
566 0 : std::vector<uint256> blockHashes;
567 :
568 0 : if (!g_timestampindex->GetBlockHashes(high, low, blockHashes)) {
569 0 : throw JSONRPCError(RPC_MISC_ERROR, "Failed to read timestamp index.");
570 : }
571 :
572 0 : UniValue result(UniValue::VARR);
573 0 : for (const auto& hash : blockHashes) {
574 0 : result.push_back(hash.GetHex());
575 : }
576 :
577 0 : return result;
578 0 : },
579 : };
580 0 : }
581 :
582 92 : static RPCHelpMan getblockhash()
583 : {
584 184 : return RPCHelpMan{"getblockhash",
585 92 : "\nReturns hash of block in best-block-chain at height provided.\n",
586 184 : {
587 92 : {"height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The height index"},
588 : },
589 92 : RPCResult{
590 92 : RPCResult::Type::STR_HEX, "", "The block hash"},
591 92 : RPCExamples{
592 92 : HelpExampleCli("getblockhash", "1000")
593 92 : + HelpExampleRpc("getblockhash", "1000")
594 : },
595 92 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
596 : {
597 0 : ChainstateManager& chainman = EnsureAnyChainman(request.context);
598 0 : LOCK(cs_main);
599 0 : const CChain& active_chain = chainman.ActiveChain();
600 :
601 0 : int nHeight = request.params[0].getInt<int>();
602 0 : if (nHeight < 0 || nHeight > active_chain.Height())
603 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range");
604 :
605 0 : const CBlockIndex* pblockindex = active_chain[nHeight];
606 0 : return pblockindex->GetBlockHash().GetHex();
607 0 : },
608 : };
609 0 : }
610 :
611 92 : static RPCHelpMan getblockheader()
612 : {
613 184 : return RPCHelpMan{"getblockheader",
614 92 : "\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 276 : {
617 92 : {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
618 92 : {"verbose", RPCArg::Type::BOOL, RPCArg::Default{true}, "true for a json object, false for the hex-encoded data"},
619 : },
620 276 : {
621 184 : RPCResult{"for verbose = true",
622 92 : RPCResult::Type::OBJ, "", "",
623 1564 : {
624 92 : {RPCResult::Type::STR_HEX, "hash", "the block hash (same as provided)"},
625 92 : {RPCResult::Type::NUM, "confirmations", "The number of confirmations, or -1 if the block is not on the main chain"},
626 92 : {RPCResult::Type::NUM, "height", "The block height or index"},
627 92 : {RPCResult::Type::NUM, "version", "The block version"},
628 92 : {RPCResult::Type::STR_HEX, "versionHex", "The block version formatted in hexadecimal"},
629 92 : {RPCResult::Type::STR_HEX, "merkleroot", "The merkle root"},
630 92 : {RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME},
631 92 : {RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME},
632 92 : {RPCResult::Type::NUM, "nonce", "The nonce"},
633 92 : {RPCResult::Type::STR_HEX, "bits", "The bits"},
634 92 : {RPCResult::Type::NUM, "difficulty", "The difficulty"},
635 92 : {RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the current chain"},
636 92 : {RPCResult::Type::NUM, "nTx", "The number of transactions in the block"},
637 92 : {RPCResult::Type::STR_HEX, "previousblockhash", /*optional=*/true, "The hash of the previous block (if available)"},
638 92 : {RPCResult::Type::STR_HEX, "nextblockhash", /*optional=*/true, "The hash of the next block (if available)"},
639 92 : {RPCResult::Type::BOOL, "chainlock", "The state of the block ChainLock"},
640 : }},
641 184 : RPCResult{"for verbose=false",
642 92 : RPCResult::Type::STR_HEX, "", "A string that is serialized, hex-encoded data for block 'hash'"},
643 : },
644 92 : RPCExamples{
645 92 : HelpExampleCli("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
646 92 : + HelpExampleRpc("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
647 : },
648 92 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
649 : {
650 0 : uint256 hash(ParseHashV(request.params[0], "hash"));
651 0 : const NodeContext& node = EnsureAnyNodeContext(request.context);
652 :
653 0 : bool fVerbose = true;
654 0 : if (!request.params[1].isNull())
655 0 : fVerbose = request.params[1].get_bool();
656 :
657 : const CBlockIndex* pblockindex;
658 : const CBlockIndex* tip;
659 : {
660 0 : ChainstateManager& chainman = EnsureChainman(node);
661 0 : LOCK(cs_main);
662 0 : pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
663 0 : tip = chainman.ActiveChain().Tip();
664 0 : }
665 :
666 0 : if (!pblockindex) {
667 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
668 : }
669 :
670 0 : if (!fVerbose)
671 : {
672 0 : CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
673 0 : ssBlock << pblockindex->GetBlockHeader();
674 0 : std::string strHex = HexStr(ssBlock);
675 0 : return strHex;
676 0 : }
677 :
678 0 : CHECK_NONFATAL(node.chainlocks);
679 0 : return blockheaderToJSON(tip, pblockindex, *node.chainlocks);
680 0 : },
681 : };
682 0 : }
683 :
684 92 : static RPCHelpMan getblockheaders()
685 : {
686 184 : return RPCHelpMan{"getblockheaders",
687 92 : "\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 368 : {
691 92 : {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
692 92 : {"count", RPCArg::Type::NUM, RPCArg::Default{int{MAX_HEADERS_UNCOMPRESSED_RESULT}}, ""},
693 92 : {"verbose", RPCArg::Type::BOOL, RPCArg::Default{true}, "true for a json object, false for the hex-encoded data"},
694 : },
695 276 : {
696 184 : RPCResult{"for verbose = true",
697 92 : RPCResult::Type::ARR, "", "",
698 184 : {{RPCResult::Type::OBJ, "", "",
699 1564 : {
700 92 : {RPCResult::Type::STR_HEX, "hash", "The block hash (same as provided)"},
701 92 : {RPCResult::Type::NUM, "confirmations", "The number of confirmations, or -1 if the block is not on the main chain"},
702 92 : {RPCResult::Type::NUM, "height", "The block height or index"},
703 92 : {RPCResult::Type::NUM, "version", "The block version"},
704 92 : {RPCResult::Type::STR_HEX, "versionHex", "The block version formatted in hexadecimal"},
705 92 : {RPCResult::Type::STR_HEX, "merkleroot", "The merkle root"},
706 92 : {RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME},
707 92 : {RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME},
708 92 : {RPCResult::Type::NUM, "nonce", "The nonce"},
709 92 : {RPCResult::Type::STR_HEX, "bits", "The bits"},
710 92 : {RPCResult::Type::NUM, "difficulty", "The difficulty"},
711 92 : {RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the current chain"},
712 92 : {RPCResult::Type::NUM, "nTx", "The number of transactions in the block"},
713 92 : {RPCResult::Type::STR_HEX, "previousblockhash", /*optional=*/true, "The hash of the previous block (if available)"},
714 92 : {RPCResult::Type::STR_HEX, "nextblockhash", /*optional=*/true, "The hash of the next block (if available)"},
715 92 : {RPCResult::Type::BOOL, "chainlock", "The state of the block ChainLock"},
716 : }},
717 : }},
718 184 : RPCResult{"for verbose=false",
719 92 : RPCResult::Type::ARR, "", "",
720 92 : {{RPCResult::Type::STR_HEX, "", "A string that is serialized, hex-encoded data for block 'hash'"}}},
721 : },
722 92 : RPCExamples{
723 92 : HelpExampleCli("getblockheaders", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" 2000")
724 92 : + HelpExampleRpc("getblockheaders", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" 2000")
725 : },
726 92 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
727 : {
728 :
729 0 : uint256 hash(ParseHashV(request.params[0], "blockhash"));
730 :
731 0 : const NodeContext& node = EnsureAnyNodeContext(request.context);
732 :
733 0 : ChainstateManager& chainman = EnsureChainman(node);
734 :
735 0 : CChainState& active_chainstate = chainman.ActiveChainstate();
736 0 : CChain& active_chain = active_chainstate.m_chain;
737 :
738 : const CBlockIndex* pblockindex;
739 : const CBlockIndex* tip;
740 : {
741 0 : LOCK(cs_main);
742 0 : pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
743 0 : tip = active_chain.Tip();
744 0 : }
745 :
746 0 : if (!pblockindex) {
747 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
748 : }
749 :
750 0 : int nCount = MAX_HEADERS_UNCOMPRESSED_RESULT;
751 0 : if (!request.params[1].isNull())
752 0 : nCount = request.params[1].getInt<int>();
753 :
754 0 : if (nCount <= 0 || nCount > (int)MAX_HEADERS_UNCOMPRESSED_RESULT)
755 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Count is out of range");
756 :
757 0 : bool fVerbose = true;
758 0 : if (!request.params[2].isNull())
759 0 : fVerbose = request.params[2].get_bool();
760 :
761 0 : UniValue arrHeaders(UniValue::VARR);
762 :
763 0 : if (!fVerbose)
764 : {
765 0 : for (; pblockindex; pblockindex = active_chain.Next(pblockindex))
766 : {
767 0 : CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
768 0 : ssBlock << pblockindex->GetBlockHeader();
769 0 : std::string strHex = HexStr(ssBlock);
770 0 : arrHeaders.push_back(strHex);
771 0 : if (--nCount <= 0)
772 0 : break;
773 0 : }
774 0 : return arrHeaders;
775 : }
776 :
777 0 : CHECK_NONFATAL(node.chainlocks);
778 0 : for (; pblockindex; pblockindex = active_chain.Next(pblockindex))
779 : {
780 0 : arrHeaders.push_back(blockheaderToJSON(tip, pblockindex, *node.chainlocks));
781 0 : if (--nCount <= 0)
782 0 : break;
783 0 : }
784 :
785 0 : return arrHeaders;
786 0 : },
787 : };
788 0 : }
789 :
790 0 : static CBlock GetBlockChecked(BlockManager& blockman, const CBlockIndex* pblockindex)
791 : {
792 0 : CBlock block;
793 : {
794 0 : LOCK(cs_main);
795 0 : if (blockman.IsBlockPruned(pblockindex)) {
796 0 : throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)");
797 : }
798 0 : }
799 :
800 0 : 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 0 : throw JSONRPCError(RPC_MISC_ERROR, "Block not found on disk");
805 : }
806 :
807 0 : return block;
808 0 : }
809 :
810 0 : static CBlockUndo GetUndoChecked(BlockManager& blockman, const CBlockIndex* pblockindex)
811 : {
812 0 : CBlockUndo blockUndo;
813 :
814 : // The Genesis block does not have undo data
815 0 : if (pblockindex->nHeight == 0) return blockUndo;
816 :
817 : {
818 0 : LOCK(cs_main);
819 0 : if (blockman.IsBlockPruned(pblockindex)) {
820 0 : throw JSONRPCError(RPC_MISC_ERROR, "Undo data not available (pruned data)");
821 : }
822 0 : }
823 :
824 0 : if (!UndoReadFromDisk(blockUndo, pblockindex)) {
825 0 : throw JSONRPCError(RPC_MISC_ERROR, "Can't read undo data from disk");
826 : }
827 :
828 0 : return blockUndo;
829 0 : }
830 :
831 92 : static RPCHelpMan getmerkleblocks()
832 : {
833 184 : return RPCHelpMan{"getmerkleblocks",
834 92 : "\nReturns an array of hex-encoded merkleblocks for <count> blocks starting from <hash> which match <filter>.\n",
835 368 : {
836 92 : {"filter", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex-encoded bloom filter"},
837 92 : {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
838 92 : {"count", RPCArg::Type::NUM, RPCArg::Default{int{MAX_HEADERS_UNCOMPRESSED_RESULT}}, ""},
839 : },
840 92 : RPCResult{
841 92 : RPCResult::Type::ARR, "", "",
842 92 : {{RPCResult::Type::STR_HEX, "", "A string that is serialized, hex-encoded data for a merkleblock"}}},
843 92 : RPCExamples{
844 92 : HelpExampleCli("getmerkleblocks", "\"2303028005802040100040000008008400048141010000f8400420800080025004000004130000000000000001\" \"00000000007e1432d2af52e8463278bf556b55cf5049262f25634557e2e91202\" 2000")
845 92 : + HelpExampleRpc("getmerkleblocks", "\"2303028005802040100040000008008400048141010000f8400420800080025004000004130000000000000001\" \"00000000007e1432d2af52e8463278bf556b55cf5049262f25634557e2e91202\" 2000")
846 : },
847 92 : [&](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 146 : const RPCResult getblock_vin{
907 146 : RPCResult::Type::ARR, "vin", "",
908 292 : {
909 292 : {RPCResult::Type::OBJ, "", "",
910 438 : {
911 146 : {RPCResult::Type::ELISION, "", "The same output as verbosity = 2"},
912 292 : {RPCResult::Type::OBJ, "prevout", "(Only if undo information is available)",
913 730 : {
914 146 : {RPCResult::Type::BOOL, "generated", "Coinbase or not"},
915 146 : {RPCResult::Type::NUM, "height", "The height of the prevout"},
916 146 : {RPCResult::Type::STR_AMOUNT, "value", "The value in " + CURRENCY_UNIT},
917 292 : {RPCResult::Type::OBJ, "scriptPubKey", "",
918 876 : {
919 146 : {RPCResult::Type::STR, "asm", "Disassembly of the public key script"},
920 146 : {RPCResult::Type::STR, "desc", "Inferred descriptor for the output"},
921 146 : {RPCResult::Type::STR_HEX, "hex", "The raw public key script bytes, hex-encoded"},
922 146 : {RPCResult::Type::STR, "address", /*optional=*/true, "The Dash address (only if a well-defined address exists)"},
923 146 : {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"},
924 : }},
925 : }},
926 : }},
927 : }
928 : };
929 :
930 92 : static RPCHelpMan getblock()
931 : {
932 184 : return RPCHelpMan{"getblock",
933 92 : "\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 276 : {
938 92 : {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
939 92 : {"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 460 : {
942 184 : RPCResult{"for verbosity = 0",
943 92 : RPCResult::Type::STR_HEX, "", "A string that is serialized, hex-encoded data for block 'hash'"},
944 184 : RPCResult{"for verbosity = 1",
945 92 : RPCResult::Type::OBJ, "", "",
946 1840 : {
947 92 : {RPCResult::Type::STR_HEX, "hash", "the block hash (same as provided)"},
948 92 : {RPCResult::Type::NUM, "confirmations", "The number of confirmations, or -1 if the block is not on the main chain"},
949 92 : {RPCResult::Type::NUM, "height", "The block height or index"},
950 92 : {RPCResult::Type::NUM, "version", "The block version"},
951 92 : {RPCResult::Type::STR_HEX, "versionHex", "The block version formatted in hexadecimal"},
952 92 : {RPCResult::Type::STR_HEX, "merkleroot", "The merkle root"},
953 92 : {RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME},
954 92 : {RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME},
955 92 : {RPCResult::Type::NUM, "nonce", "The nonce"},
956 92 : {RPCResult::Type::STR_HEX, "bits", "The bits"},
957 92 : {RPCResult::Type::NUM, "difficulty", "The difficulty"},
958 92 : {RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the current chain"},
959 92 : {RPCResult::Type::NUM, "nTx", "The number of transactions in the block"},
960 92 : {RPCResult::Type::STR_HEX, "previousblockhash", /*optional=*/true, "The hash of the previous block (if available)"},
961 92 : {RPCResult::Type::STR_HEX, "nextblockhash", /*optional=*/true, "The hash of the next block (if available)"},
962 92 : {RPCResult::Type::BOOL, "chainlock", "The state of the block ChainLock"},
963 92 : {RPCResult::Type::NUM, "size", "The block size"},
964 184 : {RPCResult::Type::ARR, "tx", "The transaction ids",
965 92 : {{RPCResult::Type::STR_HEX, "", "The transaction id"}}},
966 92 : CCbTx::GetJsonHelp(/*key=*/"cbTx", /*optional=*/true),
967 : }},
968 184 : RPCResult{"for verbosity = 2",
969 92 : RPCResult::Type::OBJ, "", "",
970 276 : {
971 92 : {RPCResult::Type::ELISION, "", "Same output as verbosity = 1"},
972 184 : {RPCResult::Type::ARR, "tx", "",
973 184 : {
974 184 : {RPCResult::Type::OBJ, "", "",
975 276 : {
976 92 : {RPCResult::Type::ELISION, "", "The transactions in the format of the getrawtransaction RPC. Different from verbosity = 1 \"tx\" result"},
977 92 : {RPCResult::Type::NUM, "fee", "The transaction fee in " + CURRENCY_UNIT + ", omitted if block undo data is not available"},
978 : }},
979 : }},
980 : }},
981 184 : RPCResult{"for verbosity = 3",
982 92 : RPCResult::Type::OBJ, "", "",
983 276 : {
984 92 : {RPCResult::Type::ELISION, "", "Same output as verbosity = 2"},
985 184 : {RPCResult::Type::ARR, "tx", "",
986 184 : {
987 184 : {RPCResult::Type::OBJ, "", "",
988 92 : {
989 92 : getblock_vin,
990 : }},
991 : }},
992 : }},
993 : },
994 92 : RPCExamples{
995 92 : HelpExampleCli("getblock", "\"00000000000fd08c2fb661d2fcb0d49abb3a91e5f27082ce64feed3b4dede2e2\"")
996 92 : + HelpExampleRpc("getblock", "\"00000000000fd08c2fb661d2fcb0d49abb3a91e5f27082ce64feed3b4dede2e2\"")
997 : },
998 92 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
999 : {
1000 0 : uint256 hash(ParseHashV(request.params[0], "blockhash"));
1001 :
1002 0 : int verbosity = 1;
1003 0 : if (!request.params[1].isNull()) {
1004 0 : if (request.params[1].isBool()) {
1005 0 : verbosity = request.params[1].get_bool() ? 1 : 0;
1006 0 : } else {
1007 0 : verbosity = request.params[1].getInt<int>();
1008 : }
1009 0 : }
1010 :
1011 0 : const NodeContext& node = EnsureAnyNodeContext(request.context);
1012 :
1013 : const CBlockIndex* pblockindex;
1014 : const CBlockIndex* tip;
1015 0 : ChainstateManager& chainman = EnsureChainman(node);
1016 : {
1017 0 : LOCK(cs_main);
1018 0 : pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
1019 0 : tip = chainman.ActiveChain().Tip();
1020 :
1021 0 : if (!pblockindex) {
1022 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
1023 : }
1024 0 : }
1025 :
1026 0 : const CBlock block{GetBlockChecked(chainman.m_blockman, pblockindex)};
1027 :
1028 0 : if (verbosity <= 0)
1029 : {
1030 0 : CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
1031 0 : ssBlock << block;
1032 0 : std::string strHex = HexStr(ssBlock);
1033 0 : return strHex;
1034 0 : }
1035 :
1036 0 : const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
1037 0 : CHECK_NONFATAL(node.chainlocks);
1038 : TxVerbosity tx_verbosity;
1039 0 : if (verbosity == 1) {
1040 0 : tx_verbosity = TxVerbosity::SHOW_TXID;
1041 0 : } else if (verbosity == 2) {
1042 0 : tx_verbosity = TxVerbosity::SHOW_DETAILS;
1043 0 : } else {
1044 0 : tx_verbosity = TxVerbosity::SHOW_DETAILS_AND_PREVOUT;
1045 : }
1046 :
1047 0 : return blockToJSON(chainman.m_blockman, block, tip, pblockindex, *node.chainlocks, *llmq_ctx.isman, tx_verbosity);
1048 0 : },
1049 : };
1050 0 : }
1051 :
1052 92 : static RPCHelpMan pruneblockchain()
1053 : {
1054 184 : return RPCHelpMan{"pruneblockchain", "",
1055 184 : {
1056 92 : {"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 92 : RPCResult{
1060 92 : RPCResult::Type::NUM, "", "Height of the last block pruned"},
1061 92 : RPCExamples{
1062 92 : HelpExampleCli("pruneblockchain", "1000")
1063 92 : + HelpExampleRpc("pruneblockchain", "1000")
1064 : },
1065 92 : [&](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 0 : CoinStatsHashType ParseHashType(const std::string& hash_type_input)
1112 : {
1113 0 : if (hash_type_input == "hash_serialized_2") {
1114 0 : return CoinStatsHashType::HASH_SERIALIZED;
1115 0 : } else if (hash_type_input == "muhash") {
1116 0 : return CoinStatsHashType::MUHASH;
1117 0 : } else if (hash_type_input == "none") {
1118 0 : return CoinStatsHashType::NONE;
1119 : } else {
1120 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("'%s' is not a valid hash_type", hash_type_input));
1121 : }
1122 0 : }
1123 :
1124 9 : 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 9 : if ((hash_type == kernel::CoinStatsHashType::MUHASH || hash_type == kernel::CoinStatsHashType::NONE) && g_coin_stats_index && index_requested) {
1132 0 : if (pindex) {
1133 0 : 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 9 : CHECK_NONFATAL(!pindex || pindex->GetBlockHash() == view->GetBestBlock());
1145 :
1146 9 : return kernel::ComputeUTXOStats(hash_type, view, blockman, interruption_point);
1147 9 : }
1148 :
1149 92 : static RPCHelpMan gettxoutsetinfo()
1150 : {
1151 184 : return RPCHelpMan{"gettxoutsetinfo",
1152 92 : "\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 368 : {
1155 92 : {"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 92 : {"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 92 : {"use_index", RPCArg::Type::BOOL, RPCArg::Default{true}, "Use coinstatsindex, if available."},
1158 : },
1159 92 : RPCResult{
1160 92 : RPCResult::Type::OBJ, "", "",
1161 1104 : {
1162 92 : {RPCResult::Type::NUM, "height", "The block height (index) of the returned statistics"},
1163 92 : {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at which these statistics are calculated"},
1164 92 : {RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs"},
1165 92 : {RPCResult::Type::NUM, "bogosize", "Database-independent, meaningless metric indicating the UTXO set size"},
1166 92 : {RPCResult::Type::STR_HEX, "hash_serialized_2", /*optional=*/true, "The serialized hash (only present if 'hash_serialized_2' hash_type is chosen)"},
1167 92 : {RPCResult::Type::STR_HEX, "muhash", /*optional=*/true, "The serialized hash (only present if 'muhash' hash_type is chosen)"},
1168 92 : {RPCResult::Type::NUM, "transactions", /*optional=*/true, "The number of transactions with unspent outputs (not available when coinstatsindex is used)"},
1169 92 : {RPCResult::Type::NUM, "disk_size", /*optional=*/true, "The estimated size of the chainstate on disk (not available when coinstatsindex is used)"},
1170 92 : {RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount of coins in the UTXO set"},
1171 92 : {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 184 : {RPCResult::Type::OBJ, "block_info", /*optional=*/true, "Info on amounts in the block at this block height (only available if coinstatsindex is used)",
1173 552 : {
1174 92 : {RPCResult::Type::STR_AMOUNT, "prevout_spent", "Total amount of all prevouts spent in this block"},
1175 92 : {RPCResult::Type::STR_AMOUNT, "coinbase", "Coinbase subsidy amount of this block"},
1176 92 : {RPCResult::Type::STR_AMOUNT, "new_outputs_ex_coinbase", "Total amount of new outputs created by this block"},
1177 92 : {RPCResult::Type::STR_AMOUNT, "unspendable", "Total amount of unspendable outputs created in this block"},
1178 184 : {RPCResult::Type::OBJ, "unspendables", "Detailed view of the unspendable categories",
1179 460 : {
1180 92 : {RPCResult::Type::STR_AMOUNT, "genesis_block", "The unspendable amount of the Genesis block subsidy"},
1181 92 : {RPCResult::Type::STR_AMOUNT, "bip30", "Transactions overridden by duplicates (no longer possible with BIP30)"},
1182 92 : {RPCResult::Type::STR_AMOUNT, "scripts", "Amounts sent to scripts that are unspendable (for example OP_RETURN outputs)"},
1183 92 : {RPCResult::Type::STR_AMOUNT, "unclaimed_rewards", "Fee rewards that miners did not claim in their coinbase transaction"},
1184 : }}
1185 : }},
1186 : }},
1187 92 : RPCExamples{
1188 184 : HelpExampleCli("gettxoutsetinfo", "") +
1189 184 : HelpExampleCli("gettxoutsetinfo", R"("none")") +
1190 184 : HelpExampleCli("gettxoutsetinfo", R"("none" 1000)") +
1191 184 : HelpExampleCli("gettxoutsetinfo", R"("none" '"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09"')") +
1192 184 : HelpExampleCli("-named gettxoutsetinfo", R"(hash_type='muhash' use_index='false')") +
1193 184 : HelpExampleRpc("gettxoutsetinfo", "") +
1194 184 : HelpExampleRpc("gettxoutsetinfo", R"("none")") +
1195 184 : HelpExampleRpc("gettxoutsetinfo", R"("none", 1000)") +
1196 92 : HelpExampleRpc("gettxoutsetinfo", R"("none", "00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09")")
1197 : },
1198 92 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1199 : {
1200 0 : UniValue ret(UniValue::VOBJ);
1201 :
1202 0 : const CBlockIndex* pindex{nullptr};
1203 0 : const CoinStatsHashType hash_type{request.params[0].isNull() ? CoinStatsHashType::HASH_SERIALIZED : ParseHashType(request.params[0].get_str())};
1204 0 : bool index_requested = request.params[2].isNull() || request.params[2].get_bool();
1205 :
1206 0 : const NodeContext& node = EnsureAnyNodeContext(request.context);
1207 0 : ChainstateManager& chainman = EnsureChainman(node);
1208 0 : CChainState& active_chainstate = chainman.ActiveChainstate();
1209 0 : active_chainstate.ForceFlushStateToDisk();
1210 :
1211 : CCoinsView* coins_view;
1212 : BlockManager* blockman;
1213 : {
1214 0 : LOCK(::cs_main);
1215 0 : coins_view = &active_chainstate.CoinsDB();
1216 0 : blockman = &active_chainstate.m_blockman;
1217 0 : pindex = blockman->LookupBlockIndex(coins_view->GetBestBlock());
1218 0 : }
1219 :
1220 0 : if (!request.params[1].isNull()) {
1221 0 : if (!g_coin_stats_index) {
1222 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Querying specific block heights requires coinstatsindex");
1223 : }
1224 :
1225 0 : if (hash_type == CoinStatsHashType::HASH_SERIALIZED) {
1226 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "hash_serialized_2 hash type cannot be queried for a specific block");
1227 : }
1228 :
1229 0 : if (!index_requested) {
1230 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot set use_index to false when querying for a specific block");
1231 : }
1232 0 : pindex = ParseHashOrHeight(request.params[1], chainman);
1233 0 : }
1234 :
1235 0 : if (index_requested && g_coin_stats_index) {
1236 0 : 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 0 : }
1246 :
1247 0 : const std::optional<CCoinsStats> maybe_stats = GetUTXOStats(coins_view, *blockman, hash_type, node.rpc_interruption_point, pindex, index_requested);
1248 0 : if (maybe_stats.has_value()) {
1249 0 : const CCoinsStats& stats = maybe_stats.value();
1250 0 : ret.pushKV("height", (int64_t)stats.nHeight);
1251 0 : ret.pushKV("bestblock", stats.hashBlock.GetHex());
1252 0 : ret.pushKV("txouts", (int64_t)stats.nTransactionOutputs);
1253 0 : ret.pushKV("bogosize", (int64_t)stats.nBogoSize);
1254 0 : if (hash_type == CoinStatsHashType::HASH_SERIALIZED) {
1255 0 : ret.pushKV("hash_serialized_2", stats.hashSerialized.GetHex());
1256 0 : }
1257 0 : if (hash_type == CoinStatsHashType::MUHASH) {
1258 0 : ret.pushKV("muhash", stats.hashSerialized.GetHex());
1259 0 : }
1260 0 : CHECK_NONFATAL(stats.total_amount.has_value());
1261 0 : ret.pushKV("total_amount", ValueFromAmount(stats.total_amount.value()));
1262 0 : if (!stats.index_used) {
1263 0 : ret.pushKV("transactions", static_cast<int64_t>(stats.nTransactions));
1264 0 : ret.pushKV("disk_size", stats.nDiskSize);
1265 0 : } else {
1266 0 : ret.pushKV("total_unspendable_amount", ValueFromAmount(stats.total_unspendable_amount));
1267 :
1268 0 : CCoinsStats prev_stats{};
1269 0 : if (pindex->nHeight > 0) {
1270 0 : const std::optional<CCoinsStats> maybe_prev_stats = GetUTXOStats(coins_view, *blockman, hash_type, node.rpc_interruption_point, pindex->pprev, index_requested);
1271 0 : if (!maybe_prev_stats) {
1272 0 : throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
1273 : }
1274 0 : prev_stats = maybe_prev_stats.value();
1275 0 : }
1276 :
1277 0 : UniValue block_info(UniValue::VOBJ);
1278 0 : block_info.pushKV("prevout_spent", ValueFromAmount(stats.total_prevout_spent_amount - prev_stats.total_prevout_spent_amount));
1279 0 : block_info.pushKV("coinbase", ValueFromAmount(stats.total_coinbase_amount - prev_stats.total_coinbase_amount));
1280 0 : block_info.pushKV("new_outputs_ex_coinbase", ValueFromAmount(stats.total_new_outputs_ex_coinbase_amount - prev_stats.total_new_outputs_ex_coinbase_amount));
1281 0 : block_info.pushKV("unspendable", ValueFromAmount(stats.total_unspendable_amount - prev_stats.total_unspendable_amount));
1282 :
1283 0 : UniValue unspendables(UniValue::VOBJ);
1284 0 : unspendables.pushKV("genesis_block", ValueFromAmount(stats.total_unspendables_genesis_block - prev_stats.total_unspendables_genesis_block));
1285 0 : unspendables.pushKV("bip30", ValueFromAmount(stats.total_unspendables_bip30 - prev_stats.total_unspendables_bip30));
1286 0 : unspendables.pushKV("scripts", ValueFromAmount(stats.total_unspendables_scripts - prev_stats.total_unspendables_scripts));
1287 0 : unspendables.pushKV("unclaimed_rewards", ValueFromAmount(stats.total_unspendables_unclaimed_rewards - prev_stats.total_unspendables_unclaimed_rewards));
1288 0 : block_info.pushKV("unspendables", unspendables);
1289 :
1290 0 : ret.pushKV("block_info", block_info);
1291 0 : }
1292 0 : } else {
1293 0 : throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
1294 : }
1295 0 : return ret;
1296 0 : },
1297 : };
1298 0 : }
1299 :
1300 92 : static RPCHelpMan gettxout()
1301 : {
1302 184 : return RPCHelpMan{"gettxout",
1303 92 : "\nReturns details about an unspent transaction output.\n",
1304 368 : {
1305 92 : {"txid", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction id"},
1306 92 : {"n", RPCArg::Type::NUM, RPCArg::Optional::NO, "vout number"},
1307 92 : {"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 276 : {
1310 92 : RPCResult{"If the UTXO was not found", RPCResult::Type::NONE, "", ""},
1311 552 : RPCResult{"Otherwise", RPCResult::Type::OBJ, "", "", {
1312 92 : {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"},
1313 92 : {RPCResult::Type::NUM, "confirmations", "The number of confirmations"},
1314 92 : {RPCResult::Type::STR_AMOUNT, "value", "The transaction value in " + CURRENCY_UNIT},
1315 552 : {RPCResult::Type::OBJ, "scriptPubKey", "", {
1316 92 : {RPCResult::Type::STR, "asm", "Disassembly of the public key script"},
1317 92 : {RPCResult::Type::STR, "desc", "Inferred descriptor for the output"},
1318 92 : {RPCResult::Type::STR_HEX, "hex", "The raw public key script bytes, hex-encoded"},
1319 92 : {RPCResult::Type::STR_HEX, "type", "The type, eg pubkeyhash"},
1320 92 : {RPCResult::Type::STR, "address", /*optional=*/ true, "Dash address (only if a well-defined address exists)"},
1321 : }},
1322 92 : {RPCResult::Type::BOOL, "coinbase", "Coinbase or not"},
1323 : }},
1324 : },
1325 92 : RPCExamples{
1326 : "\nGet unspent transactions\n"
1327 92 : + HelpExampleCli("listunspent", "") +
1328 : "\nView the details\n"
1329 92 : + HelpExampleCli("gettxout", "\"txid\" 1") +
1330 : "\nAs a JSON-RPC call\n"
1331 92 : + HelpExampleRpc("gettxout", "\"txid\", 1")
1332 : },
1333 92 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1334 : {
1335 :
1336 0 : const NodeContext& node = EnsureAnyNodeContext(request.context);
1337 :
1338 0 : ChainstateManager& chainman = EnsureChainman(node);
1339 0 : LOCK(cs_main);
1340 :
1341 0 : CChainState& active_chainstate = chainman.ActiveChainstate();
1342 :
1343 0 : UniValue ret(UniValue::VOBJ);
1344 :
1345 0 : uint256 hash(ParseHashV(request.params[0], "txid"));
1346 0 : COutPoint out{hash, request.params[1].getInt<uint32_t>()};
1347 0 : bool fMempool = true;
1348 0 : if (!request.params[2].isNull())
1349 0 : fMempool = request.params[2].get_bool();
1350 :
1351 0 : Coin coin;
1352 0 : CCoinsViewCache* coins_view = &active_chainstate.CoinsTip();
1353 :
1354 0 : if (fMempool) {
1355 0 : const CTxMemPool& mempool = EnsureMemPool(node);
1356 0 : LOCK(mempool.cs);
1357 0 : CCoinsViewMemPool view(coins_view, mempool);
1358 0 : if (!view.GetCoin(out, coin) || mempool.isSpent(out)) {
1359 0 : return UniValue::VNULL;
1360 : }
1361 0 : } else {
1362 0 : if (!coins_view->GetCoin(out, coin)) {
1363 0 : return UniValue::VNULL;
1364 : }
1365 : }
1366 :
1367 0 : const CBlockIndex* pindex = active_chainstate.m_blockman.LookupBlockIndex(coins_view->GetBestBlock());
1368 0 : ret.pushKV("bestblock", pindex->GetBlockHash().GetHex());
1369 0 : if (coin.nHeight == MEMPOOL_HEIGHT) {
1370 0 : ret.pushKV("confirmations", 0);
1371 0 : } else {
1372 0 : ret.pushKV("confirmations", (int64_t)(pindex->nHeight - coin.nHeight + 1));
1373 : }
1374 0 : ret.pushKV("value", ValueFromAmount(coin.out.nValue));
1375 0 : UniValue o(UniValue::VOBJ);
1376 0 : ScriptToUniv(coin.out.scriptPubKey, /*out=*/o, /*include_hex=*/true, /*include_address=*/true);
1377 0 : ret.pushKV("scriptPubKey", o);
1378 0 : ret.pushKV("coinbase", (bool)coin.fCoinBase);
1379 :
1380 0 : return ret;
1381 0 : },
1382 : };
1383 0 : }
1384 :
1385 92 : static RPCHelpMan verifychain()
1386 : {
1387 184 : return RPCHelpMan{"verifychain",
1388 92 : "\nVerifies blockchain database.\n",
1389 276 : {
1390 184 : {"checklevel", RPCArg::Type::NUM, RPCArg::DefaultHint{strprintf("%d, range=0-4", DEFAULT_CHECKLEVEL)},
1391 92 : strprintf("How thorough the block verification is:\n - %s", MakeUnorderedList(CHECKLEVEL_DOC))},
1392 92 : {"nblocks", RPCArg::Type::NUM, RPCArg::DefaultHint{strprintf("%d, 0=all", DEFAULT_CHECKBLOCKS)}, "The number of blocks to check."},
1393 : },
1394 92 : RPCResult{
1395 92 : RPCResult::Type::BOOL, "", "Verified or not"},
1396 92 : RPCExamples{
1397 92 : HelpExampleCli("verifychain", "")
1398 92 : + HelpExampleRpc("verifychain", "")
1399 : },
1400 92 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1401 : {
1402 0 : const int check_level{request.params[0].isNull() ? DEFAULT_CHECKLEVEL : request.params[0].getInt<int>()};
1403 0 : const int check_depth{request.params[1].isNull() ? DEFAULT_CHECKBLOCKS : request.params[1].getInt<int>()};
1404 :
1405 0 : const NodeContext& node = EnsureAnyNodeContext(request.context);
1406 :
1407 0 : ChainstateManager& chainman = EnsureChainman(node);
1408 0 : LOCK(cs_main);
1409 :
1410 0 : CChainState& active_chainstate = chainman.ActiveChainstate();
1411 0 : return CVerifyDB().VerifyDB(
1412 0 : active_chainstate, Params().GetConsensus(), active_chainstate.CoinsTip(), *CHECK_NONFATAL(node.evodb), check_level, check_depth);
1413 0 : },
1414 : };
1415 0 : }
1416 :
1417 0 : static void SoftForkDescPushBack(const CBlockIndex* active_chain_tip, UniValue& softforks, const ChainstateManager& chainman, Consensus::BuriedDeployment dep)
1418 : {
1419 : // For buried deployments.
1420 :
1421 0 : if (!DeploymentEnabled(chainman, dep)) return;
1422 :
1423 0 : UniValue rv(UniValue::VOBJ);
1424 0 : rv.pushKV("type", "buried");
1425 : // getblockchaininfo reports the softfork as active from when the chain height is
1426 : // one below the activation height
1427 0 : rv.pushKV("active", DeploymentActiveAfter(active_chain_tip, chainman.GetConsensus(), dep));
1428 0 : rv.pushKV("height", chainman.GetConsensus().DeploymentHeight(dep));
1429 0 : softforks.pushKV(DeploymentName(dep), rv);
1430 0 : }
1431 :
1432 0 : 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 0 : if (!DeploymentEnabled(chainman, id)) return;
1437 :
1438 0 : UniValue bip9(UniValue::VOBJ);
1439 0 : const ThresholdState thresholdState = chainman.m_versionbitscache.State(active_chain_tip, chainman.GetConsensus(), id);
1440 0 : switch (thresholdState) {
1441 0 : case ThresholdState::DEFINED: bip9.pushKV("status", "defined"); break;
1442 0 : case ThresholdState::STARTED: bip9.pushKV("status", "started"); break;
1443 0 : case ThresholdState::LOCKED_IN: bip9.pushKV("status", "locked_in"); break;
1444 0 : case ThresholdState::ACTIVE: bip9.pushKV("status", "active"); break;
1445 0 : case ThresholdState::FAILED: bip9.pushKV("status", "failed"); break;
1446 : }
1447 0 : const bool has_signal = (ThresholdState::STARTED == thresholdState || ThresholdState::LOCKED_IN == thresholdState);
1448 0 : if (has_signal) {
1449 0 : bip9.pushKV("bit", chainman.GetConsensus().vDeployments[id].bit);
1450 0 : }
1451 0 : bip9.pushKV("start_time", chainman.GetConsensus().vDeployments[id].nStartTime);
1452 0 : bip9.pushKV("timeout", chainman.GetConsensus().vDeployments[id].nTimeout);
1453 0 : bip9.pushKV("min_activation_height", chainman.GetConsensus().vDeployments[id].min_activation_height);
1454 0 : bip9.pushKV("ehf", chainman.GetConsensus().vDeployments[id].useEHF);
1455 0 : if (auto it = signals.find(chainman.GetConsensus().vDeployments[id].bit); it != signals.end()) {
1456 0 : bip9.pushKV("ehf_height", it->second);
1457 0 : }
1458 0 : int64_t since_height = chainman.m_versionbitscache.StateSinceHeight(active_chain_tip, chainman.GetConsensus(), id);
1459 0 : bip9.pushKV("since", since_height);
1460 0 : if (has_signal) {
1461 0 : UniValue statsUV(UniValue::VOBJ);
1462 0 : BIP9Stats statsStruct = chainman.m_versionbitscache.Statistics(active_chain_tip, chainman.GetConsensus(), id);
1463 0 : statsUV.pushKV("period", statsStruct.period);
1464 0 : statsUV.pushKV("elapsed", statsStruct.elapsed);
1465 0 : statsUV.pushKV("count", statsStruct.count);
1466 0 : if (ThresholdState::LOCKED_IN != thresholdState) {
1467 0 : statsUV.pushKV("threshold", statsStruct.threshold);
1468 0 : statsUV.pushKV("possible", statsStruct.possible);
1469 0 : }
1470 0 : bip9.pushKV("statistics", statsUV);
1471 0 : }
1472 0 : if (ThresholdState::LOCKED_IN == thresholdState) {
1473 0 : bip9.pushKV("activation_height", since_height + static_cast<int>(chainman.GetConsensus().vDeployments[id].nWindowSize));
1474 0 : }
1475 0 : bip9.pushKV("min_activation_height", chainman.GetConsensus().vDeployments[id].min_activation_height);
1476 :
1477 0 : UniValue rv(UniValue::VOBJ);
1478 0 : rv.pushKV("type", "bip9");
1479 0 : rv.pushKV("bip9", bip9);
1480 0 : if (ThresholdState::ACTIVE == thresholdState) {
1481 0 : rv.pushKV("height", since_height);
1482 0 : }
1483 0 : rv.pushKV("active", ThresholdState::ACTIVE == thresholdState);
1484 :
1485 0 : softforks.pushKV(DeploymentName(id), rv);
1486 0 : }
1487 :
1488 92 : RPCHelpMan getblockchaininfo()
1489 : {
1490 184 : return RPCHelpMan{"getblockchaininfo",
1491 92 : "Returns an object containing various state info regarding blockchain processing.\n",
1492 92 : {},
1493 92 : RPCResult{
1494 92 : RPCResult::Type::OBJ, "", "",
1495 1656 : {
1496 92 : {RPCResult::Type::STR, "chain", "current network name (main, test, regtest) and "
1497 : "devnet or devnet-<name> for \"-devnet\" and \"-devnet=<name>\" respectively\n"},
1498 92 : {RPCResult::Type::NUM, "blocks", "the height of the most-work fully-validated chain. The genesis block has height 0"},
1499 92 : {RPCResult::Type::NUM, "headers", "the current number of headers we have validated"},
1500 92 : {RPCResult::Type::STR, "bestblockhash", "the hash of the currently best block"},
1501 92 : {RPCResult::Type::NUM, "difficulty", "the current difficulty"},
1502 92 : {RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME},
1503 92 : {RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME},
1504 92 : {RPCResult::Type::NUM, "verificationprogress", "estimate of verification progress [0..1]"},
1505 92 : {RPCResult::Type::BOOL, "initialblockdownload", "(debug information) estimate of whether this node is in Initial Block Download mode"},
1506 92 : {RPCResult::Type::STR_HEX, "chainwork", "total amount of work in active chain, in hexadecimal"},
1507 92 : {RPCResult::Type::NUM, "size_on_disk", "the estimated size of the block and undo files on disk"},
1508 92 : {RPCResult::Type::BOOL, "pruned", "if the blocks are subject to pruning"},
1509 92 : {RPCResult::Type::NUM, "pruneheight", /*optional=*/true, "height of the last block pruned, plus one (only present if pruning is enabled)"},
1510 92 : {RPCResult::Type::BOOL, "automatic_pruning", /*optional=*/true, "whether automatic pruning is enabled (only present if pruning is enabled)"},
1511 92 : {RPCResult::Type::NUM, "prune_target_size", /*optional=*/true, "the target size used by pruning (only present if automatic pruning is enabled)"},
1512 184 : {RPCResult::Type::OBJ_DYN, "softforks", "status of softforks in progress",
1513 184 : {
1514 184 : {RPCResult::Type::OBJ, "xxxx", "name of the softfork",
1515 460 : {
1516 92 : {RPCResult::Type::STR, "type", "one of \"buried\", \"bip9\""},
1517 184 : {RPCResult::Type::OBJ, "bip9", /*optional=*/true, "status of bip9 softforks (only for \"bip9\" type)",
1518 1012 : {
1519 92 : {RPCResult::Type::STR, "status", "one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\""},
1520 92 : {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 92 : {RPCResult::Type::NUM_TIME, "start_time", "the minimum median time past of a block at which the bit gains its meaning"},
1522 92 : {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 92 : {RPCResult::Type::BOOL, "ehf", "returns true for EHF activated forks"},
1524 92 : {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 92 : {RPCResult::Type::NUM, "since", "height of the first block to which the status applies"},
1526 92 : {RPCResult::Type::NUM, "activation_height", "expected activation height for this softfork (only for \"locked_in\" status)"},
1527 92 : {RPCResult::Type::NUM, "min_activation_height", "minimum height of blocks for which the rules may be enforced"},
1528 184 : {RPCResult::Type::OBJ, "statistics", /*optional=*/true, "numeric statistics about signalling for a softfork (only for \"started\" and \"locked_in\" status)",
1529 552 : {
1530 92 : {RPCResult::Type::NUM, "period", "the length in blocks of the signalling period"},
1531 92 : {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 92 : {RPCResult::Type::NUM, "elapsed", "the number of blocks elapsed since the beginning of the current period"},
1533 92 : {RPCResult::Type::NUM, "count", "the number of blocks with the version bit set in the current period"},
1534 92 : {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 92 : {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 92 : {RPCResult::Type::BOOL, "active", "true if the rules are enforced for the mempool and the next block"},
1539 : }},
1540 : }},
1541 92 : {RPCResult::Type::STR, "warnings", "any network and blockchain warnings"},
1542 : }},
1543 92 : RPCExamples{
1544 92 : HelpExampleCli("getblockchaininfo", "")
1545 92 : + HelpExampleRpc("getblockchaininfo", "")
1546 : },
1547 92 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1548 : {
1549 :
1550 0 : const NodeContext& node = EnsureAnyNodeContext(request.context);
1551 0 : const ArgsManager& args{EnsureArgsman(node)};
1552 0 : ChainstateManager& chainman = EnsureChainman(node);
1553 :
1554 0 : LOCK(cs_main);
1555 0 : CChainState& active_chainstate = chainman.ActiveChainstate();
1556 :
1557 0 : const CBlockIndex& tip{*CHECK_NONFATAL(active_chainstate.m_chain.Tip())};
1558 0 : const int height{tip.nHeight};
1559 :
1560 0 : const auto ehfSignals{active_chainstate.ChainHelper().GetSignalsStage(&tip)};
1561 :
1562 0 : UniValue obj(UniValue::VOBJ);
1563 0 : if (args.IsArgSet("-devnet")) {
1564 0 : obj.pushKV("chain", args.GetDevNetName());
1565 0 : } else {
1566 0 : obj.pushKV("chain", Params().NetworkIDString());
1567 : }
1568 0 : obj.pushKV("blocks", height);
1569 0 : obj.pushKV("headers", chainman.m_best_header ? chainman.m_best_header->nHeight : -1);
1570 0 : obj.pushKV("bestblockhash", tip.GetBlockHash().GetHex());
1571 0 : obj.pushKV("difficulty", GetDifficulty(&tip));
1572 0 : obj.pushKV("time", tip.GetBlockTime());
1573 0 : obj.pushKV("mediantime", tip.GetMedianTimePast());
1574 0 : obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), &tip));
1575 0 : obj.pushKV("initialblockdownload", active_chainstate.IsInitialBlockDownload());
1576 0 : obj.pushKV("chainwork", tip.nChainWork.GetHex());
1577 0 : obj.pushKV("size_on_disk", chainman.m_blockman.CalculateCurrentUsage());
1578 0 : obj.pushKV("pruned", node::fPruneMode);
1579 0 : if (node::fPruneMode) {
1580 0 : obj.pushKV("pruneheight", chainman.m_blockman.GetFirstStoredBlock(tip)->nHeight);
1581 :
1582 : // if 0, execution bypasses the whole if block.
1583 0 : bool automatic_pruning{args.GetIntArg("-prune", 0) != 1};
1584 0 : obj.pushKV("automatic_pruning", automatic_pruning);
1585 0 : if (automatic_pruning) {
1586 0 : obj.pushKV("prune_target_size", node::nPruneTarget);
1587 0 : }
1588 0 : }
1589 :
1590 0 : UniValue softforks(UniValue::VOBJ);
1591 0 : 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 0 : SoftForkDescPushBack(&tip, softforks, chainman, deploy);
1609 : }
1610 0 : for (auto ehf_deploy : { /* sorted by activation block */
1611 : Consensus::DEPLOYMENT_V24,
1612 : Consensus::DEPLOYMENT_TESTDUMMY }) {
1613 0 : SoftForkDescPushBack(&tip, ehfSignals, softforks, chainman, ehf_deploy);
1614 : }
1615 0 : obj.pushKV("softforks", softforks);
1616 :
1617 0 : obj.pushKV("warnings", GetWarnings(false).original);
1618 0 : return obj;
1619 0 : },
1620 : };
1621 0 : }
1622 :
1623 : /** Comparison function for sorting the getchaintips heads. */
1624 : struct CompareBlocksByHeight
1625 : {
1626 0 : 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 0 : if (a->nHeight != b->nHeight)
1632 0 : return (a->nHeight > b->nHeight);
1633 :
1634 0 : return a < b;
1635 0 : }
1636 : };
1637 :
1638 92 : static RPCHelpMan getchaintips()
1639 : {
1640 184 : return RPCHelpMan{"getchaintips",
1641 92 : "Return information about all known tips in the block tree,"
1642 : " including the main chain as well as orphaned branches.\n",
1643 276 : {
1644 92 : {"count", RPCArg::Type::NUM, RPCArg::Default{INT_MAX}, "only show this much of latest tips"},
1645 92 : {"branchlen", RPCArg::Type::NUM, RPCArg::Default{-1}, "only show tips that have equal or greater length of branch"},
1646 : },
1647 92 : RPCResult{
1648 92 : RPCResult::Type::ARR, "", "",
1649 184 : {{RPCResult::Type::OBJ, "", "",
1650 736 : {
1651 92 : {RPCResult::Type::NUM, "height", "height of the chain tip"},
1652 92 : {RPCResult::Type::STR_HEX, "hash", "block hash of the tip"},
1653 92 : {RPCResult::Type::NUM, "difficulty", "The difficulty"},
1654 92 : {RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the current chain (in hex)"},
1655 92 : {RPCResult::Type::NUM, "branchlen", "zero for main chain, otherwise length of branch connecting the tip to the main chain"},
1656 92 : {RPCResult::Type::STR_HEX, "forkpoint", "same as \"hash\" for the main chain"},
1657 92 : {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 92 : RPCExamples{
1667 92 : HelpExampleCli("getchaintips", "")
1668 92 : + HelpExampleRpc("getchaintips", "")
1669 : },
1670 92 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1671 : {
1672 :
1673 0 : ChainstateManager& chainman = EnsureAnyChainman(request.context);
1674 0 : LOCK(cs_main);
1675 0 : 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 0 : std::set<const CBlockIndex*, CompareBlocksByHeight> setTips;
1685 0 : std::set<const CBlockIndex*> setOrphans;
1686 0 : std::set<const CBlockIndex*> setPrevs;
1687 :
1688 0 : for (const auto& [_, block_index] : chainman.BlockIndex()) {
1689 0 : if (!active_chain.Contains(&block_index)) {
1690 0 : setOrphans.insert(&block_index);
1691 0 : setPrevs.insert(block_index.pprev);
1692 0 : }
1693 : }
1694 :
1695 0 : for (const auto& orphan : setOrphans) {
1696 0 : if (setPrevs.erase(orphan) == 0) {
1697 0 : setTips.insert(orphan);
1698 0 : }
1699 : }
1700 :
1701 : // Always report the currently active tip.
1702 0 : setTips.insert(active_chain.Tip());
1703 :
1704 0 : int nCountMax{request.params[0].isNull() ? INT_MAX : request.params[0].getInt<int>()};
1705 0 : const int nBranchMin{request.params[1].isNull() ? -1: request.params[1].getInt<int>()};
1706 :
1707 : /* Construct the output array. */
1708 0 : UniValue res(UniValue::VARR);
1709 0 : for (const CBlockIndex* block : setTips)
1710 : {
1711 0 : const CBlockIndex* pindexFork = active_chain.FindFork(block);
1712 0 : const int branchLen = block->nHeight - pindexFork->nHeight;
1713 0 : if(branchLen < nBranchMin) continue;
1714 :
1715 0 : if(nCountMax-- < 1) break;
1716 :
1717 0 : UniValue obj(UniValue::VOBJ);
1718 0 : obj.pushKV("height", block->nHeight);
1719 0 : obj.pushKV("hash", block->phashBlock->GetHex());
1720 0 : obj.pushKV("difficulty", GetDifficulty(block));
1721 0 : obj.pushKV("chainwork", block->nChainWork.GetHex());
1722 0 : obj.pushKV("branchlen", branchLen);
1723 0 : obj.pushKV("forkpoint", pindexFork->phashBlock->GetHex());
1724 :
1725 0 : std::string status;
1726 0 : if (active_chain.Contains(block)) {
1727 : // This block is part of the currently active chain.
1728 0 : status = "active";
1729 0 : } else if (block->nStatus & BLOCK_FAILED_MASK) {
1730 : // This block or one of its ancestors is invalid.
1731 0 : status = "invalid";
1732 0 : } else if (block->nStatus & BLOCK_CONFLICT_CHAINLOCK) {
1733 : // This block or one of its ancestors is conflicting with ChainLocks.
1734 0 : status = "conflicting";
1735 0 : } else if (!block->HaveTxsDownloaded()) {
1736 : // This block cannot be connected because full block data for it or one of its parents is missing.
1737 0 : status = "headers-only";
1738 0 : } 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 0 : status = "valid-fork";
1741 0 : } 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 0 : obj.pushKV("status", status);
1749 :
1750 0 : res.push_back(obj);
1751 0 : }
1752 :
1753 0 : return res;
1754 0 : },
1755 : };
1756 0 : }
1757 :
1758 92 : static RPCHelpMan preciousblock()
1759 : {
1760 184 : return RPCHelpMan{"preciousblock",
1761 92 : "\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 184 : {
1765 92 : {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hash of the block to mark as precious"},
1766 : },
1767 92 : RPCResult{RPCResult::Type::NONE, "", ""},
1768 92 : RPCExamples{
1769 92 : HelpExampleCli("preciousblock", "\"blockhash\"")
1770 92 : + HelpExampleRpc("preciousblock", "\"blockhash\"")
1771 : },
1772 92 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1773 : {
1774 0 : uint256 hash(ParseHashV(request.params[0], "blockhash"));
1775 : CBlockIndex* pblockindex;
1776 :
1777 0 : ChainstateManager& chainman = EnsureAnyChainman(request.context);
1778 : {
1779 0 : LOCK(cs_main);
1780 0 : pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
1781 0 : if (!pblockindex) {
1782 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
1783 : }
1784 0 : }
1785 :
1786 0 : BlockValidationState state;
1787 0 : chainman.ActiveChainstate().PreciousBlock(state, pblockindex);
1788 :
1789 0 : if (!state.IsValid()) {
1790 0 : throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString());
1791 : }
1792 :
1793 0 : return UniValue::VNULL;
1794 0 : },
1795 : };
1796 0 : }
1797 :
1798 92 : static RPCHelpMan invalidateblock()
1799 : {
1800 184 : return RPCHelpMan{"invalidateblock",
1801 92 : "\nPermanently marks a block as invalid, as if it violated a consensus rule.\n",
1802 184 : {
1803 92 : {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hash of the block to mark as invalid"},
1804 : },
1805 92 : RPCResult{RPCResult::Type::NONE, "", ""},
1806 92 : RPCExamples{
1807 92 : HelpExampleCli("invalidateblock", "\"blockhash\"")
1808 92 : + HelpExampleRpc("invalidateblock", "\"blockhash\"")
1809 : },
1810 92 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1811 : {
1812 0 : uint256 hash(ParseHashV(request.params[0], "blockhash"));
1813 0 : BlockValidationState state;
1814 :
1815 : CBlockIndex* pblockindex;
1816 0 : ChainstateManager& chainman = EnsureAnyChainman(request.context);
1817 : {
1818 0 : LOCK(cs_main);
1819 0 : pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
1820 0 : if (!pblockindex) {
1821 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
1822 : }
1823 0 : }
1824 :
1825 0 : CChainState& active_chainstate = chainman.ActiveChainstate();
1826 0 : active_chainstate.InvalidateBlock(state, pblockindex);
1827 :
1828 0 : if (state.IsValid()) {
1829 0 : active_chainstate.ActivateBestChain(state);
1830 0 : }
1831 :
1832 0 : if (!state.IsValid()) {
1833 0 : throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString());
1834 : }
1835 :
1836 0 : return UniValue::VNULL;
1837 0 : },
1838 : };
1839 0 : }
1840 :
1841 92 : static RPCHelpMan reconsiderblock()
1842 : {
1843 184 : return RPCHelpMan{"reconsiderblock",
1844 92 : "\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 276 : {
1847 92 : {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hash of the block to reconsider"},
1848 92 : {"ignore_chainlocks", RPCArg::Type::BOOL, RPCArg::Default{false}, "if true, existing chainlocks will be ignored"},
1849 : },
1850 92 : RPCResult{RPCResult::Type::NONE, "", ""},
1851 92 : RPCExamples{
1852 92 : HelpExampleCli("reconsiderblock", "\"blockhash\"")
1853 92 : + HelpExampleRpc("reconsiderblock", "\"blockhash\"")
1854 : },
1855 92 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1856 : {
1857 0 : ChainstateManager& chainman = EnsureAnyChainman(request.context);
1858 0 : CChainState& active_chainstate = chainman.ActiveChainstate();
1859 :
1860 0 : uint256 hash(ParseHashV(request.params[0], "blockhash"));
1861 0 : const bool ignore_chainlocks{request.params[1].isNull() ? false : request.params[1].get_bool()};
1862 :
1863 : {
1864 0 : LOCK(cs_main);
1865 0 : CBlockIndex* pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
1866 0 : if (!pblockindex) {
1867 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
1868 : }
1869 :
1870 0 : active_chainstate.ResetBlockFailureFlags(pblockindex, ignore_chainlocks);
1871 0 : }
1872 :
1873 0 : BlockValidationState state;
1874 0 : active_chainstate.ActivateBestChain(state);
1875 :
1876 0 : if (!state.IsValid()) {
1877 0 : throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString());
1878 : }
1879 :
1880 0 : return UniValue::VNULL;
1881 0 : },
1882 : };
1883 0 : }
1884 :
1885 92 : static RPCHelpMan getchaintxstats()
1886 : {
1887 184 : return RPCHelpMan{"getchaintxstats",
1888 92 : "\nCompute statistics about the total number and rate of transactions in the chain.\n",
1889 276 : {
1890 92 : {"nblocks", RPCArg::Type::NUM, RPCArg::DefaultHint{"one month"}, "Size of the window in number of blocks"},
1891 92 : {"blockhash", RPCArg::Type::STR_HEX, RPCArg::DefaultHint{"chain tip"}, "The hash of the block that ends the window."},
1892 : },
1893 92 : RPCResult{
1894 92 : RPCResult::Type::OBJ, "", "",
1895 828 : {
1896 92 : {RPCResult::Type::NUM_TIME, "time", "The timestamp for the final block in the window, expressed in " + UNIX_EPOCH_TIME},
1897 92 : {RPCResult::Type::NUM, "txcount", "The total number of transactions in the chain up to that point"},
1898 92 : {RPCResult::Type::STR_HEX, "window_final_block_hash", "The hash of the final block in the window"},
1899 92 : {RPCResult::Type::NUM, "window_final_block_height", "The height of the final block in the window."},
1900 92 : {RPCResult::Type::NUM, "window_block_count", "Size of the window in number of blocks"},
1901 92 : {RPCResult::Type::NUM, "window_tx_count", /*optional=*/true, "The number of transactions in the window. Only returned if \"window_block_count\" is > 0"},
1902 92 : {RPCResult::Type::NUM, "window_interval", /*optional=*/true, "The elapsed time in the window in seconds. Only returned if \"window_block_count\" is > 0"},
1903 92 : {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 92 : RPCExamples{
1906 92 : HelpExampleCli("getchaintxstats", "")
1907 92 : + HelpExampleRpc("getchaintxstats", "2016")
1908 : },
1909 92 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1910 : {
1911 0 : ChainstateManager& chainman = EnsureAnyChainman(request.context);
1912 :
1913 0 : CChain& active_chain = chainman.ActiveChain();
1914 : const CBlockIndex* pindex;
1915 0 : int blockcount = 30 * 24 * 60 * 60 / Params().GetConsensus().nPowTargetSpacing; // By default: 1 month
1916 :
1917 0 : if (request.params[1].isNull()) {
1918 0 : LOCK(cs_main);
1919 0 : pindex = active_chain.Tip();
1920 0 : } else {
1921 0 : uint256 hash(ParseHashV(request.params[1], "blockhash"));
1922 0 : LOCK(cs_main);
1923 0 : pindex = chainman.m_blockman.LookupBlockIndex(hash);
1924 0 : if (!pindex) {
1925 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
1926 : }
1927 0 : if (!active_chain.Contains(pindex)) {
1928 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Block is not in main chain");
1929 : }
1930 0 : }
1931 :
1932 0 : CHECK_NONFATAL(pindex != nullptr);
1933 :
1934 0 : if (request.params[0].isNull()) {
1935 0 : blockcount = std::max(0, std::min(blockcount, pindex->nHeight - 1));
1936 0 : } else {
1937 0 : blockcount = request.params[0].getInt<int>();
1938 :
1939 0 : if (blockcount < 0 || (blockcount > 0 && blockcount >= pindex->nHeight)) {
1940 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block count: should be between 0 and the block's height - 1");
1941 : }
1942 : }
1943 :
1944 0 : const CBlockIndex& past_block{*CHECK_NONFATAL(pindex->GetAncestor(pindex->nHeight - blockcount))};
1945 0 : const int64_t nTimeDiff{pindex->GetMedianTimePast() - past_block.GetMedianTimePast()};
1946 0 : const int nTxDiff = pindex->nChainTx - past_block.nChainTx;
1947 :
1948 0 : UniValue ret(UniValue::VOBJ);
1949 0 : ret.pushKV("time", (int64_t)pindex->nTime);
1950 0 : ret.pushKV("txcount", (int64_t)pindex->nChainTx);
1951 0 : ret.pushKV("window_final_block_hash", pindex->GetBlockHash().GetHex());
1952 0 : ret.pushKV("window_final_block_height", pindex->nHeight);
1953 0 : ret.pushKV("window_block_count", blockcount);
1954 0 : if (blockcount > 0) {
1955 0 : ret.pushKV("window_tx_count", nTxDiff);
1956 0 : ret.pushKV("window_interval", nTimeDiff);
1957 0 : if (nTimeDiff > 0) {
1958 0 : ret.pushKV("txrate", ((double)nTxDiff) / nTimeDiff);
1959 0 : }
1960 0 : }
1961 :
1962 0 : return ret;
1963 0 : },
1964 : };
1965 0 : }
1966 :
1967 : template<typename T>
1968 0 : static T CalculateTruncatedMedian(std::vector<T>& scores)
1969 : {
1970 0 : size_t size = scores.size();
1971 0 : if (size == 0) {
1972 0 : return 0;
1973 : }
1974 :
1975 0 : std::sort(scores.begin(), scores.end());
1976 0 : if (size % 2 == 0) {
1977 0 : return (scores[size / 2 - 1] + scores[size / 2]) / 2;
1978 : } else {
1979 0 : return scores[size / 2];
1980 : }
1981 0 : }
1982 :
1983 4 : void CalculatePercentilesBySize(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES], std::vector<std::pair<CAmount, int64_t>>& scores, int64_t total_size)
1984 : {
1985 4 : if (scores.empty()) {
1986 0 : return;
1987 : }
1988 :
1989 4 : std::sort(scores.begin(), scores.end());
1990 :
1991 : // 10th, 25th, 50th, 75th, and 90th percentile weight units.
1992 20 : const double weights[NUM_GETBLOCKSTATS_PERCENTILES] = {
1993 20 : 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 4 : int64_t next_percentile_index = 0;
1997 4 : int64_t cumulative_weight = 0;
1998 220 : for (const auto& element : scores) {
1999 216 : cumulative_weight += element.second;
2000 236 : while (next_percentile_index < NUM_GETBLOCKSTATS_PERCENTILES && cumulative_weight >= weights[next_percentile_index]) {
2001 20 : result[next_percentile_index] = element.first;
2002 20 : ++next_percentile_index;
2003 : }
2004 : }
2005 :
2006 : // Fill any remaining percentiles with the last value.
2007 4 : for (int64_t i = next_percentile_index; i < NUM_GETBLOCKSTATS_PERCENTILES; i++) {
2008 0 : result[i] = scores.back().first;
2009 0 : }
2010 4 : }
2011 :
2012 : template<typename T>
2013 0 : static inline bool SetHasKeys(const std::set<T>& set) {return false;}
2014 : template<typename T, typename Tk, typename... Args>
2015 0 : static inline bool SetHasKeys(const std::set<T>& set, const Tk& key, const Args&... args)
2016 : {
2017 0 : 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 92 : static RPCHelpMan getblockstats()
2024 : {
2025 184 : return RPCHelpMan{"getblockstats",
2026 92 : "\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 276 : {
2029 92 : {"hash_or_height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The block hash or height of the target block", "", {"", "string or numeric"}},
2030 184 : {"stats", RPCArg::Type::ARR, RPCArg::DefaultHint{"all values"}, "Values to plot (see result below)",
2031 276 : {
2032 92 : {"height", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Selected statistic"},
2033 92 : {"time", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Selected statistic"},
2034 : },
2035 92 : "stats"},
2036 : },
2037 92 : RPCResult{
2038 92 : RPCResult::Type::OBJ, "", "",
2039 2576 : {
2040 92 : {RPCResult::Type::NUM, "avgfee", /*optional=*/true, "Average fee in the block"},
2041 92 : {RPCResult::Type::NUM, "avgfeerate", /*optional=*/true, "Average feerate (in duffs per byte)"},
2042 92 : {RPCResult::Type::NUM, "avgtxsize", /*optional=*/true, "Average transaction size"},
2043 92 : {RPCResult::Type::STR_HEX, "blockhash", /*optional=*/true, "The block hash (to check for potential reorgs)"},
2044 184 : {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 552 : {
2046 92 : {RPCResult::Type::NUM, "10th_percentile_feerate", "The 10th percentile feerate"},
2047 92 : {RPCResult::Type::NUM, "25th_percentile_feerate", "The 25th percentile feerate"},
2048 92 : {RPCResult::Type::NUM, "50th_percentile_feerate", "The 50th percentile feerate"},
2049 92 : {RPCResult::Type::NUM, "75th_percentile_feerate", "The 75th percentile feerate"},
2050 92 : {RPCResult::Type::NUM, "90th_percentile_feerate", "The 90th percentile feerate"},
2051 : }},
2052 92 : {RPCResult::Type::NUM, "height", /*optional=*/true, "The height of the block"},
2053 92 : {RPCResult::Type::NUM, "ins", /*optional=*/true, "The number of inputs (excluding coinbase)"},
2054 92 : {RPCResult::Type::NUM, "maxfee", /*optional=*/true, "Maximum fee in the block"},
2055 92 : {RPCResult::Type::NUM, "maxfeerate", /*optional=*/true, "Maximum feerate (in duffs per virtual byte)"},
2056 92 : {RPCResult::Type::NUM, "maxtxsize", /*optional=*/true, "Maximum transaction size"},
2057 92 : {RPCResult::Type::NUM, "medianfee", /*optional=*/true, "Truncated median fee in the block"},
2058 92 : {RPCResult::Type::NUM, "mediantime", /*optional=*/true, "The block median time past"},
2059 92 : {RPCResult::Type::NUM, "mediantxsize", /*optional=*/true, "Truncated median transaction size"},
2060 92 : {RPCResult::Type::NUM, "minfee", /*optional=*/true, "Minimum fee in the block"},
2061 92 : {RPCResult::Type::NUM, "minfeerate", /*optional=*/true, "Minimum feerate (in duffs per virtual byte)"},
2062 92 : {RPCResult::Type::NUM, "mintxsize", /*optional=*/true, "Minimum transaction size"},
2063 92 : {RPCResult::Type::NUM, "outs", /*optional=*/true, "The number of outputs"},
2064 92 : {RPCResult::Type::NUM, "subsidy", /*optional=*/true, "The block subsidy"},
2065 92 : {RPCResult::Type::NUM, "time", /*optional=*/true, "The block time"},
2066 92 : {RPCResult::Type::NUM, "total_out", /*optional=*/true, "Total amount in all outputs (excluding coinbase and thus reward [ie subsidy + totalfee])"},
2067 92 : {RPCResult::Type::NUM, "total_size", /*optional=*/true, "Total size of all non-coinbase transactions"},
2068 92 : {RPCResult::Type::NUM, "totalfee", /*optional=*/true, "The fee total"},
2069 92 : {RPCResult::Type::NUM, "txs", /*optional=*/true, "The number of transactions (including coinbase)"},
2070 92 : {RPCResult::Type::NUM, "utxo_increase", /*optional=*/true, "The increase/decrease in the number of unspent outputs (not discounting op_return and similar)"},
2071 92 : {RPCResult::Type::NUM, "utxo_size_inc", /*optional=*/true, "The increase/decrease in size for the utxo index (not discounting op_return and similar)"},
2072 92 : {RPCResult::Type::NUM, "utxo_increase_actual", /*optional=*/true, "The increase/decrease in the number of unspent outputs, not counting unspendables"},
2073 92 : {RPCResult::Type::NUM, "utxo_size_inc_actual", /*optional=*/true, "The increase/decrease in size for the utxo index, not counting unspendables"},
2074 : }},
2075 92 : RPCExamples{
2076 184 : HelpExampleCli("getblockstats", R"('"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09"' '["minfeerate","avgfeerate"]')") +
2077 184 : HelpExampleCli("getblockstats", R"(1000 '["minfeerate","avgfeerate"]')") +
2078 184 : HelpExampleRpc("getblockstats", R"("00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09", ["minfeerate","avgfeerate"])") +
2079 92 : HelpExampleRpc("getblockstats", R"(1000, ["minfeerate","avgfeerate"])")
2080 : },
2081 92 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
2082 : {
2083 0 : if (g_txindex) {
2084 0 : g_txindex->BlockUntilSyncedToCurrentChain();
2085 0 : }
2086 :
2087 0 : ChainstateManager& chainman = EnsureAnyChainman(request.context);
2088 0 : const CBlockIndex& pindex{*CHECK_NONFATAL(ParseHashOrHeight(request.params[0], chainman))};
2089 :
2090 0 : std::set<std::string> stats;
2091 0 : if (!request.params[1].isNull()) {
2092 0 : const UniValue stats_univalue = request.params[1].get_array();
2093 0 : for (unsigned int i = 0; i < stats_univalue.size(); i++) {
2094 0 : const std::string stat = stats_univalue[i].get_str();
2095 0 : stats.insert(stat);
2096 0 : }
2097 0 : }
2098 :
2099 0 : const CBlock& block = GetBlockChecked(chainman.m_blockman, &pindex);
2100 0 : const CBlockUndo& blockUndo = GetUndoChecked(chainman.m_blockman, &pindex);
2101 :
2102 0 : const bool do_all = stats.size() == 0; // Calculate everything if nothing selected (default)
2103 0 : const bool do_mediantxsize = do_all || stats.count("mediantxsize") != 0;
2104 0 : const bool do_medianfee = do_all || stats.count("medianfee") != 0;
2105 0 : const bool do_feerate_percentiles = do_all || stats.count("feerate_percentiles") != 0;
2106 0 : const bool loop_inputs = do_all || do_medianfee || do_feerate_percentiles ||
2107 0 : SetHasKeys(stats, "utxo_increase", "utxo_increase_actual", "utxo_size_inc", "utxo_size_inc_actual", "totalfee", "avgfee", "avgfeerate", "minfee", "maxfee", "minfeerate", "maxfeerate");
2108 0 : const bool loop_outputs = do_all || loop_inputs || stats.count("total_out");
2109 0 : const bool do_calculate_size = do_all || do_mediantxsize ||
2110 0 : SetHasKeys(stats, "total_size", "avgtxsize", "mintxsize", "maxtxsize", "avgfeerate", "feerate_percentiles", "minfeerate", "maxfeerate");
2111 :
2112 0 : CAmount maxfee = 0;
2113 0 : CAmount maxfeerate = 0;
2114 0 : CAmount minfee = MAX_MONEY;
2115 0 : CAmount minfeerate = MAX_MONEY;
2116 0 : CAmount total_out = 0;
2117 0 : CAmount totalfee = 0;
2118 0 : int64_t inputs = 0;
2119 0 : int64_t maxtxsize = 0;
2120 0 : int64_t mintxsize = MaxBlockSize();
2121 0 : int64_t outputs = 0;
2122 0 : int64_t total_size = 0;
2123 0 : int64_t utxos = 0;
2124 0 : int64_t utxo_size_inc = 0;
2125 0 : int64_t utxo_size_inc_actual = 0;
2126 0 : std::vector<CAmount> fee_array;
2127 0 : std::vector<std::pair<CAmount, int64_t>> feerate_array;
2128 0 : std::vector<int64_t> txsize_array;
2129 :
2130 0 : for (size_t i = 0; i < block.vtx.size(); ++i) {
2131 0 : const auto& tx = block.vtx.at(i);
2132 0 : outputs += tx->vout.size();
2133 :
2134 0 : CAmount tx_total_out = 0;
2135 0 : if (loop_outputs) {
2136 0 : for (const CTxOut& out : tx->vout) {
2137 0 : tx_total_out += out.nValue;
2138 :
2139 0 : size_t out_size = GetSerializeSize(out, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD;
2140 0 : 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 0 : if (pindex.nHeight == 0 || (IsBIP30Repeat(pindex) && tx->IsCoinBase())) continue;
2145 : // Skip unspendable outputs since they are not included in the UTXO set
2146 0 : if (out.scriptPubKey.IsUnspendable()) continue;
2147 :
2148 0 : ++utxos;
2149 0 : utxo_size_inc_actual += out_size;
2150 : }
2151 0 : }
2152 :
2153 0 : if (tx->IsCoinBase()) {
2154 0 : continue;
2155 : }
2156 :
2157 0 : inputs += tx->vin.size(); // Don't count coinbase's fake input
2158 0 : total_out += tx_total_out; // Don't count coinbase reward
2159 :
2160 0 : int64_t tx_size = 0;
2161 0 : if (do_calculate_size) {
2162 :
2163 0 : tx_size = tx->GetTotalSize();
2164 0 : if (do_mediantxsize) {
2165 0 : txsize_array.push_back(tx_size);
2166 0 : }
2167 0 : maxtxsize = std::max(maxtxsize, tx_size);
2168 0 : mintxsize = std::min(mintxsize, tx_size);
2169 0 : total_size += tx_size;
2170 0 : }
2171 :
2172 0 : if (loop_inputs) {
2173 0 : CAmount tx_total_in = 0;
2174 0 : const auto& txundo = blockUndo.vtxundo.at(i - 1);
2175 0 : for (const Coin& coin: txundo.vprevout) {
2176 0 : const CTxOut& prevoutput = coin.out;
2177 :
2178 0 : tx_total_in += prevoutput.nValue;
2179 0 : size_t prevout_size = GetSerializeSize(prevoutput, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD;
2180 0 : utxo_size_inc -= prevout_size;
2181 0 : utxo_size_inc_actual -= prevout_size;
2182 : }
2183 :
2184 0 : CAmount txfee = tx_total_in - tx_total_out;
2185 :
2186 0 : if (tx->IsPlatformTransfer()) {
2187 0 : auto payload = GetTxPayload<CAssetUnlockPayload>(*tx);
2188 0 : CHECK_NONFATAL(payload);
2189 0 : txfee = payload->getFee();
2190 0 : }
2191 :
2192 0 : CHECK_NONFATAL(MoneyRange(txfee));
2193 0 : if (do_medianfee) {
2194 0 : fee_array.push_back(txfee);
2195 0 : }
2196 0 : maxfee = std::max(maxfee, txfee);
2197 0 : minfee = std::min(minfee, txfee);
2198 0 : totalfee += txfee;
2199 :
2200 0 : CAmount feerate = tx_size ? txfee / tx_size : 0;
2201 0 : if (do_feerate_percentiles) {
2202 0 : feerate_array.emplace_back(feerate, tx_size);
2203 0 : }
2204 0 : maxfeerate = std::max(maxfeerate, feerate);
2205 0 : minfeerate = std::min(minfeerate, feerate);
2206 0 : }
2207 0 : }
2208 :
2209 0 : CAmount feerate_percentiles[NUM_GETBLOCKSTATS_PERCENTILES] = { 0 };
2210 0 : CalculatePercentilesBySize(feerate_percentiles, feerate_array, total_size);
2211 :
2212 0 : UniValue feerates_res(UniValue::VARR);
2213 0 : for (int64_t i = 0; i < NUM_GETBLOCKSTATS_PERCENTILES; i++) {
2214 0 : feerates_res.push_back(feerate_percentiles[i]);
2215 0 : }
2216 :
2217 0 : UniValue ret_all(UniValue::VOBJ);
2218 0 : ret_all.pushKV("avgfee", (block.vtx.size() > 1) ? totalfee / (block.vtx.size() - 1) : 0);
2219 0 : ret_all.pushKV("avgfeerate", total_size ? totalfee / total_size : 0); // Unit: sat/byte
2220 0 : ret_all.pushKV("avgtxsize", (block.vtx.size() > 1) ? total_size / (block.vtx.size() - 1) : 0);
2221 0 : ret_all.pushKV("blockhash", pindex.GetBlockHash().GetHex());
2222 0 : ret_all.pushKV("feerate_percentiles", feerates_res);
2223 0 : ret_all.pushKV("height", (int64_t)pindex.nHeight);
2224 0 : ret_all.pushKV("ins", inputs);
2225 0 : ret_all.pushKV("maxfee", maxfee);
2226 0 : ret_all.pushKV("maxfeerate", maxfeerate);
2227 0 : ret_all.pushKV("maxtxsize", maxtxsize);
2228 0 : ret_all.pushKV("medianfee", CalculateTruncatedMedian(fee_array));
2229 0 : ret_all.pushKV("mediantime", pindex.GetMedianTimePast());
2230 0 : ret_all.pushKV("mediantxsize", CalculateTruncatedMedian(txsize_array));
2231 0 : ret_all.pushKV("minfee", (minfee == MAX_MONEY) ? 0 : minfee);
2232 0 : ret_all.pushKV("minfeerate", (minfeerate == MAX_MONEY) ? 0 : minfeerate);
2233 0 : ret_all.pushKV("mintxsize", mintxsize == MaxBlockSize() ? 0 : mintxsize);
2234 0 : ret_all.pushKV("outs", outputs);
2235 0 : ret_all.pushKV("subsidy", GetBlockSubsidy(&pindex, Params().GetConsensus()));
2236 0 : ret_all.pushKV("time", pindex.GetBlockTime());
2237 0 : ret_all.pushKV("total_out", total_out);
2238 0 : ret_all.pushKV("total_size", total_size);
2239 0 : ret_all.pushKV("totalfee", totalfee);
2240 0 : ret_all.pushKV("txs", (int64_t)block.vtx.size());
2241 0 : ret_all.pushKV("utxo_increase", outputs - inputs);
2242 0 : ret_all.pushKV("utxo_size_inc", utxo_size_inc);
2243 0 : ret_all.pushKV("utxo_increase_actual", utxos - inputs);
2244 0 : ret_all.pushKV("utxo_size_inc_actual", utxo_size_inc_actual);
2245 :
2246 0 : if (do_all) {
2247 0 : return ret_all;
2248 : }
2249 :
2250 0 : UniValue ret(UniValue::VOBJ);
2251 0 : for (const std::string& stat : stats) {
2252 0 : const UniValue& value = ret_all[stat];
2253 0 : if (value.isNull()) {
2254 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid selected statistic '%s'", stat));
2255 : }
2256 0 : ret.pushKV(stat, value);
2257 : }
2258 0 : return ret;
2259 0 : },
2260 : };
2261 0 : }
2262 :
2263 92 : static RPCHelpMan getspecialtxes()
2264 : {
2265 184 : return RPCHelpMan{"getspecialtxes",
2266 92 : "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 552 : {
2271 92 : {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
2272 92 : {"type", RPCArg::Type::NUM, RPCArg::Default{-1}, "Filter special txes by type, -1 means all types"},
2273 92 : {"count", RPCArg::Type::NUM, RPCArg::Default{10}, "The number of transactions to return"},
2274 92 : {"skip", RPCArg::Type::NUM, RPCArg::Default{0}, "The number of transactions to skip"},
2275 92 : {"verbosity", RPCArg::Type::NUM, RPCArg::Default{0}, "0 for hashes, 1 for hex-encoded data, and 2 for json object"},
2276 : },
2277 368 : {
2278 184 : RPCResult{"for verbosity = 0",
2279 92 : RPCResult::Type::ARR, "", "",
2280 92 : {{RPCResult::Type::STR_HEX, "", "The transaction id"}}},
2281 184 : RPCResult{"for verbosity = 1",
2282 92 : RPCResult::Type::ARR, "", "",
2283 92 : {{RPCResult::Type::STR_HEX, "data", "A string that is serialized, hex-encoded data for the transaction"}}},
2284 184 : RPCResult{"for verbosity = 2",
2285 92 : RPCResult::Type::ARR, "", "",
2286 92 : {{RPCResult::Type::ELISION, "", "The transactions in the format of the getrawtransaction RPC"}}},
2287 : },
2288 92 : RPCExamples{
2289 92 : HelpExampleCli("getspecialtxes", "\"00000000000fd08c2fb661d2fcb0d49abb3a91e5f27082ce64feed3b4dede2e2\"")
2290 92 : + HelpExampleRpc("getspecialtxes", "\"00000000000fd08c2fb661d2fcb0d49abb3a91e5f27082ce64feed3b4dede2e2\"")
2291 : },
2292 92 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
2293 : {
2294 0 : const NodeContext& node = EnsureAnyNodeContext(request.context);
2295 :
2296 0 : ChainstateManager& chainman = EnsureChainman(node);
2297 0 : LOCK(cs_main);
2298 :
2299 0 : const CTxMemPool& mempool = EnsureMemPool(node);
2300 0 : const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
2301 0 : CHECK_NONFATAL(node.chainlocks);
2302 :
2303 0 : const uint256 blockhash(ParseHashV(request.params[0], "blockhash"));
2304 :
2305 0 : int nTxType = -1;
2306 0 : if (!request.params[1].isNull()) {
2307 0 : nTxType = request.params[1].getInt<int>();
2308 0 : }
2309 :
2310 0 : int nCount = 10;
2311 0 : if (!request.params[2].isNull()) {
2312 0 : nCount = request.params[2].getInt<int>();
2313 0 : if (nCount < 0)
2314 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
2315 0 : }
2316 :
2317 0 : int nSkip = 0;
2318 0 : if (!request.params[3].isNull()) {
2319 0 : nSkip = request.params[3].getInt<int>();
2320 0 : if (nSkip < 0)
2321 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative skip");
2322 0 : }
2323 :
2324 0 : int nVerbosity = 0;
2325 0 : if (!request.params[4].isNull()) {
2326 0 : nVerbosity = request.params[4].getInt<int>();
2327 0 : if (nVerbosity < 0 || nVerbosity > 2) {
2328 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Verbosity must be in range 0..2");
2329 : }
2330 0 : }
2331 :
2332 0 : const CBlockIndex* pblockindex = chainman.m_blockman.LookupBlockIndex(blockhash);
2333 0 : if (!pblockindex) {
2334 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
2335 : }
2336 :
2337 0 : const CBlock block = GetBlockChecked(chainman.m_blockman, pblockindex);
2338 :
2339 0 : int nTxNum = 0;
2340 0 : UniValue result(UniValue::VARR);
2341 :
2342 0 : for(const auto& tx : block.vtx)
2343 : {
2344 0 : if (!tx->HasExtraPayloadField() // ensure it's in fact a special tx
2345 0 : || (nTxType != -1 && tx->nType != nTxType)) { // ensure special tx type matches filter, if given
2346 0 : continue;
2347 : }
2348 :
2349 0 : nTxNum++;
2350 0 : if (nTxNum <= nSkip) continue;
2351 0 : if (nTxNum > nSkip + nCount) break;
2352 :
2353 0 : 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 0 : UniValue objTx(UniValue::VOBJ);
2360 0 : TxToJSON(*tx, blockhash, mempool, chainman.ActiveChainstate(), *node.chainlocks, *llmq_ctx.isman, objTx);
2361 0 : result.push_back(objTx);
2362 : break;
2363 0 : }
2364 0 : default : throw JSONRPCError(RPC_INTERNAL_ERROR, "Unsupported verbosity");
2365 : }
2366 : }
2367 0 : return result;
2368 0 : },
2369 : };
2370 0 : }
2371 :
2372 : namespace {
2373 : //! Search for a given set of pubkey scripts
2374 0 : 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 0 : scan_progress = 0;
2377 0 : count = 0;
2378 0 : while (cursor->Valid()) {
2379 0 : COutPoint key;
2380 0 : Coin coin;
2381 0 : if (!cursor->GetKey(key) || !cursor->GetValue(coin)) return false;
2382 0 : 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 0 : if (count % 256 == 0) {
2390 : // update progress reference every 256 item
2391 0 : uint32_t high = 0x100 * *key.hash.begin() + *(key.hash.begin() + 1);
2392 0 : scan_progress = (int)(high * 100.0 / 65536.0 + 0.5);
2393 0 : }
2394 0 : if (needles.count(coin.out.scriptPubKey)) {
2395 0 : out_results.emplace(key, coin);
2396 0 : }
2397 0 : cursor->Next();
2398 0 : }
2399 0 : scan_progress = 100;
2400 0 : return true;
2401 0 : }
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 0 : bool m_could_reserve{false};
2412 : public:
2413 0 : explicit CoinsViewScanReserver() = default;
2414 :
2415 0 : bool reserve() {
2416 0 : CHECK_NONFATAL(!m_could_reserve);
2417 0 : if (g_scan_in_progress.exchange(true)) {
2418 0 : return false;
2419 : }
2420 0 : CHECK_NONFATAL(g_scan_progress == 0);
2421 0 : m_could_reserve = true;
2422 0 : return true;
2423 0 : }
2424 :
2425 0 : ~CoinsViewScanReserver() {
2426 0 : if (m_could_reserve) {
2427 0 : g_scan_in_progress = false;
2428 0 : g_scan_progress = 0;
2429 0 : }
2430 0 : }
2431 : };
2432 :
2433 92 : static RPCHelpMan scantxoutset()
2434 : {
2435 : // scriptPubKey corresponding to mainnet address XcJSEb79KEeNbwMU7eE27aL5dhDwvjgBuH
2436 92 : const std::string EXAMPLE_DESCRIPTOR_RAW = "raw(76a91411b366edfc0a8b66feebae5c2e25a7b6a5d1cf3188ac)#fm24fxxy";
2437 :
2438 184 : return RPCHelpMan{"scantxoutset",
2439 92 : "\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 276 : {
2452 92 : {"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 184 : {"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 276 : {
2459 92 : {"descriptor", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "An output descriptor"},
2460 184 : {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "An object with output descriptor and metadata",
2461 276 : {
2462 92 : {"desc", RPCArg::Type::STR, RPCArg::Optional::NO, "An output descriptor"},
2463 92 : {"range", RPCArg::Type::RANGE, RPCArg::Default{1000}, "The range of HD chain indexes to explore (either end or [begin,end])"},
2464 : },
2465 : },
2466 : },
2467 92 : "[scanobjects,...]"},
2468 : },
2469 460 : {
2470 644 : RPCResult{"when action=='start'; only returns after scan completes", RPCResult::Type::OBJ, "", "", {
2471 92 : {RPCResult::Type::BOOL, "success", "Whether the scan was completed"},
2472 92 : {RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs scanned"},
2473 92 : {RPCResult::Type::NUM, "height", "The current block height (index)"},
2474 92 : {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"},
2475 184 : {RPCResult::Type::ARR, "unspents", "",
2476 184 : {
2477 184 : {RPCResult::Type::OBJ, "", "",
2478 736 : {
2479 92 : {RPCResult::Type::STR_HEX, "txid", "The transaction id"},
2480 92 : {RPCResult::Type::NUM, "vout", "The vout value"},
2481 92 : {RPCResult::Type::STR_HEX, "scriptPubKey", "The script key"},
2482 92 : {RPCResult::Type::STR, "desc", "A specialized descriptor for the matched scriptPubKey"},
2483 92 : {RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " of the unspent output"},
2484 92 : {RPCResult::Type::BOOL, "coinbase", "Whether this is a coinbase output"},
2485 92 : {RPCResult::Type::NUM, "height", "Height of the unspent transaction output"},
2486 : }},
2487 : }},
2488 92 : {RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount of all found unspent outputs in " + CURRENCY_UNIT},
2489 : }},
2490 92 : 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 184 : RPCResult{"when action=='status' and a scan is currently in progress", RPCResult::Type::OBJ, "", "",
2492 184 : {
2493 92 : {RPCResult::Type::NUM, "progress", "Approximate percent complete"},
2494 : }},
2495 92 : RPCResult{"when action=='status' and no scan is in progress - possibly already completed", RPCResult::Type::NONE, "", ""},
2496 : },
2497 92 : RPCExamples{
2498 184 : HelpExampleCli("scantxoutset", "start \'[\"" + EXAMPLE_DESCRIPTOR_RAW + "\"]\'") +
2499 184 : HelpExampleCli("scantxoutset", "status") +
2500 184 : HelpExampleCli("scantxoutset", "abort") +
2501 184 : HelpExampleRpc("scantxoutset", "\"start\", [\"" + EXAMPLE_DESCRIPTOR_RAW + "\"]") +
2502 184 : HelpExampleRpc("scantxoutset", "\"status\"") +
2503 92 : HelpExampleRpc("scantxoutset", "\"abort\"")
2504 : },
2505 92 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
2506 : {
2507 0 : RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR});
2508 :
2509 0 : UniValue result(UniValue::VOBJ);
2510 0 : if (request.params[0].get_str() == "status") {
2511 0 : CoinsViewScanReserver reserver;
2512 0 : if (reserver.reserve()) {
2513 : // no scan in progress
2514 0 : return UniValue::VNULL;
2515 : }
2516 0 : result.pushKV("progress", g_scan_progress.load());
2517 0 : return result;
2518 0 : } else if (request.params[0].get_str() == "abort") {
2519 0 : CoinsViewScanReserver reserver;
2520 0 : if (reserver.reserve()) {
2521 : // reserve was possible which means no scan was running
2522 0 : return false;
2523 : }
2524 : // set the abort flag
2525 0 : g_should_abort_scan = true;
2526 0 : return true;
2527 0 : } else if (request.params[0].get_str() == "start") {
2528 0 : CoinsViewScanReserver reserver;
2529 0 : if (!reserver.reserve()) {
2530 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan already in progress, use action \"abort\" or \"status\"");
2531 : }
2532 :
2533 0 : if (request.params.size() < 2) {
2534 0 : throw JSONRPCError(RPC_MISC_ERROR, "scanobjects argument is required for the start action");
2535 : }
2536 :
2537 0 : std::set<CScript> needles;
2538 0 : std::map<CScript, std::string> descriptors;
2539 0 : CAmount total_in = 0;
2540 :
2541 : // loop through the scan objects
2542 0 : for (const UniValue& scanobject : request.params[1].get_array().getValues()) {
2543 0 : FlatSigningProvider provider;
2544 0 : auto scripts = EvalDescriptorStringOrObject(scanobject, provider);
2545 0 : for (CScript& script : scripts) {
2546 0 : std::string inferred = InferDescriptor(script, provider)->ToString();
2547 0 : needles.emplace(script);
2548 0 : descriptors.emplace(std::move(script), std::move(inferred));
2549 0 : }
2550 0 : }
2551 :
2552 : // Scan the unspent transaction output set for inputs
2553 0 : UniValue unspents(UniValue::VARR);
2554 0 : std::vector<CTxOut> input_txos;
2555 0 : std::map<COutPoint, Coin> coins;
2556 0 : g_should_abort_scan = false;
2557 0 : int64_t count = 0;
2558 0 : std::unique_ptr<CCoinsViewCursor> pcursor;
2559 : const CBlockIndex* tip;
2560 0 : NodeContext& node = EnsureAnyNodeContext(request.context);
2561 : {
2562 0 : ChainstateManager& chainman = EnsureChainman(node);
2563 0 : LOCK(cs_main);
2564 0 : CChainState& active_chainstate = chainman.ActiveChainstate();
2565 0 : active_chainstate.ForceFlushStateToDisk();
2566 0 : pcursor = CHECK_NONFATAL(active_chainstate.CoinsDB().Cursor());
2567 0 : tip = CHECK_NONFATAL(active_chainstate.m_chain.Tip());
2568 0 : }
2569 0 : bool res = FindScriptPubKey(g_scan_progress, g_should_abort_scan, count, pcursor.get(), needles, coins, node.rpc_interruption_point);
2570 0 : result.pushKV("success", res);
2571 0 : result.pushKV("txouts", count);
2572 0 : result.pushKV("height", tip->nHeight);
2573 0 : result.pushKV("bestblock", tip->GetBlockHash().GetHex());
2574 :
2575 0 : for (const auto& it : coins) {
2576 0 : const COutPoint& outpoint = it.first;
2577 0 : const Coin& coin = it.second;
2578 0 : const CTxOut& txo = coin.out;
2579 0 : input_txos.push_back(txo);
2580 0 : total_in += txo.nValue;
2581 :
2582 0 : UniValue unspent(UniValue::VOBJ);
2583 0 : unspent.pushKV("txid", outpoint.hash.GetHex());
2584 0 : unspent.pushKV("vout", (int32_t)outpoint.n);
2585 0 : unspent.pushKV("scriptPubKey", HexStr(txo.scriptPubKey));
2586 0 : unspent.pushKV("desc", descriptors[txo.scriptPubKey]);
2587 0 : unspent.pushKV("amount", ValueFromAmount(txo.nValue));
2588 0 : unspent.pushKV("coinbase", coin.IsCoinBase());
2589 0 : unspent.pushKV("height", (int32_t)coin.nHeight);
2590 :
2591 0 : unspents.push_back(unspent);
2592 0 : }
2593 0 : result.pushKV("unspents", unspents);
2594 0 : result.pushKV("total_amount", ValueFromAmount(total_in));
2595 0 : } else {
2596 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid command");
2597 : }
2598 0 : return result;
2599 0 : },
2600 : };
2601 92 : }
2602 :
2603 92 : static RPCHelpMan getblockfilter()
2604 : {
2605 184 : return RPCHelpMan{"getblockfilter",
2606 92 : "\nRetrieve a BIP 157 content filter for a particular block.\n",
2607 276 : {
2608 92 : {"blockhash", RPCArg::Type::STR, RPCArg::Optional::NO, "The hash of the block"},
2609 92 : {"filtertype", RPCArg::Type::STR, RPCArg::Default{BlockFilterTypeName(BlockFilterType::BASIC_FILTER)}, "The type name of the filter"},
2610 : },
2611 92 : RPCResult{
2612 92 : RPCResult::Type::OBJ, "", "",
2613 276 : {
2614 92 : {RPCResult::Type::STR_HEX, "filter", "the hex-encoded filter data"},
2615 92 : {RPCResult::Type::STR_HEX, "header", "the hex-encoded filter header"},
2616 : }},
2617 92 : RPCExamples{
2618 184 : HelpExampleCli("getblockfilter", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" \"basic\"")+
2619 92 : HelpExampleRpc("getblockfilter", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\", \"basic\"")
2620 : },
2621 92 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
2622 : {
2623 0 : uint256 block_hash(ParseHashV(request.params[0], "blockhash"));
2624 0 : std::string filtertype_name = BlockFilterTypeName(BlockFilterType::BASIC_FILTER);
2625 0 : if (!request.params[1].isNull()) {
2626 0 : filtertype_name = request.params[1].get_str();
2627 0 : }
2628 :
2629 : BlockFilterType filtertype;
2630 0 : if (!BlockFilterTypeByName(filtertype_name, filtertype)) {
2631 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unknown filtertype");
2632 : }
2633 :
2634 0 : BlockFilterIndex* index = GetBlockFilterIndex(filtertype);
2635 0 : if (!index) {
2636 0 : 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 0 : ChainstateManager& chainman = EnsureAnyChainman(request.context);
2643 0 : LOCK(cs_main);
2644 0 : block_index = chainman.m_blockman.LookupBlockIndex(block_hash);
2645 0 : if (!block_index) {
2646 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
2647 : }
2648 0 : block_was_connected = block_index->IsValid(BLOCK_VALID_SCRIPTS);
2649 0 : }
2650 :
2651 0 : bool index_ready = index->BlockUntilSyncedToCurrentChain();
2652 :
2653 0 : BlockFilter filter;
2654 0 : uint256 filter_header;
2655 0 : if (!index->LookupFilter(block_index, filter) ||
2656 0 : !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 0 : UniValue ret(UniValue::VOBJ);
2675 0 : ret.pushKV("filter", HexStr(filter.GetEncodedFilter()));
2676 0 : ret.pushKV("header", filter_header.GetHex());
2677 0 : return ret;
2678 0 : },
2679 : };
2680 0 : }
2681 :
2682 : /**
2683 : * Serialize the UTXO set to a file for loading elsewhere.
2684 : *
2685 : * @see SnapshotMetadata
2686 : */
2687 92 : static RPCHelpMan dumptxoutset()
2688 : {
2689 92 : return RPCHelpMan{
2690 92 : "dumptxoutset",
2691 92 : "Write the serialized UTXO set to disk.",
2692 184 : {
2693 92 : {"path", RPCArg::Type::STR, RPCArg::Optional::NO, "Path to the output file. If relative, will be prefixed by datadir."},
2694 : },
2695 92 : RPCResult{
2696 92 : RPCResult::Type::OBJ, "", "",
2697 644 : {
2698 92 : {RPCResult::Type::NUM, "coins_written", "the number of coins written in the snapshot"},
2699 92 : {RPCResult::Type::STR_HEX, "base_hash", "the hash of the base of the snapshot"},
2700 92 : {RPCResult::Type::NUM, "base_height", "the height of the base of the snapshot"},
2701 92 : {RPCResult::Type::STR, "path", "the absolute path that the snapshot was written to"},
2702 92 : {RPCResult::Type::STR_HEX, "txoutset_hash", "the hash of the UTXO set contents"},
2703 92 : {RPCResult::Type::NUM, "nchaintx", "the number of transactions in the chain up to and including the base block"},
2704 : }
2705 : },
2706 92 : RPCExamples{
2707 92 : HelpExampleCli("dumptxoutset", "utxo.dat")
2708 : },
2709 92 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
2710 : {
2711 0 : const ArgsManager& args{EnsureAnyArgsman(request.context)};
2712 0 : 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 0 : const fs::path temppath = fsbridge::AbsPathJoin(args.GetDataDirNet(), fs::u8path(request.params[0].get_str() + ".incomplete"));
2716 :
2717 0 : if (fs::exists(path)) {
2718 0 : throw JSONRPCError(
2719 : RPC_INVALID_PARAMETER,
2720 0 : path.utf8string() + " already exists. If you are sure this is what you want, "
2721 : "move it out of the way first");
2722 : }
2723 :
2724 0 : FILE* file{fsbridge::fopen(temppath, "wb")};
2725 0 : AutoFile afile{file};
2726 0 : if (afile.IsNull()) {
2727 0 : throw JSONRPCError(
2728 : RPC_INVALID_PARAMETER,
2729 0 : "Couldn't open file " + temppath.utf8string() + " for writing.");
2730 : }
2731 :
2732 0 : NodeContext& node = EnsureAnyNodeContext(request.context);
2733 0 : UniValue result = CreateUTXOSnapshot(
2734 0 : node, node.chainman->ActiveChainstate(), afile, path, temppath);
2735 0 : fs::rename(temppath, path);
2736 :
2737 0 : result.pushKV("path", path.utf8string());
2738 0 : return result;
2739 0 : },
2740 : };
2741 0 : }
2742 :
2743 9 : UniValue CreateUTXOSnapshot(
2744 : NodeContext& node,
2745 : CChainState& chainstate,
2746 : AutoFile& afile,
2747 : const fs::path& path,
2748 : const fs::path& temppath)
2749 : {
2750 9 : std::unique_ptr<CCoinsViewCursor> pcursor;
2751 9 : 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 9 : LOCK(::cs_main);
2768 :
2769 9 : chainstate.ForceFlushStateToDisk();
2770 :
2771 9 : maybe_stats = GetUTXOStats(&chainstate.CoinsDB(), chainstate.m_blockman, CoinStatsHashType::HASH_SERIALIZED, node.rpc_interruption_point);
2772 9 : if (!maybe_stats) {
2773 0 : throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
2774 : }
2775 :
2776 9 : pcursor = chainstate.CoinsDB().Cursor();
2777 9 : tip = CHECK_NONFATAL(chainstate.m_blockman.LookupBlockIndex(maybe_stats->hashBlock));
2778 9 : }
2779 :
2780 9 : 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 9 : SnapshotMetadata metadata{tip->GetBlockHash(), maybe_stats->coins_count, tip->nChainTx};
2785 :
2786 9 : afile << metadata;
2787 :
2788 9 : COutPoint key;
2789 9 : Coin coin;
2790 9 : unsigned int iter{0};
2791 :
2792 1089 : while (pcursor->Valid()) {
2793 1080 : if (iter % 5000 == 0) node.rpc_interruption_point();
2794 1080 : ++iter;
2795 1080 : if (pcursor->GetKey(key) && pcursor->GetValue(coin)) {
2796 1080 : afile << key;
2797 1080 : afile << coin;
2798 1080 : }
2799 :
2800 1080 : pcursor->Next();
2801 : }
2802 :
2803 9 : afile.fclose();
2804 :
2805 9 : UniValue result(UniValue::VOBJ);
2806 9 : result.pushKV("coins_written", maybe_stats->coins_count);
2807 9 : result.pushKV("base_hash", tip->GetBlockHash().ToString());
2808 9 : result.pushKV("base_height", tip->nHeight);
2809 9 : result.pushKV("path", path.utf8string());
2810 9 : 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 9 : result.pushKV("nchaintx", uint64_t{tip->nChainTx});
2814 9 : return result;
2815 9 : }
2816 :
2817 178 : void RegisterBlockchainRPCCommands(CRPCTable& t)
2818 : {
2819 1558 : static const CRPCCommand commands[]{
2820 46 : {"blockchain", &getblockchaininfo},
2821 46 : {"blockchain", &getchaintxstats},
2822 46 : {"blockchain", &getblockstats},
2823 46 : {"blockchain", &getbestblockhash},
2824 46 : {"blockchain", &getbestchainlock},
2825 46 : {"blockchain", &getblockcount},
2826 46 : {"blockchain", &getblock},
2827 46 : {"blockchain", &getblockfrompeer},
2828 46 : {"blockchain", &getblockhashes},
2829 46 : {"blockchain", &getblockhash},
2830 46 : {"blockchain", &getblockheader},
2831 46 : {"blockchain", &getblockheaders},
2832 46 : {"blockchain", &getmerkleblocks},
2833 46 : {"blockchain", &getchaintips},
2834 46 : {"blockchain", &getdifficulty},
2835 46 : {"blockchain", &getspecialtxes},
2836 46 : {"blockchain", &gettxout},
2837 46 : {"blockchain", &gettxoutsetinfo},
2838 46 : {"blockchain", &pruneblockchain},
2839 46 : {"blockchain", &verifychain},
2840 46 : {"blockchain", &preciousblock},
2841 46 : {"blockchain", &scantxoutset},
2842 46 : {"blockchain", &getblockfilter},
2843 46 : {"hidden", &invalidateblock},
2844 46 : {"hidden", &reconsiderblock},
2845 46 : {"hidden", &waitfornewblock},
2846 46 : {"hidden", &waitforblock},
2847 46 : {"hidden", &waitforblockheight},
2848 46 : {"hidden", &syncwithvalidationinterfacequeue},
2849 46 : {"hidden", &dumptxoutset},
2850 : };
2851 5518 : for (const auto& c : commands) {
2852 5340 : t.appendCommand(c.name, &c);
2853 : }
2854 178 : }
|