Coverage Report

Created: 2025-06-10 13:21

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