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