Coverage Report

Created: 2026-08-05 14:35

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