fuzz coverage

Coverage Report

Created: 2025-10-29 15:27

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