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