Coverage Report

Created: 2026-07-08 14:14

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
19.7k
{
108
19.7k
    int nShift = (blockindex.nBits >> 24) & 0xff;
109
19.7k
    double dDiff =
110
19.7k
        (double)0x0000ffff / (double)(blockindex.nBits & 0x00ffffff);
111
112
19.7k
    while (nShift < 29)
113
15
    {
114
15
        dDiff *= 256.0;
115
15
        nShift++;
116
15
    }
117
78.7k
    while (nShift > 29)
118
59.0k
    {
119
59.0k
        dDiff /= 256.0;
120
59.0k
        nShift--;
121
59.0k
    }
122
123
19.7k
    return dDiff;
124
19.7k
}
125
126
static int ComputeNextBlockAndDepth(const CBlockIndex& tip, const CBlockIndex& blockindex, const CBlockIndex*& next)
127
3.98k
{
128
3.98k
    next = tip.GetAncestor(blockindex.nHeight + 1);
129
3.98k
    if (next && next->pprev == &blockindex) {
130
1.62k
        return tip.nHeight - blockindex.nHeight + 1;
131
1.62k
    }
132
2.35k
    next = nullptr;
133
2.35k
    return &blockindex == &tip ? 1 : -1;
134
3.98k
}
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
3.98k
{
166
    // Serialize passed information without accessing chain state of the active chain!
167
3.98k
    AssertLockNotHeld(cs_main); // For performance reasons
168
169
3.98k
    UniValue result(UniValue::VOBJ);
170
3.98k
    result.pushKV("hash", blockindex.GetBlockHash().GetHex());
171
3.98k
    const CBlockIndex* pnext;
172
3.98k
    int confirmations = ComputeNextBlockAndDepth(tip, blockindex, pnext);
173
3.98k
    result.pushKV("confirmations", confirmations);
174
3.98k
    result.pushKV("height", blockindex.nHeight);
175
3.98k
    result.pushKV("version", blockindex.nVersion);
176
3.98k
    result.pushKV("versionHex", strprintf("%08x", blockindex.nVersion));
177
3.98k
    result.pushKV("merkleroot", blockindex.hashMerkleRoot.GetHex());
178
3.98k
    result.pushKV("time", blockindex.nTime);
179
3.98k
    result.pushKV("mediantime", blockindex.GetMedianTimePast());
180
3.98k
    result.pushKV("nonce", blockindex.nNonce);
181
3.98k
    result.pushKV("bits", strprintf("%08x", blockindex.nBits));
182
3.98k
    result.pushKV("target", GetTarget(blockindex, pow_limit).GetHex());
183
3.98k
    result.pushKV("difficulty", GetDifficulty(blockindex));
184
3.98k
    result.pushKV("chainwork", blockindex.nChainWork.GetHex());
185
3.98k
    result.pushKV("nTx", blockindex.nTx);
186
187
3.98k
    if (blockindex.pprev)
188
3.95k
        result.pushKV("previousblockhash", blockindex.pprev->GetBlockHash().GetHex());
189
3.98k
    if (pnext)
190
1.62k
        result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex());
191
3.98k
    return result;
192
3.98k
}
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
5.96k
{
259
5.96k
    return RPCMethod{
260
5.96k
        "getblockcount",
261
5.96k
        "Returns the height of the most-work fully-validated chain.\n"
262
5.96k
                "The genesis block has height 0.\n",
263
5.96k
                {},
264
5.96k
                RPCResult{
265
5.96k
                    RPCResult::Type::NUM, "", "The current block count"},
266
5.96k
                RPCExamples{
267
5.96k
                    HelpExampleCli("getblockcount", "")
268
5.96k
            + HelpExampleRpc("getblockcount", "")
269
5.96k
                },
270
5.96k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
271
5.96k
{
272
3.59k
    ChainstateManager& chainman = EnsureAnyChainman(request.context);
273
3.59k
    LOCK(cs_main);
274
3.59k
    return chainman.ActiveChain().Height();
275
3.59k
},
276
5.96k
    };
277
5.96k
}
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.0k
    ChainstateManager& chainman = EnsureAnyChainman(request.context);
294
12.0k
    LOCK(cs_main);
295
12.0k
    return chainman.ActiveChain().Tip()->GetBlockHash().GetHex();
296
12.0k
},
297
14.4k
    };
298
14.4k
}
299
300
static RPCMethod waitfornewblock()
301
2.37k
{
302
2.37k
    return RPCMethod{
303
2.37k
        "waitfornewblock",
304
2.37k
        "Waits for any new block and returns useful info about it.\n"
305
2.37k
                "\nReturns the current block on timeout or exit.\n"
306
2.37k
                "\nMake sure to use no RPC timeout (bitcoin-cli -rpcclienttimeout=0)",
307
2.37k
                {
308
2.37k
                    {"timeout", RPCArg::Type::NUM, RPCArg::Default{0}, "Time in milliseconds to wait for a response. 0 indicates no timeout."},
309
2.37k
                    {"current_tip", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "Method waits for the chain tip to differ from this."},
310
2.37k
                },
311
2.37k
                RPCResult{
312
2.37k
                    RPCResult::Type::OBJ, "", "",
313
2.37k
                    {
314
2.37k
                        {RPCResult::Type::STR_HEX, "hash", "The blockhash"},
315
2.37k
                        {RPCResult::Type::NUM, "height", "Block height"},
316
2.37k
                    }},
317
2.37k
                RPCExamples{
318
2.37k
                    HelpExampleCli("waitfornewblock", "1000")
319
2.37k
            + HelpExampleRpc("waitfornewblock", "1000")
320
2.37k
                },
321
2.37k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
322
2.37k
{
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.37k
    };
357
2.37k
}
358
359
static RPCMethod waitforblock()
360
2.37k
{
361
2.37k
    return RPCMethod{
362
2.37k
        "waitforblock",
363
2.37k
        "Waits for a specific new block and returns useful info about it.\n"
364
2.37k
                "\nReturns the current block on timeout or exit.\n"
365
2.37k
                "\nMake sure to use no RPC timeout (bitcoin-cli -rpcclienttimeout=0)",
366
2.37k
                {
367
2.37k
                    {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Block hash to wait for."},
368
2.37k
                    {"timeout", RPCArg::Type::NUM, RPCArg::Default{0}, "Time in milliseconds to wait for a response. 0 indicates no timeout."},
369
2.37k
                },
370
2.37k
                RPCResult{
371
2.37k
                    RPCResult::Type::OBJ, "", "",
372
2.37k
                    {
373
2.37k
                        {RPCResult::Type::STR_HEX, "hash", "The blockhash"},
374
2.37k
                        {RPCResult::Type::NUM, "height", "Block height"},
375
2.37k
                    }},
376
2.37k
                RPCExamples{
377
2.37k
                    HelpExampleCli("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\" 1000")
378
2.37k
            + HelpExampleRpc("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\", 1000")
379
2.37k
                },
380
2.37k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
381
2.37k
{
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.37k
    };
418
2.37k
}
419
420
static RPCMethod waitforblockheight()
421
2.38k
{
422
2.38k
    return RPCMethod{
423
2.38k
        "waitforblockheight",
424
2.38k
        "Waits for (at least) block height and returns the height and hash\n"
425
2.38k
                "of the current tip.\n"
426
2.38k
                "\nReturns the current block on timeout or exit.\n"
427
2.38k
                "\nMake sure to use no RPC timeout (bitcoin-cli -rpcclienttimeout=0)",
428
2.38k
                {
429
2.38k
                    {"height", RPCArg::Type::NUM, RPCArg::Optional::NO, "Block height to wait for."},
430
2.38k
                    {"timeout", RPCArg::Type::NUM, RPCArg::Default{0}, "Time in milliseconds to wait for a response. 0 indicates no timeout."},
431
2.38k
                },
432
2.38k
                RPCResult{
433
2.38k
                    RPCResult::Type::OBJ, "", "",
434
2.38k
                    {
435
2.38k
                        {RPCResult::Type::STR_HEX, "hash", "The blockhash"},
436
2.38k
                        {RPCResult::Type::NUM, "height", "Block height"},
437
2.38k
                    }},
438
2.38k
                RPCExamples{
439
2.38k
                    HelpExampleCli("waitforblockheight", "100 1000")
440
2.38k
            + HelpExampleRpc("waitforblockheight", "100, 1000")
441
2.38k
                },
442
2.38k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
443
2.38k
{
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.38k
    };
481
2.38k
}
482
483
static RPCMethod syncwithvalidationinterfacequeue()
484
6.37k
{
485
6.37k
    return RPCMethod{
486
6.37k
        "syncwithvalidationinterfacequeue",
487
6.37k
        "Waits for the validation interface queue to catch up on everything that was there when we entered this function.\n",
488
6.37k
                {},
489
6.37k
                RPCResult{RPCResult::Type::NONE, "", ""},
490
6.37k
                RPCExamples{
491
6.37k
                    HelpExampleCli("syncwithvalidationinterfacequeue","")
492
6.37k
            + HelpExampleRpc("syncwithvalidationinterfacequeue","")
493
6.37k
                },
494
6.37k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
495
6.37k
{
496
4.01k
    NodeContext& node = EnsureAnyNodeContext(request.context);
497
4.01k
    CHECK_NONFATAL(node.validation_signals)->SyncWithValidationInterfaceQueue();
498
4.01k
    return UniValue::VNULL;
499
4.01k
},
500
6.37k
    };
501
6.37k
}
502
503
static RPCMethod getdifficulty()
504
2.36k
{
505
2.36k
    return RPCMethod{
506
2.36k
        "getdifficulty",
507
2.36k
        "Returns the proof-of-work difficulty as a multiple of the minimum difficulty.\n",
508
2.36k
                {},
509
2.36k
                RPCResult{
510
2.36k
                    RPCResult::Type::NUM, "", "the proof-of-work difficulty as a multiple of the minimum difficulty."},
511
2.36k
                RPCExamples{
512
2.36k
                    HelpExampleCli("getdifficulty", "")
513
2.36k
            + HelpExampleRpc("getdifficulty", "")
514
2.36k
                },
515
2.36k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
516
2.36k
{
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.36k
    };
522
2.36k
}
523
524
static RPCMethod getblockfrompeer()
525
2.37k
{
526
2.37k
    return RPCMethod{
527
2.37k
        "getblockfrompeer",
528
2.37k
        "Attempt to fetch block from a given peer.\n\n"
529
2.37k
        "We must have the header for this block, e.g. using submitheader.\n"
530
2.37k
        "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.37k
        "Subsequent calls for the same block may cause the response from the previous peer to be ignored.\n"
532
2.37k
        "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.37k
        "When a peer does not respond with a block, we will disconnect.\n"
534
2.37k
        "Note: The block could be re-pruned as soon as it is received.\n\n"
535
2.37k
        "Returns an empty JSON object if the request was successfully scheduled.",
536
2.37k
        {
537
2.37k
            {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash to try to fetch"},
538
2.37k
            {"peer_id", RPCArg::Type::NUM, RPCArg::Optional::NO, "The peer to fetch it from (see getpeerinfo for peer IDs)"},
539
2.37k
        },
540
2.37k
        RPCResult{RPCResult::Type::OBJ, "", /*optional=*/false, "", {}},
541
2.37k
        RPCExamples{
542
2.37k
            HelpExampleCli("getblockfrompeer", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" 0")
543
2.37k
            + HelpExampleRpc("getblockfrompeer", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" 0")
544
2.37k
        },
545
2.37k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
546
2.37k
{
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.37k
    };
577
2.37k
}
578
579
static RPCMethod getblockhash()
580
7.35k
{
581
7.35k
    return RPCMethod{
582
7.35k
        "getblockhash",
583
7.35k
        "Returns hash of block in best-block-chain at height provided.\n",
584
7.35k
                {
585
7.35k
                    {"height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The height index"},
586
7.35k
                },
587
7.35k
                RPCResult{
588
7.35k
                    RPCResult::Type::STR_HEX, "", "The block hash"},
589
7.35k
                RPCExamples{
590
7.35k
                    HelpExampleCli("getblockhash", "1000")
591
7.35k
            + HelpExampleRpc("getblockhash", "1000")
592
7.35k
                },
593
7.35k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
594
7.35k
{
595
4.98k
    ChainstateManager& chainman = EnsureAnyChainman(request.context);
596
4.98k
    LOCK(cs_main);
597
4.98k
    const CChain& active_chain = chainman.ActiveChain();
598
599
4.98k
    int nHeight = request.params[0].getInt<int>();
600
4.98k
    if (nHeight < 0 || nHeight > active_chain.Height())
601
2
        throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range");
602
603
4.98k
    const CBlockIndex* pblockindex = active_chain[nHeight];
604
4.98k
    return pblockindex->GetBlockHash().GetHex();
605
4.98k
},
606
7.35k
    };
607
7.35k
}
608
609
static RPCMethod getblockheader()
610
5.25k
{
611
5.25k
    return RPCMethod{
612
5.25k
        "getblockheader",
613
5.25k
        "If verbose is false, returns a string that is serialized, hex-encoded data for blockheader 'hash'.\n"
614
5.25k
                "If verbose is true, returns an Object with information about blockheader <hash>.\n",
615
5.25k
                {
616
5.25k
                    {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
617
5.25k
                    {"verbose", RPCArg::Type::BOOL, RPCArg::Default{true}, "true for a json object, false for the hex-encoded data"},
618
5.25k
                },
619
5.25k
                {
620
5.25k
                    RPCResult{"for verbose = true",
621
5.25k
                        RPCResult::Type::OBJ, "", "",
622
5.25k
                        {
623
5.25k
                            {RPCResult::Type::STR_HEX, "hash", "the block hash (same as provided)"},
624
5.25k
                            {RPCResult::Type::NUM, "confirmations", "The number of confirmations, or -1 if the block is not on the main chain"},
625
5.25k
                            {RPCResult::Type::NUM, "height", "The block height or index"},
626
5.25k
                            {RPCResult::Type::NUM, "version", "The block version"},
627
5.25k
                            {RPCResult::Type::STR_HEX, "versionHex", "The block version formatted in hexadecimal"},
628
5.25k
                            {RPCResult::Type::STR_HEX, "merkleroot", "The merkle root"},
629
5.25k
                            {RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME},
630
5.25k
                            {RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME},
631
5.25k
                            {RPCResult::Type::NUM, "nonce", "The nonce"},
632
5.25k
                            {RPCResult::Type::STR_HEX, "bits", "nBits: compact representation of the block difficulty target"},
633
5.25k
                            {RPCResult::Type::STR_HEX, "target", "The difficulty target"},
634
5.25k
                            {RPCResult::Type::NUM, "difficulty", "The difficulty"},
635
5.25k
                            {RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the current chain"},
636
5.25k
                            {RPCResult::Type::NUM, "nTx", "The number of transactions in the block"},
637
5.25k
                            {RPCResult::Type::STR_HEX, "previousblockhash", /*optional=*/true, "The hash of the previous block (if available)"},
638
5.25k
                            {RPCResult::Type::STR_HEX, "nextblockhash", /*optional=*/true, "The hash of the next block (if available)"},
639
5.25k
                        }},
640
5.25k
                    RPCResult{"for verbose=false",
641
5.25k
                        RPCResult::Type::STR_HEX, "", "A string that is serialized, hex-encoded data for block 'hash'"},
642
5.25k
                },
643
5.25k
                RPCExamples{
644
5.25k
                    HelpExampleCli("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
645
5.25k
            + HelpExampleRpc("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
646
5.25k
                },
647
5.25k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
648
5.25k
{
649
2.88k
    uint256 hash(ParseHashV(request.params[0], "hash"));
650
651
2.88k
    bool fVerbose = true;
652
2.88k
    if (!request.params[1].isNull())
653
463
        fVerbose = request.params[1].get_bool();
654
655
2.88k
    const CBlockIndex* pblockindex;
656
2.88k
    const CBlockIndex* tip;
657
2.88k
    ChainstateManager& chainman = EnsureAnyChainman(request.context);
658
2.88k
    {
659
2.88k
        LOCK(cs_main);
660
2.88k
        pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
661
2.88k
        tip = chainman.ActiveChain().Tip();
662
2.88k
    }
663
664
2.88k
    if (!pblockindex) {
665
3
        throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
666
3
    }
667
668
2.88k
    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.44k
    return blockheaderToJSON(*tip, *pblockindex, chainman.GetConsensus().powLimit);
677
2.88k
},
678
5.25k
    };
679
5.25k
}
680
681
void CheckBlockDataAvailability(BlockManager& blockman, const CBlockIndex& blockindex, bool check_for_undo)
682
3.31k
{
683
3.31k
    AssertLockHeld(cs_main);
684
3.31k
    uint32_t flag = check_for_undo ? BLOCK_HAVE_UNDO : BLOCK_HAVE_DATA;
685
3.31k
    if (!(blockindex.nStatus & flag)) {
686
109
        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
104
        if (check_for_undo) {
690
0
            throw JSONRPCError(RPC_MISC_ERROR, "Undo data not available");
691
0
        }
692
104
        throw JSONRPCError(RPC_MISC_ERROR, "Block not available (not fully downloaded)");
693
104
    }
694
3.31k
}
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.05k
{
715
3.05k
    FlatFilePos pos{};
716
3.05k
    {
717
3.05k
        LOCK(cs_main);
718
3.05k
        CheckBlockDataAvailability(blockman, blockindex, /*check_for_undo=*/false);
719
3.05k
        pos = blockindex.GetBlockPos();
720
3.05k
    }
721
722
3.05k
    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
109
    throw JSONRPCError(RPC_MISC_ERROR, "Block not found on disk");
726
3.05k
}
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.3k
{
749
16.3k
    auto fields = std::vector<RPCResult>{
750
16.3k
        {RPCResult::Type::STR_HEX, "hash", "the block hash (same as provided)"},
751
16.3k
        {RPCResult::Type::NUM, "confirmations", "The number of confirmations, or -1 if the block is not on the main chain"},
752
16.3k
        {RPCResult::Type::NUM, "size", "The block size"},
753
16.3k
        {RPCResult::Type::NUM, "strippedsize", "The block size excluding witness data"},
754
16.3k
        {RPCResult::Type::NUM, "weight", "The block weight as defined in BIP 141"},
755
16.3k
        {RPCResult::Type::OBJ, "coinbase_tx", "Coinbase transaction metadata",
756
16.3k
        {
757
16.3k
            {RPCResult::Type::NUM, "version", "The coinbase transaction version"},
758
16.3k
            {RPCResult::Type::NUM, "locktime", "The coinbase transaction's locktime (nLockTime)"},
759
16.3k
            {RPCResult::Type::NUM, "sequence", "The coinbase input's sequence number (nSequence)"},
760
16.3k
            {RPCResult::Type::STR_HEX, "coinbase", "The coinbase input's script"},
761
16.3k
            {RPCResult::Type::STR_HEX, "witness", /*optional=*/true, "The coinbase input's first (and only) witness stack element, if present"},
762
16.3k
        }},
763
16.3k
        {RPCResult::Type::NUM, "height", "The block height or index"},
764
16.3k
        {RPCResult::Type::NUM, "version", "The block version"},
765
16.3k
        {RPCResult::Type::STR_HEX, "versionHex", "The block version formatted in hexadecimal"},
766
16.3k
        {RPCResult::Type::STR_HEX, "merkleroot", "The merkle root"},
767
16.3k
    };
768
16.3k
    fields.push_back(std::move(tx_result));
769
16.3k
    fields.emplace_back(RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME);
770
16.3k
    fields.emplace_back(RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME);
771
16.3k
    fields.emplace_back(RPCResult::Type::NUM, "nonce", "The nonce");
772
16.3k
    fields.emplace_back(RPCResult::Type::STR_HEX, "bits", "nBits: compact representation of the block difficulty target");
773
16.3k
    fields.emplace_back(RPCResult::Type::STR_HEX, "target", "The difficulty target");
774
16.3k
    fields.emplace_back(RPCResult::Type::NUM, "difficulty", "The difficulty");
775
16.3k
    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.3k
    fields.emplace_back(RPCResult::Type::NUM, "nTx", "The number of transactions in the block");
777
16.3k
    fields.emplace_back(RPCResult::Type::STR_HEX, "previousblockhash", /*optional=*/true, "The hash of the previous block (if available)");
778
16.3k
    fields.emplace_back(RPCResult::Type::STR_HEX, "nextblockhash", /*optional=*/true, "The hash of the next block (if available)");
779
16.3k
    if (elision_msg) {
780
        // Elide all block-level fields except the tx array (which differs per verbosity)
781
10.8k
        std::vector<RPCResult> new_fields;
782
10.8k
        new_fields.reserve(fields.size());
783
10.8k
        bool first = true;
784
228k
        for (const auto& f : fields) {
785
228k
            if (f.m_key_name == "tx") {
786
10.8k
                new_fields.push_back(f);
787
10.8k
                continue;
788
10.8k
            }
789
217k
            if (first) {
790
10.8k
                RPCResultOptions eopts = f.m_opts;
791
10.8k
                eopts.print_elision = *elision_msg;
792
10.8k
                new_fields.emplace_back(f, std::move(eopts));
793
10.8k
                first = false;
794
207k
            } else {
795
207k
                RPCResultOptions eopts = f.m_opts;
796
207k
                eopts.print_elision = HelpElisionSkip{};
797
207k
                new_fields.emplace_back(f, std::move(eopts));
798
207k
            }
799
217k
        }
800
10.8k
        fields = std::move(new_fields);
801
10.8k
    }
802
16.3k
    return fields;
803
16.3k
}
804
805
static RPCMethod getblock()
806
5.44k
{
807
5.44k
    return RPCMethod{
808
5.44k
        "getblock",
809
5.44k
        "If verbosity is 0, returns a string that is serialized, hex-encoded data for block 'hash'.\n"
810
5.44k
                "If verbosity is 1, returns an Object with information about block <hash>.\n"
811
5.44k
                "If verbosity is 2, returns an Object with information about block <hash> and information about each transaction.\n"
812
5.44k
                "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.44k
                {
814
5.44k
                    {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
815
5.44k
                    {"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.44k
                     RPCArgOptions{.skip_type_check = true}},
817
5.44k
                },
818
5.44k
                {
819
5.44k
                    RPCResult{"for verbosity = 0", RPCResult::Type::STR_HEX, "", "A string that is serialized, hex-encoded data for block 'hash'"},
820
5.44k
                    RPCResult{"for verbosity = 1", RPCResult::Type::OBJ, "", "",
821
5.44k
                        GetBlockFields({RPCResult::Type::ARR, "tx", "The transaction ids",
822
5.44k
                            {{RPCResult::Type::STR_HEX, "", "The transaction id"}}})},
823
5.44k
                    RPCResult{"for verbosity = 2", RPCResult::Type::OBJ, "", "",
824
5.44k
                        GetBlockFields({RPCResult::Type::ARR, "tx", "",
825
5.44k
                        {
826
5.44k
                            {RPCResult::Type::OBJ, "", "",
827
5.44k
                                TxDoc({.elision_mode = ElisionMode::WithSummary,
828
5.44k
                                       .elision_summary = "The transactions in the format of the getrawtransaction RPC. Different from verbosity = 1 \"tx\" result",
829
5.44k
                                       .fee = true, .hex = true,
830
5.44k
                                       .fee_doc = "The transaction fee in " + CURRENCY_UNIT + ", omitted if block undo data is not available"})},
831
5.44k
                        }}, /*elision_msg=*/"Same output as verbosity = 1")},
832
5.44k
                    RPCResult{"for verbosity = 3", RPCResult::Type::OBJ, "", "",
833
5.44k
                        GetBlockFields({RPCResult::Type::ARR, "tx", "",
834
5.44k
                        {
835
5.44k
                            {RPCResult::Type::OBJ, "", "",
836
5.44k
                                TxDoc({.elision_mode = ElisionMode::Silent,
837
5.44k
                                       .prevout = true,
838
5.44k
                                       .prevout_optional = true,
839
5.44k
                                       .fee = true,
840
5.44k
                                       .hex = true,
841
5.44k
                                       .vin_item_doc = "",
842
5.44k
                                       .prevout_doc = "(Only if undo information is available)",
843
5.44k
                                       .vin_inner_elision = "The same output as verbosity = 2"})},
844
5.44k
                        }}, /*elision_msg=*/"Same output as verbosity = 2")},
845
5.44k
                },
846
5.44k
                RPCExamples{
847
5.44k
                    HelpExampleCli("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
848
5.44k
            + HelpExampleRpc("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
849
5.44k
                },
850
5.44k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
851
5.44k
{
852
3.08k
    uint256 hash(ParseHashV(request.params[0], "blockhash"));
853
854
3.08k
    int verbosity{ParseVerbosity(request.params[1], /*default_verbosity=*/1, /*allow_bool=*/true)};
855
856
3.08k
    const CBlockIndex* pblockindex;
857
3.08k
    const CBlockIndex* tip;
858
3.08k
    ChainstateManager& chainman = EnsureAnyChainman(request.context);
859
3.08k
    {
860
3.08k
        LOCK(cs_main);
861
3.08k
        pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
862
3.08k
        tip = chainman.ActiveChain().Tip();
863
864
3.08k
        if (!pblockindex) {
865
22
            throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
866
22
        }
867
3.08k
    }
868
869
3.05k
    const std::vector<std::byte> block_data{GetRawBlockChecked(chainman.m_blockman, *pblockindex)};
870
871
3.05k
    if (verbosity <= 0) {
872
1.41k
        return HexStr(block_data);
873
1.41k
    }
874
875
1.64k
    CBlock block{};
876
1.64k
    SpanReader{block_data} >> TX_WITH_WITNESS(block);
877
878
1.64k
    TxVerbosity tx_verbosity;
879
1.64k
    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
333
    } else {
884
333
        tx_verbosity = TxVerbosity::SHOW_DETAILS_AND_PREVOUT;
885
333
    }
886
887
1.64k
    return blockToJSON(chainman.m_blockman, block, *tip, *pblockindex, tx_verbosity, chainman.GetConsensus().powLimit);
888
3.05k
},
889
5.44k
    };
890
5.44k
}
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.37k
{
920
2.37k
    return RPCMethod{"pruneblockchain",
921
2.37k
                "Attempts to delete block and undo data up to a specified height or timestamp, if eligible for pruning.\n"
922
2.37k
                "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.37k
                {
924
2.37k
                    {"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.37k
            "                  to prune blocks whose block time is at least 2 hours older than the provided timestamp."},
926
2.37k
                },
927
2.37k
                RPCResult{
928
2.37k
                    RPCResult::Type::NUM, "", "Height of the last block pruned"},
929
2.37k
                RPCExamples{
930
2.37k
                    HelpExampleCli("pruneblockchain", "1000")
931
2.37k
            + HelpExampleRpc("pruneblockchain", "1000")
932
2.37k
                },
933
2.37k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
934
2.37k
{
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.37k
    };
975
2.37k
}
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(CCoinsView* view, node::BlockManager& blockman,
996
                                                       kernel::CoinStatsHashType hash_type,
997
                                                       const std::function<void()>& interruption_point = {},
998
                                                       const CBlockIndex* pindex = nullptr,
999
                                                       bool index_requested = true)
1000
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.43k
{
1022
2.43k
    return RPCMethod{
1023
2.43k
        "gettxoutsetinfo",
1024
2.43k
        "Returns statistics about the unspent transaction output set.\n"
1025
2.43k
                "Note this call may take some time if you are not using coinstatsindex.\n",
1026
2.43k
                {
1027
2.43k
                    {"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.43k
                    {"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.43k
                     RPCArgOptions{
1030
2.43k
                         .skip_type_check = true,
1031
2.43k
                         .type_str = {"", "string or numeric"},
1032
2.43k
                     }},
1033
2.43k
                    {"use_index", RPCArg::Type::BOOL, RPCArg::Default{true}, "Use coinstatsindex, if available."},
1034
2.43k
                },
1035
2.43k
                RPCResult{
1036
2.43k
                    RPCResult::Type::OBJ, "", "",
1037
2.43k
                    {
1038
2.43k
                        {RPCResult::Type::NUM, "height", "The block height (index) of the returned statistics"},
1039
2.43k
                        {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at which these statistics are calculated"},
1040
2.43k
                        {RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs"},
1041
2.43k
                        {RPCResult::Type::NUM, "bogosize", "Database-independent, meaningless metric indicating the UTXO set size"},
1042
2.43k
                        {RPCResult::Type::STR_HEX, "hash_serialized_3", /*optional=*/true, "The serialized hash (only present if 'hash_serialized_3' hash_type is chosen)"},
1043
2.43k
                        {RPCResult::Type::STR_HEX, "muhash", /*optional=*/true, "The serialized hash (only present if 'muhash' hash_type is chosen)"},
1044
2.43k
                        {RPCResult::Type::NUM, "transactions", /*optional=*/true, "The number of transactions with unspent outputs (not available when coinstatsindex is used)"},
1045
2.43k
                        {RPCResult::Type::NUM, "disk_size", /*optional=*/true, "The estimated size of the chainstate on disk (not available when coinstatsindex is used)"},
1046
2.43k
                        {RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount of coins in the UTXO set"},
1047
2.43k
                        {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.43k
                        {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.43k
                        {
1050
2.43k
                            {RPCResult::Type::STR_AMOUNT, "prevout_spent", "Total amount of all prevouts spent in this block"},
1051
2.43k
                            {RPCResult::Type::STR_AMOUNT, "coinbase", "Coinbase subsidy amount of this block"},
1052
2.43k
                            {RPCResult::Type::STR_AMOUNT, "new_outputs_ex_coinbase", "Total amount of new outputs created by this block"},
1053
2.43k
                            {RPCResult::Type::STR_AMOUNT, "unspendable", "Total amount of unspendable outputs created in this block"},
1054
2.43k
                            {RPCResult::Type::OBJ, "unspendables", "Detailed view of the unspendable categories",
1055
2.43k
                            {
1056
2.43k
                                {RPCResult::Type::STR_AMOUNT, "genesis_block", "The unspendable amount of the Genesis block subsidy"},
1057
2.43k
                                {RPCResult::Type::STR_AMOUNT, "bip30", "Transactions overridden by duplicates (no longer possible with BIP30)"},
1058
2.43k
                                {RPCResult::Type::STR_AMOUNT, "scripts", "Amounts sent to scripts that are unspendable (for example OP_RETURN outputs)"},
1059
2.43k
                                {RPCResult::Type::STR_AMOUNT, "unclaimed_rewards", "Fee rewards that miners did not claim in their coinbase transaction"},
1060
2.43k
                            }}
1061
2.43k
                        }},
1062
2.43k
                    }},
1063
2.43k
                RPCExamples{
1064
2.43k
                    HelpExampleCli("gettxoutsetinfo", "") +
1065
2.43k
                    HelpExampleCli("gettxoutsetinfo", R"("none")") +
1066
2.43k
                    HelpExampleCli("gettxoutsetinfo", R"("none" 1000)") +
1067
2.43k
                    HelpExampleCli("gettxoutsetinfo", R"("none" '"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09"')") +
1068
2.43k
                    HelpExampleCli("-named gettxoutsetinfo", R"(hash_type='muhash' use_index='false')") +
1069
2.43k
                    HelpExampleRpc("gettxoutsetinfo", "") +
1070
2.43k
                    HelpExampleRpc("gettxoutsetinfo", R"("none")") +
1071
2.43k
                    HelpExampleRpc("gettxoutsetinfo", R"("none", 1000)") +
1072
2.43k
                    HelpExampleRpc("gettxoutsetinfo", R"("none", "00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09")")
1073
2.43k
                },
1074
2.43k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
1075
2.43k
{
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
    CCoinsView* coins_view;
1087
66
    BlockManager* blockman;
1088
66
    {
1089
66
        LOCK(::cs_main);
1090
66
        coins_view = &active_chainstate.CoinsDB();
1091
66
        blockman = &active_chainstate.m_blockman;
1092
66
    }
1093
1094
66
    const CBlockIndex* pindex{nullptr};
1095
66
    if (!request.params[1].isNull()) {
1096
23
        if (!g_coin_stats_index) {
1097
2
            throw JSONRPCError(RPC_INVALID_PARAMETER, "Querying specific block heights requires coinstatsindex");
1098
2
        }
1099
1100
21
        if (hash_type == CoinStatsHashType::HASH_SERIALIZED) {
1101
4
            throw JSONRPCError(RPC_INVALID_PARAMETER, "hash_serialized_3 hash type cannot be queried for a specific block");
1102
4
        }
1103
1104
17
        if (!index_requested) {
1105
0
            throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot set use_index to false when querying for a specific block");
1106
0
        }
1107
17
        pindex = ParseHashOrHeight(request.params[1], chainman);
1108
17
    }
1109
1110
60
    if (index_requested && g_coin_stats_index) {
1111
34
        if (!g_coin_stats_index->BlockUntilSyncedToCurrentChain()) {
1112
0
            const IndexSummary summary{g_coin_stats_index->GetSummary()};
1113
1114
            // If a specific block was requested and the index has already synced past that height, we can return the
1115
            // data already even though the index is not fully synced yet.
1116
0
            if (pindex && pindex->nHeight > summary.best_block_height) {
1117
0
                throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf("Unable to get data because coinstatsindex is still syncing. Current height: %d", summary.best_block_height));
1118
0
            }
1119
0
        }
1120
34
    }
1121
1122
60
    const std::optional<CCoinsStats> maybe_stats = GetUTXOStats(coins_view, *blockman, hash_type, node.rpc_interruption_point, pindex, index_requested);
1123
60
    if (maybe_stats.has_value()) {
1124
57
        const CCoinsStats& stats = maybe_stats.value();
1125
57
        ret.pushKV("height", stats.nHeight);
1126
57
        ret.pushKV("bestblock", stats.hashBlock.GetHex());
1127
57
        ret.pushKV("txouts", stats.nTransactionOutputs);
1128
57
        ret.pushKV("bogosize", stats.nBogoSize);
1129
57
        if (hash_type == CoinStatsHashType::HASH_SERIALIZED) {
1130
12
            ret.pushKV("hash_serialized_3", stats.hashSerialized.GetHex());
1131
12
        }
1132
57
        if (hash_type == CoinStatsHashType::MUHASH) {
1133
35
            ret.pushKV("muhash", stats.hashSerialized.GetHex());
1134
35
        }
1135
57
        CHECK_NONFATAL(stats.total_amount.has_value());
1136
57
        ret.pushKV("total_amount", ValueFromAmount(stats.total_amount.value()));
1137
57
        if (!stats.index_used) {
1138
23
            ret.pushKV("transactions", stats.nTransactions);
1139
23
            ret.pushKV("disk_size", stats.nDiskSize);
1140
34
        } else {
1141
34
            CCoinsStats prev_stats{};
1142
34
            if (stats.nHeight > 0) {
1143
32
                const CBlockIndex& block_index = *CHECK_NONFATAL(WITH_LOCK(::cs_main, return blockman->LookupBlockIndex(stats.hashBlock)));
1144
32
                const std::optional<CCoinsStats> maybe_prev_stats = GetUTXOStats(coins_view, *blockman, hash_type, node.rpc_interruption_point, block_index.pprev, index_requested);
1145
32
                if (!maybe_prev_stats) {
1146
0
                    throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
1147
0
                }
1148
32
                prev_stats = maybe_prev_stats.value();
1149
32
            }
1150
1151
34
            CAmount block_total_unspendable_amount = stats.total_unspendables_genesis_block +
1152
34
                                                     stats.total_unspendables_bip30 +
1153
34
                                                     stats.total_unspendables_scripts +
1154
34
                                                     stats.total_unspendables_unclaimed_rewards;
1155
34
            CAmount prev_block_total_unspendable_amount = prev_stats.total_unspendables_genesis_block +
1156
34
                                                          prev_stats.total_unspendables_bip30 +
1157
34
                                                          prev_stats.total_unspendables_scripts +
1158
34
                                                          prev_stats.total_unspendables_unclaimed_rewards;
1159
1160
34
            ret.pushKV("total_unspendable_amount", ValueFromAmount(block_total_unspendable_amount));
1161
1162
34
            UniValue block_info(UniValue::VOBJ);
1163
            // These per-block values should fit uint64 under normal circumstances
1164
34
            arith_uint256 diff_prevout = stats.total_prevout_spent_amount - prev_stats.total_prevout_spent_amount;
1165
34
            arith_uint256 diff_coinbase = stats.total_coinbase_amount - prev_stats.total_coinbase_amount;
1166
34
            arith_uint256 diff_outputs = stats.total_new_outputs_ex_coinbase_amount - prev_stats.total_new_outputs_ex_coinbase_amount;
1167
34
            CAmount prevout_amount = static_cast<CAmount>(diff_prevout.GetLow64());
1168
34
            CAmount coinbase_amount = static_cast<CAmount>(diff_coinbase.GetLow64());
1169
34
            CAmount outputs_amount = static_cast<CAmount>(diff_outputs.GetLow64());
1170
34
            block_info.pushKV("prevout_spent", ValueFromAmount(prevout_amount));
1171
34
            block_info.pushKV("coinbase", ValueFromAmount(coinbase_amount));
1172
34
            block_info.pushKV("new_outputs_ex_coinbase", ValueFromAmount(outputs_amount));
1173
34
            block_info.pushKV("unspendable", ValueFromAmount(block_total_unspendable_amount - prev_block_total_unspendable_amount));
1174
1175
34
            UniValue unspendables(UniValue::VOBJ);
1176
34
            unspendables.pushKV("genesis_block", ValueFromAmount(stats.total_unspendables_genesis_block - prev_stats.total_unspendables_genesis_block));
1177
34
            unspendables.pushKV("bip30", ValueFromAmount(stats.total_unspendables_bip30 - prev_stats.total_unspendables_bip30));
1178
34
            unspendables.pushKV("scripts", ValueFromAmount(stats.total_unspendables_scripts - prev_stats.total_unspendables_scripts));
1179
34
            unspendables.pushKV("unclaimed_rewards", ValueFromAmount(stats.total_unspendables_unclaimed_rewards - prev_stats.total_unspendables_unclaimed_rewards));
1180
34
            block_info.pushKV("unspendables", std::move(unspendables));
1181
1182
34
            ret.pushKV("block_info", std::move(block_info));
1183
34
        }
1184
57
    } else {
1185
3
        throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
1186
3
    }
1187
57
    return ret;
1188
60
},
1189
2.43k
    };
1190
2.43k
}
1191
1192
static RPCMethod gettxout()
1193
2.38k
{
1194
2.38k
    return RPCMethod{
1195
2.38k
        "gettxout",
1196
2.38k
        "Returns details about an unspent transaction output.\n",
1197
2.38k
        {
1198
2.38k
            {"txid", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction id"},
1199
2.38k
            {"n", RPCArg::Type::NUM, RPCArg::Optional::NO, "vout number"},
1200
2.38k
            {"include_mempool", RPCArg::Type::BOOL, RPCArg::Default{true}, "Whether to include the mempool. Note that an unspent output that is spent in the mempool won't appear."},
1201
2.38k
        },
1202
2.38k
        {
1203
2.38k
            RPCResult{"If the UTXO was not found", RPCResult::Type::NONE, "", ""},
1204
2.38k
            RPCResult{"Otherwise", RPCResult::Type::OBJ, "", "", {
1205
2.38k
                {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"},
1206
2.38k
                {RPCResult::Type::NUM, "confirmations", "The number of confirmations"},
1207
2.38k
                {RPCResult::Type::STR_AMOUNT, "value", "The transaction value in " + CURRENCY_UNIT},
1208
2.38k
                {RPCResult::Type::OBJ, "scriptPubKey", "", {
1209
2.38k
                    {RPCResult::Type::STR, "asm", "Disassembly of the output script"},
1210
2.38k
                    {RPCResult::Type::STR, "desc", "Inferred descriptor for the output"},
1211
2.38k
                    {RPCResult::Type::STR_HEX, "hex", "The raw output script bytes, hex-encoded"},
1212
2.38k
                    {RPCResult::Type::STR, "type", "The type, eg pubkeyhash"},
1213
2.38k
                    {RPCResult::Type::STR, "address", /*optional=*/true, "The Bitcoin address (only if a well-defined address exists)"},
1214
2.38k
                }},
1215
2.38k
                {RPCResult::Type::BOOL, "coinbase", "Coinbase or not"},
1216
2.38k
            }},
1217
2.38k
        },
1218
2.38k
        RPCExamples{
1219
2.38k
            "\nGet unspent transactions\n"
1220
2.38k
            + HelpExampleCli("listunspent", "") +
1221
2.38k
            "\nView the details\n"
1222
2.38k
            + HelpExampleCli("gettxout", "\"txid\" 1") +
1223
2.38k
            "\nAs a JSON-RPC call\n"
1224
2.38k
            + HelpExampleRpc("gettxout", "\"txid\", 1")
1225
2.38k
                },
1226
2.38k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
1227
2.38k
{
1228
18
    NodeContext& node = EnsureAnyNodeContext(request.context);
1229
18
    ChainstateManager& chainman = EnsureChainman(node);
1230
18
    LOCK(cs_main);
1231
1232
18
    UniValue ret(UniValue::VOBJ);
1233
1234
18
    auto hash{Txid::FromUint256(ParseHashV(request.params[0], "txid"))};
1235
18
    COutPoint out{hash, request.params[1].getInt<uint32_t>()};
1236
18
    bool fMempool = true;
1237
18
    if (!request.params[2].isNull())
1238
7
        fMempool = request.params[2].get_bool();
1239
1240
18
    Chainstate& active_chainstate = chainman.ActiveChainstate();
1241
18
    CCoinsViewCache* coins_view = &active_chainstate.CoinsTip();
1242
1243
18
    std::optional<Coin> coin;
1244
18
    if (fMempool) {
1245
15
        const CTxMemPool& mempool = EnsureMemPool(node);
1246
15
        LOCK(mempool.cs);
1247
15
        CCoinsViewMemPool view(coins_view, mempool);
1248
15
        if (!mempool.isSpent(out)) coin = view.GetCoin(out);
1249
15
    } else {
1250
3
        coin = coins_view->GetCoin(out);
1251
3
    }
1252
18
    if (!coin) return UniValue::VNULL;
1253
1254
14
    const CBlockIndex* pindex = active_chainstate.m_blockman.LookupBlockIndex(coins_view->GetBestBlock());
1255
14
    ret.pushKV("bestblock", pindex->GetBlockHash().GetHex());
1256
14
    if (coin->nHeight == MEMPOOL_HEIGHT) {
1257
2
        ret.pushKV("confirmations", 0);
1258
12
    } else {
1259
12
        ret.pushKV("confirmations", pindex->nHeight - coin->nHeight + 1);
1260
12
    }
1261
14
    ret.pushKV("value", ValueFromAmount(coin->out.nValue));
1262
14
    UniValue o(UniValue::VOBJ);
1263
14
    ScriptToUniv(coin->out.scriptPubKey, /*out=*/o, /*include_hex=*/true, /*include_address=*/true);
1264
14
    ret.pushKV("scriptPubKey", std::move(o));
1265
14
    ret.pushKV("coinbase", coin->IsCoinBase());
1266
1267
14
    return ret;
1268
18
},
1269
2.38k
    };
1270
2.38k
}
1271
1272
static RPCMethod verifychain()
1273
2.37k
{
1274
2.37k
    return RPCMethod{
1275
2.37k
        "verifychain",
1276
2.37k
        "Verifies blockchain database.\n",
1277
2.37k
                {
1278
2.37k
                    {"checklevel", RPCArg::Type::NUM, RPCArg::DefaultHint{strprintf("%d, range=0-4", DEFAULT_CHECKLEVEL)},
1279
2.37k
                        strprintf("How thorough the block verification is:\n%s", MakeUnorderedList(CHECKLEVEL_DOC))},
1280
2.37k
                    {"nblocks", RPCArg::Type::NUM, RPCArg::DefaultHint{strprintf("%d, 0=all", DEFAULT_CHECKBLOCKS)}, "The number of blocks to check."},
1281
2.37k
                },
1282
2.37k
                RPCResult{
1283
2.37k
                    RPCResult::Type::BOOL, "", "Verification finished successfully. If false, check debug log for reason."},
1284
2.37k
                RPCExamples{
1285
2.37k
                    HelpExampleCli("verifychain", "")
1286
2.37k
            + HelpExampleRpc("verifychain", "")
1287
2.37k
                },
1288
2.37k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
1289
2.37k
{
1290
3
    const int check_level{request.params[0].isNull() ? DEFAULT_CHECKLEVEL : request.params[0].getInt<int>()};
1291
3
    const int check_depth{request.params[1].isNull() ? DEFAULT_CHECKBLOCKS : request.params[1].getInt<int>()};
1292
1293
3
    ChainstateManager& chainman = EnsureAnyChainman(request.context);
1294
3
    LOCK(cs_main);
1295
1296
3
    Chainstate& active_chainstate = chainman.ActiveChainstate();
1297
3
    return CVerifyDB(chainman.GetNotifications()).VerifyDB(
1298
3
               active_chainstate, chainman.GetParams().GetConsensus(), active_chainstate.CoinsTip(), check_level, check_depth) == VerifyDBResult::SUCCESS;
1299
3
},
1300
2.37k
    };
1301
2.37k
}
1302
1303
static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softforks, const ChainstateManager& chainman, Consensus::BuriedDeployment dep)
1304
455
{
1305
    // For buried deployments.
1306
1307
455
    if (!DeploymentEnabled(chainman, dep)) return;
1308
1309
455
    UniValue rv(UniValue::VOBJ);
1310
455
    rv.pushKV("type", "buried");
1311
    // getdeploymentinfo reports the softfork as active from when the chain height is
1312
    // one below the activation height
1313
455
    rv.pushKV("active", DeploymentActiveAfter(blockindex, chainman, dep));
1314
455
    rv.pushKV("height", chainman.GetConsensus().DeploymentHeight(dep));
1315
455
    softforks.pushKV(DeploymentName(dep), std::move(rv));
1316
455
}
1317
1318
static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softforks, const ChainstateManager& chainman, Consensus::DeploymentPos id)
1319
91
{
1320
    // For BIP9 deployments.
1321
91
    if (!DeploymentEnabled(chainman, id)) return;
1322
91
    if (blockindex == nullptr) return;
1323
1324
91
    UniValue bip9(UniValue::VOBJ);
1325
91
    BIP9Info info{chainman.m_versionbitscache.Info(*blockindex, chainman.GetConsensus(), id)};
1326
91
    const auto& depparams{chainman.GetConsensus().vDeployments[id]};
1327
1328
    // BIP9 parameters
1329
91
    if (info.stats.has_value()) {
1330
34
        bip9.pushKV("bit", depparams.bit);
1331
34
    }
1332
91
    bip9.pushKV("start_time", depparams.nStartTime);
1333
91
    bip9.pushKV("timeout", depparams.nTimeout);
1334
91
    bip9.pushKV("min_activation_height", depparams.min_activation_height);
1335
1336
    // BIP9 status
1337
91
    bip9.pushKV("status", info.current_state);
1338
91
    bip9.pushKV("since", info.since);
1339
91
    bip9.pushKV("status_next", info.next_state);
1340
1341
    // BIP9 signalling status, if applicable
1342
91
    if (info.stats.has_value()) {
1343
34
        UniValue statsUV(UniValue::VOBJ);
1344
34
        statsUV.pushKV("period", info.stats->period);
1345
34
        statsUV.pushKV("elapsed", info.stats->elapsed);
1346
34
        statsUV.pushKV("count", info.stats->count);
1347
34
        if (info.stats->threshold > 0 || info.stats->possible) {
1348
32
            statsUV.pushKV("threshold", info.stats->threshold);
1349
32
            statsUV.pushKV("possible", info.stats->possible);
1350
32
        }
1351
34
        bip9.pushKV("statistics", std::move(statsUV));
1352
1353
34
        std::string sig;
1354
34
        sig.reserve(info.signalling_blocks.size());
1355
3.51k
        for (const bool s : info.signalling_blocks) {
1356
3.51k
            sig.push_back(s ? '#' : '-');
1357
3.51k
        }
1358
34
        bip9.pushKV("signalling", sig);
1359
34
    }
1360
1361
91
    UniValue rv(UniValue::VOBJ);
1362
91
    rv.pushKV("type", "bip9");
1363
91
    bool is_active = false;
1364
91
    if (info.active_since.has_value()) {
1365
1
        rv.pushKV("height", *info.active_since);
1366
1
        is_active = (*info.active_since <= blockindex->nHeight + 1);
1367
1
    }
1368
91
    rv.pushKV("active", is_active);
1369
91
    rv.pushKV("bip9", bip9);
1370
91
    softforks.pushKV(DeploymentName(id), std::move(rv));
1371
91
}
1372
1373
// used by rest.cpp:rest_chaininfo, so cannot be static
1374
RPCMethod getblockchaininfo()
1375
17.8k
{
1376
17.8k
    return RPCMethod{"getblockchaininfo",
1377
17.8k
        "Returns an object containing various state info regarding blockchain processing.\n",
1378
17.8k
        {},
1379
17.8k
        RPCResult{
1380
17.8k
            RPCResult::Type::OBJ, "", "",
1381
17.8k
            {
1382
17.8k
                {RPCResult::Type::STR, "chain", "current network name (" LIST_CHAIN_NAMES ")"},
1383
17.8k
                {RPCResult::Type::NUM, "blocks", "the height of the most-work fully-validated chain. The genesis block has height 0"},
1384
17.8k
                {RPCResult::Type::NUM, "headers", "the current number of headers we have validated"},
1385
17.8k
                {RPCResult::Type::STR, "bestblockhash", "the hash of the currently best block"},
1386
17.8k
                {RPCResult::Type::STR_HEX, "bits", "nBits: compact representation of the block difficulty target"},
1387
17.8k
                {RPCResult::Type::STR_HEX, "target", "the difficulty target"},
1388
17.8k
                {RPCResult::Type::NUM, "difficulty", "the current difficulty"},
1389
17.8k
                {RPCResult::Type::NUM_TIME, "time", "the block time expressed in " + UNIX_EPOCH_TIME},
1390
17.8k
                {RPCResult::Type::NUM_TIME, "mediantime", "the median block time expressed in " + UNIX_EPOCH_TIME},
1391
17.8k
                {RPCResult::Type::NUM, "verificationprogress", "estimate of verification progress [0..1]"},
1392
17.8k
                {RPCResult::Type::BOOL, "initialblockdownload", "(debug information) estimate of whether this node is in Initial Block Download mode"},
1393
17.8k
                {RPCResult::Type::OBJ, "backgroundvalidation", /*optional=*/true, "state info regarding background validation process",
1394
17.8k
                {
1395
17.8k
                    {RPCResult::Type::NUM, "snapshotheight", "the height of the snapshot block. Background validation verifies the chain from genesis up to this height"},
1396
17.8k
                    {RPCResult::Type::NUM, "blocks", "the height of the most-work background fully-validated chain. The genesis block has height 0"},
1397
17.8k
                    {RPCResult::Type::STR, "bestblockhash", "the hash of the currently best block validated in the background"},
1398
17.8k
                    {RPCResult::Type::NUM_TIME, "mediantime", "the median block time expressed in " + UNIX_EPOCH_TIME},
1399
17.8k
                    {RPCResult::Type::NUM, "verificationprogress", "estimate of background verification progress [0..1]"},
1400
17.8k
                    {RPCResult::Type::STR_HEX, "chainwork", "total amount of work in background validated chain, in hexadecimal"},
1401
17.8k
                }},
1402
17.8k
                {RPCResult::Type::STR_HEX, "chainwork", "total amount of work in active chain, in hexadecimal"},
1403
17.8k
                {RPCResult::Type::NUM, "size_on_disk", "the estimated size of the block and undo files on disk"},
1404
17.8k
                {RPCResult::Type::BOOL, "pruned", "if the blocks are subject to pruning"},
1405
17.8k
                {RPCResult::Type::NUM, "pruneheight", /*optional=*/true, "the first block unpruned, all previous blocks were pruned (only present if pruning is enabled)"},
1406
17.8k
                {RPCResult::Type::BOOL, "automatic_pruning", /*optional=*/true, "whether automatic pruning is enabled (only present if pruning is enabled)"},
1407
17.8k
                {RPCResult::Type::NUM, "prune_target_size", /*optional=*/true, "the target size used by pruning (only present if automatic pruning is enabled)"},
1408
17.8k
                {RPCResult::Type::STR_HEX, "signet_challenge", /*optional=*/true, "the block challenge (aka. block script), in hexadecimal (only present if the current network is a signet)"},
1409
17.8k
                (IsDeprecatedRPCEnabled("warnings") ?
1410
0
                    RPCResult{RPCResult::Type::STR, "warnings", "any network and blockchain warnings (DEPRECATED)"} :
1411
17.8k
                    RPCResult{RPCResult::Type::ARR, "warnings", "any network and blockchain warnings (run with `-deprecatedrpc=warnings` to return the latest warning as a single string)",
1412
17.8k
                    {
1413
17.8k
                        {RPCResult::Type::STR, "", "warning"},
1414
17.8k
                    }
1415
17.8k
                    }
1416
17.8k
                ),
1417
17.8k
            }},
1418
17.8k
        RPCExamples{
1419
17.8k
            HelpExampleCli("getblockchaininfo", "")
1420
17.8k
            + HelpExampleRpc("getblockchaininfo", "")
1421
17.8k
        },
1422
17.8k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
1423
17.8k
{
1424
15.4k
    ChainstateManager& chainman = EnsureAnyChainman(request.context);
1425
15.4k
    LOCK(cs_main);
1426
15.4k
    Chainstate& active_chainstate = chainman.ActiveChainstate();
1427
1428
15.4k
    const CBlockIndex& tip{*CHECK_NONFATAL(active_chainstate.m_chain.Tip())};
1429
15.4k
    const int height{tip.nHeight};
1430
15.4k
    UniValue obj(UniValue::VOBJ);
1431
15.4k
    obj.pushKV("chain", chainman.GetParams().GetChainTypeString());
1432
15.4k
    obj.pushKV("blocks", height);
1433
15.4k
    obj.pushKV("headers", chainman.m_best_header ? chainman.m_best_header->nHeight : -1);
1434
15.4k
    obj.pushKV("bestblockhash", tip.GetBlockHash().GetHex());
1435
15.4k
    obj.pushKV("bits", strprintf("%08x", tip.nBits));
1436
15.4k
    obj.pushKV("target", GetTarget(tip, chainman.GetConsensus().powLimit).GetHex());
1437
15.4k
    obj.pushKV("difficulty", GetDifficulty(tip));
1438
15.4k
    obj.pushKV("time", tip.GetBlockTime());
1439
15.4k
    obj.pushKV("mediantime", tip.GetMedianTimePast());
1440
15.4k
    obj.pushKV("verificationprogress", chainman.GuessVerificationProgress(&tip));
1441
15.4k
    obj.pushKV("initialblockdownload", chainman.IsInitialBlockDownload());
1442
15.4k
    auto historical_blocks{chainman.GetHistoricalBlockRange()};
1443
15.4k
    if (historical_blocks) {
1444
5
        UniValue background_validation(UniValue::VOBJ);
1445
5
        const CBlockIndex& btip{*CHECK_NONFATAL(historical_blocks->first)};
1446
5
        const CBlockIndex& btarget{*CHECK_NONFATAL(historical_blocks->second)};
1447
5
        background_validation.pushKV("snapshotheight", btarget.nHeight);
1448
5
        background_validation.pushKV("blocks", btip.nHeight);
1449
5
        background_validation.pushKV("bestblockhash", btip.GetBlockHash().GetHex());
1450
5
        background_validation.pushKV("mediantime", btip.GetMedianTimePast());
1451
5
        background_validation.pushKV("chainwork", btip.nChainWork.GetHex());
1452
5
        background_validation.pushKV("verificationprogress", chainman.GetBackgroundVerificationProgress(btip));
1453
5
        obj.pushKV("backgroundvalidation", std::move(background_validation));
1454
5
    }
1455
15.4k
    obj.pushKV("chainwork", tip.nChainWork.GetHex());
1456
15.4k
    obj.pushKV("size_on_disk", chainman.m_blockman.CalculateCurrentUsage());
1457
15.4k
    obj.pushKV("pruned", chainman.m_blockman.IsPruneMode());
1458
15.4k
    if (chainman.m_blockman.IsPruneMode()) {
1459
25
        const auto prune_height{GetPruneHeight(chainman.m_blockman, active_chainstate.m_chain)};
1460
25
        obj.pushKV("pruneheight", prune_height ? prune_height.value() + 1 : 0);
1461
1462
25
        const bool automatic_pruning{chainman.m_blockman.GetPruneTarget() != BlockManager::PRUNE_TARGET_MANUAL};
1463
25
        obj.pushKV("automatic_pruning",  automatic_pruning);
1464
25
        if (automatic_pruning) {
1465
9
            obj.pushKV("prune_target_size", chainman.m_blockman.GetPruneTarget());
1466
9
        }
1467
25
    }
1468
15.4k
    if (chainman.GetParams().GetChainType() == ChainType::SIGNET) {
1469
7
        const std::vector<uint8_t>& signet_challenge =
1470
7
            chainman.GetParams().GetConsensus().signet_challenge;
1471
7
        obj.pushKV("signet_challenge", HexStr(signet_challenge));
1472
7
    }
1473
1474
15.4k
    NodeContext& node = EnsureAnyNodeContext(request.context);
1475
15.4k
    obj.pushKV("warnings", node::GetWarningsForRpc(*CHECK_NONFATAL(node.warnings), IsDeprecatedRPCEnabled("warnings")));
1476
15.4k
    return obj;
1477
15.4k
},
1478
17.8k
    };
1479
17.8k
}
1480
1481
namespace {
1482
const std::vector<RPCResult> RPCHelpForDeployment{
1483
    {RPCResult::Type::STR, "type", "one of \"buried\", \"bip9\""},
1484
    {RPCResult::Type::NUM, "height", /*optional=*/true, "height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)"},
1485
    {RPCResult::Type::BOOL, "active", "true if the rules are enforced for the mempool and the next block"},
1486
    {RPCResult::Type::OBJ, "bip9", /*optional=*/true, "status of bip9 softforks (only for \"bip9\" type)",
1487
    {
1488
        {RPCResult::Type::NUM, "bit", /*optional=*/true, "the bit (0-28) in the block version field used to signal this softfork (only for \"started\" and \"locked_in\" status)"},
1489
        {RPCResult::Type::NUM_TIME, "start_time", "the minimum median time past of a block at which the bit gains its meaning"},
1490
        {RPCResult::Type::NUM_TIME, "timeout", "the median time past of a block at which the deployment is considered failed if not yet locked in"},
1491
        {RPCResult::Type::NUM, "min_activation_height", "minimum height of blocks for which the rules may be enforced"},
1492
        {RPCResult::Type::STR, "status", "status of deployment at specified block (one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\")"},
1493
        {RPCResult::Type::NUM, "since", "height of the first block to which the status applies"},
1494
        {RPCResult::Type::STR, "status_next", "status of deployment at the next block"},
1495
        {RPCResult::Type::OBJ, "statistics", /*optional=*/true, "numeric statistics about signalling for a softfork (only for \"started\" and \"locked_in\" status)",
1496
        {
1497
            {RPCResult::Type::NUM, "period", "the length in blocks of the signalling period"},
1498
            {RPCResult::Type::NUM, "threshold", /*optional=*/true, "the number of blocks with the version bit set required to activate the feature (only for \"started\" status)"},
1499
            {RPCResult::Type::NUM, "elapsed", "the number of blocks elapsed since the beginning of the current period"},
1500
            {RPCResult::Type::NUM, "count", "the number of blocks with the version bit set in the current period"},
1501
            {RPCResult::Type::BOOL, "possible", /*optional=*/true, "returns false if there are not enough blocks left in this period to pass activation threshold (only for \"started\" status)"},
1502
        }},
1503
        {RPCResult::Type::STR, "signalling", /*optional=*/true, "indicates blocks that signalled with a # and blocks that did not with a -"},
1504
    }},
1505
};
1506
1507
UniValue DeploymentInfo(const CBlockIndex* blockindex, const ChainstateManager& chainman)
1508
91
{
1509
91
    UniValue softforks(UniValue::VOBJ);
1510
91
    SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_HEIGHTINCB);
1511
91
    SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_DERSIG);
1512
91
    SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_CLTV);
1513
91
    SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_CSV);
1514
91
    SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_SEGWIT);
1515
91
    SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_TESTDUMMY);
1516
91
    return softforks;
1517
91
}
1518
} // anon namespace
1519
1520
RPCMethod getdeploymentinfo()
1521
2.45k
{
1522
2.45k
    return RPCMethod{"getdeploymentinfo",
1523
2.45k
        "Returns an object containing various state info regarding deployments of consensus changes.\n"
1524
2.45k
        "Consensus changes for which the new rules are enforced from genesis are not listed in \"deployments\".",
1525
2.45k
        {
1526
2.45k
            {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Default{"hash of current chain tip"}, "The block hash at which to query deployment state"},
1527
2.45k
        },
1528
2.45k
        RPCResult{
1529
2.45k
            RPCResult::Type::OBJ, "", "", {
1530
2.45k
                {RPCResult::Type::STR, "hash", "requested block hash (or tip)"},
1531
2.45k
                {RPCResult::Type::NUM, "height", "requested block height (or tip)"},
1532
2.45k
                {RPCResult::Type::ARR, "script_flags", "script verify flags for the block", {
1533
2.45k
                    {RPCResult::Type::STR, "flag", "a script verify flag"},
1534
2.45k
                }},
1535
2.45k
                {RPCResult::Type::OBJ_DYN, "deployments", "", {
1536
2.45k
                    {RPCResult::Type::OBJ, "xxxx", "name of the deployment", RPCHelpForDeployment}
1537
2.45k
                }},
1538
2.45k
            }
1539
2.45k
        },
1540
2.45k
        RPCExamples{ HelpExampleCli("getdeploymentinfo", "") + HelpExampleRpc("getdeploymentinfo", "") },
1541
2.45k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
1542
2.45k
        {
1543
91
            const ChainstateManager& chainman = EnsureAnyChainman(request.context);
1544
91
            LOCK(cs_main);
1545
91
            const Chainstate& active_chainstate = chainman.ActiveChainstate();
1546
1547
91
            const CBlockIndex* blockindex;
1548
91
            if (request.params[0].isNull()) {
1549
87
                blockindex = CHECK_NONFATAL(active_chainstate.m_chain.Tip());
1550
87
            } else {
1551
4
                const uint256 hash(ParseHashV(request.params[0], "blockhash"));
1552
4
                blockindex = chainman.m_blockman.LookupBlockIndex(hash);
1553
4
                if (!blockindex) {
1554
0
                    throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
1555
0
                }
1556
4
            }
1557
1558
91
            UniValue deploymentinfo(UniValue::VOBJ);
1559
91
            deploymentinfo.pushKV("hash", blockindex->GetBlockHash().ToString());
1560
91
            deploymentinfo.pushKV("height", blockindex->nHeight);
1561
91
            {
1562
91
                const auto flagnames = GetScriptFlagNames(GetBlockScriptFlags(*blockindex, chainman));
1563
91
                UniValue uv_flagnames(UniValue::VARR);
1564
91
                uv_flagnames.push_backV(flagnames.begin(), flagnames.end());
1565
91
                deploymentinfo.pushKV("script_flags", uv_flagnames);
1566
91
            }
1567
91
            deploymentinfo.pushKV("deployments", DeploymentInfo(blockindex, chainman));
1568
91
            return deploymentinfo;
1569
91
        },
1570
2.45k
    };
1571
2.45k
}
1572
1573
/** Comparison function for sorting the getchaintips heads.  */
1574
struct CompareBlocksByHeight
1575
{
1576
    bool operator()(const CBlockIndex* a, const CBlockIndex* b) const
1577
111
    {
1578
        /* Make sure that unequal blocks with the same height do not compare
1579
           equal. Use the pointers themselves to make a distinction. */
1580
1581
111
        if (a->nHeight != b->nHeight)
1582
95
          return (a->nHeight > b->nHeight);
1583
1584
16
        return a < b;
1585
111
    }
1586
};
1587
1588
static RPCMethod getchaintips()
1589
2.42k
{
1590
2.42k
    return RPCMethod{"getchaintips",
1591
2.42k
                "Return information about all known tips in the block tree,"
1592
2.42k
                " including the main chain as well as orphaned branches.\n",
1593
2.42k
                {},
1594
2.42k
                RPCResult{
1595
2.42k
                    RPCResult::Type::ARR, "", "",
1596
2.42k
                    {{RPCResult::Type::OBJ, "", "",
1597
2.42k
                        {
1598
2.42k
                            {RPCResult::Type::NUM, "height", "height of the chain tip"},
1599
2.42k
                            {RPCResult::Type::STR_HEX, "hash", "block hash of the tip"},
1600
2.42k
                            {RPCResult::Type::NUM, "branchlen", "zero for main chain, otherwise length of branch connecting the tip to the main chain"},
1601
2.42k
                            {RPCResult::Type::STR, "status", "status of the chain, \"active\" for the main chain\n"
1602
2.42k
            "Possible values for status:\n"
1603
2.42k
            "1.  \"invalid\"               This branch contains at least one invalid block\n"
1604
2.42k
            "2.  \"headers-only\"          Not all blocks for this branch are available, but the headers are valid\n"
1605
2.42k
            "3.  \"valid-headers\"         All blocks are available for this branch, but they were never fully validated\n"
1606
2.42k
            "4.  \"valid-fork\"            This branch is not part of the active chain, but is fully validated\n"
1607
2.42k
            "5.  \"active\"                This is the tip of the active main chain, which is certainly valid"},
1608
2.42k
                        }}}},
1609
2.42k
                RPCExamples{
1610
2.42k
                    HelpExampleCli("getchaintips", "")
1611
2.42k
            + HelpExampleRpc("getchaintips", "")
1612
2.42k
                },
1613
2.42k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
1614
2.42k
{
1615
57
    ChainstateManager& chainman = EnsureAnyChainman(request.context);
1616
57
    LOCK(cs_main);
1617
57
    CChain& active_chain = chainman.ActiveChain();
1618
1619
    /*
1620
     * Idea: The set of chain tips is the active chain tip, plus orphan blocks which do not have another orphan building off of them.
1621
     * Algorithm:
1622
     *  - Make one pass through BlockIndex(), picking out the orphan blocks, and also storing a set of the orphan block's pprev pointers.
1623
     *  - Iterate through the orphan blocks. If the block isn't pointed to by another orphan, it is a chain tip.
1624
     *  - Add the active chain tip
1625
     */
1626
57
    std::set<const CBlockIndex*, CompareBlocksByHeight> setTips;
1627
57
    std::set<const CBlockIndex*> setOrphans;
1628
57
    std::set<const CBlockIndex*> setPrevs;
1629
1630
9.28k
    for (const auto& [_, block_index] : chainman.BlockIndex()) {
1631
9.28k
        if (!active_chain.Contains(block_index)) {
1632
3.59k
            setOrphans.insert(&block_index);
1633
3.59k
            setPrevs.insert(block_index.pprev);
1634
3.59k
        }
1635
9.28k
    }
1636
1637
3.65k
    for (std::set<const CBlockIndex*>::iterator it = setOrphans.begin(); it != setOrphans.end(); ++it) {
1638
3.59k
        if (setPrevs.erase(*it) == 0) {
1639
37
            setTips.insert(*it);
1640
37
        }
1641
3.59k
    }
1642
1643
    // Always report the currently active tip.
1644
57
    setTips.insert(active_chain.Tip());
1645
1646
    /* Construct the output array.  */
1647
57
    UniValue res(UniValue::VARR);
1648
94
    for (const CBlockIndex* block : setTips) {
1649
94
        CHECK_NONFATAL(block);
1650
94
        UniValue obj(UniValue::VOBJ);
1651
94
        obj.pushKV("height", block->nHeight);
1652
94
        obj.pushKV("hash", block->phashBlock->GetHex());
1653
1654
94
        const int branchLen = block->nHeight - active_chain.FindFork(*block)->nHeight;
1655
94
        obj.pushKV("branchlen", branchLen);
1656
1657
94
        std::string status;
1658
94
        if (active_chain.Contains(*block)) {
1659
            // This block is part of the currently active chain.
1660
57
            status = "active";
1661
57
        } else if (block->nStatus & BLOCK_FAILED_VALID) {
1662
            // This block or one of its ancestors is invalid.
1663
4
            status = "invalid";
1664
33
        } else if (!block->HaveNumChainTxs()) {
1665
            // This block cannot be connected because full block data for it or one of its parents is missing.
1666
29
            status = "headers-only";
1667
29
        } else if (block->IsValid(BLOCK_VALID_SCRIPTS)) {
1668
            // This block is fully validated, but no longer part of the active chain. It was probably the active block once, but was reorganized.
1669
4
            status = "valid-fork";
1670
4
        } else if (block->IsValid(BLOCK_VALID_TREE)) {
1671
            // The headers for this block are valid, but it has not been validated. It was probably never part of the most-work chain.
1672
0
            status = "valid-headers";
1673
0
        } else {
1674
            // No clue.
1675
0
            status = "unknown";
1676
0
        }
1677
94
        obj.pushKV("status", status);
1678
1679
94
        res.push_back(std::move(obj));
1680
94
    }
1681
1682
57
    return res;
1683
57
},
1684
2.42k
    };
1685
2.42k
}
1686
1687
static RPCMethod preciousblock()
1688
2.37k
{
1689
2.37k
    return RPCMethod{
1690
2.37k
        "preciousblock",
1691
2.37k
        "Treats a block as if it were received before others with the same work.\n"
1692
2.37k
                "\nA later preciousblock call can override the effect of an earlier one.\n"
1693
2.37k
                "\nThe effects of preciousblock are not retained across restarts.\n",
1694
2.37k
                {
1695
2.37k
                    {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hash of the block to mark as precious"},
1696
2.37k
                },
1697
2.37k
                RPCResult{RPCResult::Type::NONE, "", ""},
1698
2.37k
                RPCExamples{
1699
2.37k
                    HelpExampleCli("preciousblock", "\"blockhash\"")
1700
2.37k
            + HelpExampleRpc("preciousblock", "\"blockhash\"")
1701
2.37k
                },
1702
2.37k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
1703
2.37k
{
1704
10
    uint256 hash(ParseHashV(request.params[0], "blockhash"));
1705
10
    CBlockIndex* pblockindex;
1706
1707
10
    ChainstateManager& chainman = EnsureAnyChainman(request.context);
1708
10
    {
1709
10
        LOCK(cs_main);
1710
10
        pblockindex = chainman.m_blockman.LookupBlockIndex(hash);
1711
10
        if (!pblockindex) {
1712
0
            throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
1713
0
        }
1714
10
    }
1715
1716
10
    BlockValidationState state;
1717
10
    chainman.ActiveChainstate().PreciousBlock(state, pblockindex);
1718
1719
10
    if (!state.IsValid()) {
1720
0
        throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString());
1721
0
    }
1722
1723
10
    return UniValue::VNULL;
1724
10
},
1725
2.37k
    };
1726
2.37k
}
1727
1728
162
void InvalidateBlock(ChainstateManager& chainman, const uint256 block_hash) {
1729
162
    BlockValidationState state;
1730
162
    CBlockIndex* pblockindex;
1731
162
    {
1732
162
        LOCK(chainman.GetMutex());
1733
162
        pblockindex = chainman.m_blockman.LookupBlockIndex(block_hash);
1734
162
        if (!pblockindex) {
1735
1
            throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
1736
1
        }
1737
162
    }
1738
161
    chainman.ActiveChainstate().InvalidateBlock(state, pblockindex);
1739
1740
161
    if (state.IsValid()) {
1741
161
        chainman.ActiveChainstate().ActivateBestChain(state);
1742
161
    }
1743
1744
161
    if (!state.IsValid()) {
1745
0
        throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString());
1746
0
    }
1747
161
}
1748
1749
static RPCMethod invalidateblock()
1750
2.52k
{
1751
2.52k
    return RPCMethod{
1752
2.52k
        "invalidateblock",
1753
2.52k
        "Permanently marks a block as invalid, as if it violated a consensus rule.\n",
1754
2.52k
                {
1755
2.52k
                    {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hash of the block to mark as invalid"},
1756
2.52k
                },
1757
2.52k
                RPCResult{RPCResult::Type::NONE, "", ""},
1758
2.52k
                RPCExamples{
1759
2.52k
                    HelpExampleCli("invalidateblock", "\"blockhash\"")
1760
2.52k
            + HelpExampleRpc("invalidateblock", "\"blockhash\"")
1761
2.52k
                },
1762
2.52k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
1763
2.52k
{
1764
162
    ChainstateManager& chainman = EnsureAnyChainman(request.context);
1765
162
    uint256 hash(ParseHashV(request.params[0], "blockhash"));
1766
1767
162
    InvalidateBlock(chainman, hash);
1768
1769
162
    return UniValue::VNULL;
1770
162
},
1771
2.52k
    };
1772
2.52k
}
1773
1774
24
void ReconsiderBlock(ChainstateManager& chainman, uint256 block_hash) {
1775
24
    {
1776
24
        LOCK(chainman.GetMutex());
1777
24
        CBlockIndex* pblockindex = chainman.m_blockman.LookupBlockIndex(block_hash);
1778
24
        if (!pblockindex) {
1779
0
            throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
1780
0
        }
1781
1782
24
        chainman.ActiveChainstate().ResetBlockFailureFlags(pblockindex);
1783
24
        chainman.RecalculateBestHeader();
1784
24
    }
1785
1786
0
    BlockValidationState state;
1787
24
    chainman.ActiveChainstate().ActivateBestChain(state);
1788
1789
24
    if (!state.IsValid()) {
1790
0
        throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString());
1791
0
    }
1792
24
}
1793
1794
static RPCMethod reconsiderblock()
1795
2.38k
{
1796
2.38k
    return RPCMethod{
1797
2.38k
        "reconsiderblock",
1798
2.38k
        "Removes invalidity status of a block, its ancestors and its descendants, reconsider them for activation.\n"
1799
2.38k
                "This can be used to undo the effects of invalidateblock.\n",
1800
2.38k
                {
1801
2.38k
                    {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hash of the block to reconsider"},
1802
2.38k
                },
1803
2.38k
                RPCResult{RPCResult::Type::NONE, "", ""},
1804
2.38k
                RPCExamples{
1805
2.38k
                    HelpExampleCli("reconsiderblock", "\"blockhash\"")
1806
2.38k
            + HelpExampleRpc("reconsiderblock", "\"blockhash\"")
1807
2.38k
                },
1808
2.38k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
1809
2.38k
{
1810
24
    ChainstateManager& chainman = EnsureAnyChainman(request.context);
1811
24
    uint256 hash(ParseHashV(request.params[0], "blockhash"));
1812
1813
24
    ReconsiderBlock(chainman, hash);
1814
1815
24
    return UniValue::VNULL;
1816
24
},
1817
2.38k
    };
1818
2.38k
}
1819
1820
static RPCMethod getchaintxstats()
1821
2.59k
{
1822
2.59k
    return RPCMethod{
1823
2.59k
        "getchaintxstats",
1824
2.59k
        "Compute statistics about the total number and rate of transactions in the chain.\n",
1825
2.59k
                {
1826
2.59k
                    {"nblocks", RPCArg::Type::NUM, RPCArg::DefaultHint{"one month"}, "Size of the window in number of blocks"},
1827
2.59k
                    {"blockhash", RPCArg::Type::STR_HEX, RPCArg::DefaultHint{"chain tip"}, "The hash of the block that ends the window."},
1828
2.59k
                },
1829
2.59k
                RPCResult{
1830
2.59k
                    RPCResult::Type::OBJ, "", "",
1831
2.59k
                    {
1832
2.59k
                        {RPCResult::Type::NUM_TIME, "time", "The timestamp for the final block in the window, expressed in " + UNIX_EPOCH_TIME},
1833
2.59k
                        {RPCResult::Type::NUM, "txcount", /*optional=*/true,
1834
2.59k
                         "The total number of transactions in the chain up to that point, if known. "
1835
2.59k
                         "It may be unknown when using assumeutxo."},
1836
2.59k
                        {RPCResult::Type::STR_HEX, "window_final_block_hash", "The hash of the final block in the window"},
1837
2.59k
                        {RPCResult::Type::NUM, "window_final_block_height", "The height of the final block in the window."},
1838
2.59k
                        {RPCResult::Type::NUM, "window_block_count", "Size of the window in number of blocks"},
1839
2.59k
                        {RPCResult::Type::NUM, "window_interval", /*optional=*/true, "The elapsed time in the window in seconds. Only returned if \"window_block_count\" is > 0"},
1840
2.59k
                        {RPCResult::Type::NUM, "window_tx_count", /*optional=*/true,
1841
2.59k
                         "The number of transactions in the window. "
1842
2.59k
                         "Only returned if \"window_block_count\" is > 0 and if txcount exists for the start and end of the window."},
1843
2.59k
                        {RPCResult::Type::NUM, "txrate", /*optional=*/true,
1844
2.59k
                         "The average rate of transactions per second in the window. "
1845
2.59k
                         "Only returned if \"window_interval\" is > 0 and if window_tx_count exists."},
1846
2.59k
                    }},
1847
2.59k
                RPCExamples{
1848
2.59k
                    HelpExampleCli("getchaintxstats", "")
1849
2.59k
            + HelpExampleRpc("getchaintxstats", "2016")
1850
2.59k
                },
1851
2.59k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
1852
2.59k
{
1853
222
    ChainstateManager& chainman = EnsureAnyChainman(request.context);
1854
222
    const CBlockIndex* pindex;
1855
222
    int blockcount = 30 * 24 * 60 * 60 / chainman.GetParams().GetConsensus().nPowTargetSpacing; // By default: 1 month
1856
1857
222
    if (request.params[1].isNull()) {
1858
8
        LOCK(cs_main);
1859
8
        pindex = chainman.ActiveChain().Tip();
1860
214
    } else {
1861
214
        uint256 hash(ParseHashV(request.params[1], "blockhash"));
1862
214
        LOCK(cs_main);
1863
214
        pindex = chainman.m_blockman.LookupBlockIndex(hash);
1864
214
        if (!pindex) {
1865
2
            throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
1866
2
        }
1867
212
        if (!chainman.ActiveChain().Contains(*pindex)) {
1868
2
            throw JSONRPCError(RPC_INVALID_PARAMETER, "Block is not in main chain");
1869
2
        }
1870
212
    }
1871
1872
218
    CHECK_NONFATAL(pindex != nullptr);
1873
1874
218
    if (request.params[0].isNull()) {
1875
6
        blockcount = std::max(0, std::min(blockcount, pindex->nHeight - 1));
1876
212
    } else {
1877
212
        blockcount = request.params[0].getInt<int>();
1878
1879
212
        if (blockcount < 0 || (blockcount > 0 && blockcount >= pindex->nHeight)) {
1880
4
            throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block count: should be between 0 and the block's height - 1");
1881
4
        }
1882
212
    }
1883
1884
214
    const CBlockIndex& past_block{*CHECK_NONFATAL(pindex->GetAncestor(pindex->nHeight - blockcount))};
1885
214
    const int64_t nTimeDiff{pindex->GetMedianTimePast() - past_block.GetMedianTimePast()};
1886
1887
214
    UniValue ret(UniValue::VOBJ);
1888
214
    ret.pushKV("time", pindex->nTime);
1889
214
    if (pindex->m_chain_tx_count) {
1890
111
        ret.pushKV("txcount", pindex->m_chain_tx_count);
1891
111
    }
1892
214
    ret.pushKV("window_final_block_hash", pindex->GetBlockHash().GetHex());
1893
214
    ret.pushKV("window_final_block_height", pindex->nHeight);
1894
214
    ret.pushKV("window_block_count", blockcount);
1895
214
    if (blockcount > 0) {
1896
208
        ret.pushKV("window_interval", nTimeDiff);
1897
208
        if (pindex->m_chain_tx_count != 0 && past_block.m_chain_tx_count != 0) {
1898
108
            const auto window_tx_count = pindex->m_chain_tx_count - past_block.m_chain_tx_count;
1899
108
            ret.pushKV("window_tx_count", window_tx_count);
1900
108
            if (nTimeDiff > 0) {
1901
24
                ret.pushKV("txrate", double(window_tx_count) / nTimeDiff);
1902
24
            }
1903
108
        }
1904
208
    }
1905
1906
214
    return ret;
1907
218
},
1908
2.59k
    };
1909
2.59k
}
1910
1911
template<typename T>
1912
static T CalculateTruncatedMedian(std::vector<T>& scores)
1913
214
{
1914
214
    size_t size = scores.size();
1915
214
    if (size == 0) {
1916
200
        return 0;
1917
200
    }
1918
1919
14
    std::sort(scores.begin(), scores.end());
1920
14
    if (size % 2 == 0) {
1921
8
        return (scores[size / 2 - 1] + scores[size / 2]) / 2;
1922
8
    } else {
1923
6
        return scores[size / 2];
1924
6
    }
1925
14
}
1926
1927
void CalculatePercentilesByWeight(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES], std::vector<std::pair<CAmount, int64_t>>& scores, int64_t total_weight)
1928
111
{
1929
111
    if (scores.empty()) {
1930
100
        return;
1931
100
    }
1932
1933
11
    std::sort(scores.begin(), scores.end());
1934
1935
    // 10th, 25th, 50th, 75th, and 90th percentile weight units.
1936
11
    const double weights[NUM_GETBLOCKSTATS_PERCENTILES] = {
1937
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
1938
11
    };
1939
1940
11
    int64_t next_percentile_index = 0;
1941
11
    int64_t cumulative_weight = 0;
1942
235
    for (const auto& element : scores) {
1943
235
        cumulative_weight += element.second;
1944
290
        while (next_percentile_index < NUM_GETBLOCKSTATS_PERCENTILES && cumulative_weight >= weights[next_percentile_index]) {
1945
55
            result[next_percentile_index] = element.first;
1946
55
            ++next_percentile_index;
1947
55
        }
1948
235
    }
1949
1950
    // Fill any remaining percentiles with the last value.
1951
11
    for (int64_t i = next_percentile_index; i < NUM_GETBLOCKSTATS_PERCENTILES; i++) {
1952
0
        result[i] = scores.back().first;
1953
0
    }
1954
11
}
1955
1956
template<typename T>
1957
307
static inline bool SetHasKeys(const std::set<T>& set) {return false;}
1958
template<typename T, typename Tk, typename... Args>
1959
static inline bool SetHasKeys(const std::set<T>& set, const Tk& key, const Args&... args)
1960
2.22k
{
1961
2.22k
    return (set.contains(key)) || SetHasKeys(set, args...);
1962
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
1960
93
{
1961
93
    return (set.contains(key)) || SetHasKeys(set, args...);
1962
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
1960
90
{
1961
90
    return (set.contains(key)) || SetHasKeys(set, args...);
1962
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
1960
87
{
1961
87
    return (set.contains(key)) || SetHasKeys(set, args...);
1962
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
1960
84
{
1961
84
    return (set.contains(key)) || SetHasKeys(set, args...);
1962
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
1960
81
{
1961
81
    return (set.contains(key)) || SetHasKeys(set, args...);
1962
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
1960
78
{
1961
78
    return (set.contains(key)) || SetHasKeys(set, args...);
1962
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
1960
75
{
1961
75
    return (set.contains(key)) || SetHasKeys(set, args...);
1962
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
1960
72
{
1961
72
    return (set.contains(key)) || SetHasKeys(set, args...);
1962
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
1960
64
{
1961
64
    return (set.contains(key)) || SetHasKeys(set, args...);
1962
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
1960
148
{
1961
148
    return (set.contains(key)) || SetHasKeys(set, args...);
1962
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
1960
142
{
1961
142
    return (set.contains(key)) || SetHasKeys(set, args...);
1962
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
1960
96
{
1961
96
    return (set.contains(key)) || SetHasKeys(set, args...);
1962
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
1960
93
{
1961
93
    return (set.contains(key)) || SetHasKeys(set, args...);
1962
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
1960
90
{
1961
90
    return (set.contains(key)) || SetHasKeys(set, args...);
1962
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
1960
87
{
1961
87
    return (set.contains(key)) || SetHasKeys(set, args...);
1962
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
1960
84
{
1961
84
    return (set.contains(key)) || SetHasKeys(set, args...);
1962
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
1960
99
{
1961
99
    return (set.contains(key)) || SetHasKeys(set, args...);
1962
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
1960
96
{
1961
96
    return (set.contains(key)) || SetHasKeys(set, args...);
1962
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
1960
93
{
1961
93
    return (set.contains(key)) || SetHasKeys(set, args...);
1962
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
1960
90
{
1961
90
    return (set.contains(key)) || SetHasKeys(set, args...);
1962
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
1960
90
{
1961
90
    return (set.contains(key)) || SetHasKeys(set, args...);
1962
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
1960
99
{
1961
99
    return (set.contains(key)) || SetHasKeys(set, args...);
1962
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
1960
96
{
1961
96
    return (set.contains(key)) || SetHasKeys(set, args...);
1962
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
1960
93
{
1961
93
    return (set.contains(key)) || SetHasKeys(set, args...);
1962
93
}
1963
1964
// outpoint (needed for the utxo index) + nHeight|fCoinBase
1965
static constexpr size_t PER_UTXO_OVERHEAD = sizeof(COutPoint) + sizeof(uint32_t);
1966
1967
static RPCMethod getblockstats()
1968
2.48k
{
1969
2.48k
    return RPCMethod{
1970
2.48k
        "getblockstats",
1971
2.48k
        "Compute per block statistics for a given window. All amounts are in satoshis.\n"
1972
2.48k
                "It won't work for some heights with pruning.\n",
1973
2.48k
                {
1974
2.48k
                    {"hash_or_height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The block hash or height of the target block",
1975
2.48k
                     RPCArgOptions{
1976
2.48k
                         .skip_type_check = true,
1977
2.48k
                         .type_str = {"", "string or numeric"},
1978
2.48k
                     }},
1979
2.48k
                    {"stats", RPCArg::Type::ARR, RPCArg::DefaultHint{"all values"}, "Values to plot (see result below)",
1980
2.48k
                        {
1981
2.48k
                            {"height", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Selected statistic"},
1982
2.48k
                            {"time", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Selected statistic"},
1983
2.48k
                        },
1984
2.48k
                        RPCArgOptions{.oneline_description="stats"}},
1985
2.48k
                },
1986
2.48k
                RPCResult{
1987
2.48k
            RPCResult::Type::OBJ, "", "",
1988
2.48k
            {
1989
2.48k
                {RPCResult::Type::NUM, "avgfee", /*optional=*/true, "Average fee in the block"},
1990
2.48k
                {RPCResult::Type::NUM, "avgfeerate", /*optional=*/true, "Average feerate (in satoshis per virtual byte)"},
1991
2.48k
                {RPCResult::Type::NUM, "avgtxsize", /*optional=*/true, "Average transaction size"},
1992
2.48k
                {RPCResult::Type::STR_HEX, "blockhash", /*optional=*/true, "The block hash (to check for potential reorgs)"},
1993
2.48k
                {RPCResult::Type::ARR_FIXED, "feerate_percentiles", /*optional=*/true, "Feerates at the 10th, 25th, 50th, 75th, and 90th percentile weight unit (in satoshis per virtual byte)",
1994
2.48k
                {
1995
2.48k
                    {RPCResult::Type::NUM, "10th_percentile_feerate", "The 10th percentile feerate"},
1996
2.48k
                    {RPCResult::Type::NUM, "25th_percentile_feerate", "The 25th percentile feerate"},
1997
2.48k
                    {RPCResult::Type::NUM, "50th_percentile_feerate", "The 50th percentile feerate"},
1998
2.48k
                    {RPCResult::Type::NUM, "75th_percentile_feerate", "The 75th percentile feerate"},
1999
2.48k
                    {RPCResult::Type::NUM, "90th_percentile_feerate", "The 90th percentile feerate"},
2000
2.48k
                }},
2001
2.48k
                {RPCResult::Type::NUM, "height", /*optional=*/true, "The height of the block"},
2002
2.48k
                {RPCResult::Type::NUM, "ins", /*optional=*/true, "The number of inputs (excluding coinbase)"},
2003
2.48k
                {RPCResult::Type::NUM, "maxfee", /*optional=*/true, "Maximum fee in the block"},
2004
2.48k
                {RPCResult::Type::NUM, "maxfeerate", /*optional=*/true, "Maximum feerate (in satoshis per virtual byte)"},
2005
2.48k
                {RPCResult::Type::NUM, "maxtxsize", /*optional=*/true, "Maximum transaction size"},
2006
2.48k
                {RPCResult::Type::NUM, "medianfee", /*optional=*/true, "Truncated median fee in the block"},
2007
2.48k
                {RPCResult::Type::NUM, "mediantime", /*optional=*/true, "The block median time past"},
2008
2.48k
                {RPCResult::Type::NUM, "mediantxsize", /*optional=*/true, "Truncated median transaction size"},
2009
2.48k
                {RPCResult::Type::NUM, "minfee", /*optional=*/true, "Minimum fee in the block"},
2010
2.48k
                {RPCResult::Type::NUM, "minfeerate", /*optional=*/true, "Minimum feerate (in satoshis per virtual byte)"},
2011
2.48k
                {RPCResult::Type::NUM, "mintxsize", /*optional=*/true, "Minimum transaction size"},
2012
2.48k
                {RPCResult::Type::NUM, "outs", /*optional=*/true, "The number of outputs"},
2013
2.48k
                {RPCResult::Type::NUM, "subsidy", /*optional=*/true, "The block subsidy"},
2014
2.48k
                {RPCResult::Type::NUM, "swtotal_size", /*optional=*/true, "Total size of all segwit transactions"},
2015
2.48k
                {RPCResult::Type::NUM, "swtotal_weight", /*optional=*/true, "Total weight of all segwit transactions"},
2016
2.48k
                {RPCResult::Type::NUM, "swtxs", /*optional=*/true, "The number of segwit transactions"},
2017
2.48k
                {RPCResult::Type::NUM, "time", /*optional=*/true, "The block time"},
2018
2.48k
                {RPCResult::Type::NUM, "total_out", /*optional=*/true, "Total amount in all outputs (excluding coinbase and thus reward [ie subsidy + totalfee])"},
2019
2.48k
                {RPCResult::Type::NUM, "total_size", /*optional=*/true, "Total size of all non-coinbase transactions"},
2020
2.48k
                {RPCResult::Type::NUM, "total_weight", /*optional=*/true, "Total weight of all non-coinbase transactions"},
2021
2.48k
                {RPCResult::Type::NUM, "totalfee", /*optional=*/true, "The fee total"},
2022
2.48k
                {RPCResult::Type::NUM, "txs", /*optional=*/true, "The number of transactions (including coinbase)"},
2023
2.48k
                {RPCResult::Type::NUM, "utxo_increase", /*optional=*/true, "The increase/decrease in the number of unspent outputs (not discounting op_return and similar)"},
2024
2.48k
                {RPCResult::Type::NUM, "utxo_size_inc", /*optional=*/true, "The increase/decrease in size for the utxo index (not discounting op_return and similar)"},
2025
2.48k
                {RPCResult::Type::NUM, "utxo_increase_actual", /*optional=*/true, "The increase/decrease in the number of unspent outputs, not counting unspendables"},
2026
2.48k
                {RPCResult::Type::NUM, "utxo_size_inc_actual", /*optional=*/true, "The increase/decrease in size for the utxo index, not counting unspendables"},
2027
2.48k
            }},
2028
2.48k
                RPCExamples{
2029
2.48k
                    HelpExampleCli("getblockstats", R"('"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09"' '["minfeerate","avgfeerate"]')") +
2030
2.48k
                    HelpExampleCli("getblockstats", R"(1000 '["minfeerate","avgfeerate"]')") +
2031
2.48k
                    HelpExampleRpc("getblockstats", R"("00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09", ["minfeerate","avgfeerate"])") +
2032
2.48k
                    HelpExampleRpc("getblockstats", R"(1000, ["minfeerate","avgfeerate"])")
2033
2.48k
                },
2034
2.48k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
2035
2.48k
{
2036
112
    ChainstateManager& chainman = EnsureAnyChainman(request.context);
2037
112
    const CBlockIndex& pindex{*CHECK_NONFATAL(ParseHashOrHeight(request.params[0], chainman))};
2038
2039
112
    std::set<std::string> stats;
2040
112
    if (!request.params[1].isNull()) {
2041
99
        const UniValue stats_univalue = request.params[1].get_array();
2042
204
        for (unsigned int i = 0; i < stats_univalue.size(); i++) {
2043
105
            const std::string stat = stats_univalue[i].get_str();
2044
105
            stats.insert(stat);
2045
105
        }
2046
99
    }
2047
2048
112
    const CBlock& block = GetBlockChecked(chainman.m_blockman, pindex);
2049
112
    const CBlockUndo& blockUndo = GetUndoChecked(chainman.m_blockman, pindex);
2050
2051
112
    const bool do_all = stats.size() == 0; // Calculate everything if nothing selected (default)
2052
112
    const bool do_mediantxsize = do_all || stats.contains("mediantxsize");
2053
112
    const bool do_medianfee = do_all || stats.contains("medianfee");
2054
112
    const bool do_feerate_percentiles = do_all || stats.contains("feerate_percentiles");
2055
112
    const bool loop_inputs = do_all || do_medianfee || do_feerate_percentiles ||
2056
112
        SetHasKeys(stats, "utxo_increase", "utxo_increase_actual", "utxo_size_inc", "utxo_size_inc_actual", "totalfee", "avgfee", "avgfeerate", "minfee", "maxfee", "minfeerate", "maxfeerate");
2057
112
    const bool loop_outputs = do_all || loop_inputs || stats.contains("total_out");
2058
112
    const bool do_calculate_size = do_mediantxsize ||
2059
112
        SetHasKeys(stats, "total_size", "avgtxsize", "mintxsize", "maxtxsize", "swtotal_size");
2060
112
    const bool do_calculate_weight = do_all || SetHasKeys(stats, "total_weight", "avgfeerate", "swtotal_weight", "avgfeerate", "feerate_percentiles", "minfeerate", "maxfeerate");
2061
112
    const bool do_calculate_sw = do_all || SetHasKeys(stats, "swtxs", "swtotal_size", "swtotal_weight");
2062
2063
112
    CAmount maxfee = 0;
2064
112
    CAmount maxfeerate = 0;
2065
112
    CAmount minfee = MAX_MONEY;
2066
112
    CAmount minfeerate = MAX_MONEY;
2067
112
    CAmount total_out = 0;
2068
112
    CAmount totalfee = 0;
2069
112
    int64_t inputs = 0;
2070
112
    int64_t maxtxsize = 0;
2071
112
    int64_t mintxsize = MAX_BLOCK_SERIALIZED_SIZE;
2072
112
    int64_t outputs = 0;
2073
112
    int64_t swtotal_size = 0;
2074
112
    int64_t swtotal_weight = 0;
2075
112
    int64_t swtxs = 0;
2076
112
    int64_t total_size = 0;
2077
112
    int64_t total_weight = 0;
2078
112
    int64_t utxos = 0;
2079
112
    int64_t utxo_size_inc = 0;
2080
112
    int64_t utxo_size_inc_actual = 0;
2081
112
    std::vector<CAmount> fee_array;
2082
112
    std::vector<std::pair<CAmount, int64_t>> feerate_array;
2083
112
    std::vector<int64_t> txsize_array;
2084
2085
388
    for (size_t i = 0; i < block.vtx.size(); ++i) {
2086
276
        const auto& tx = block.vtx.at(i);
2087
276
        outputs += tx->vout.size();
2088
2089
276
        CAmount tx_total_out = 0;
2090
276
        if (loop_outputs) {
2091
277
            for (const CTxOut& out : tx->vout) {
2092
277
                tx_total_out += out.nValue;
2093
2094
277
                uint64_t out_size{GetSerializeSize(out) + PER_UTXO_OVERHEAD};
2095
277
                utxo_size_inc += out_size;
2096
2097
                // The Genesis block and the repeated BIP30 block coinbases don't change the UTXO
2098
                // set counts, so they have to be excluded from the statistics
2099
277
                if (pindex.nHeight == 0 || (IsBIP30Repeat(pindex) && tx->IsCoinBase())) continue;
2100
                // Skip unspendable outputs since they are not included in the UTXO set
2101
276
                if (out.scriptPubKey.IsUnspendable()) continue;
2102
2103
205
                ++utxos;
2104
205
                utxo_size_inc_actual += out_size;
2105
205
            }
2106
139
        }
2107
2108
276
        if (tx->IsCoinBase()) {
2109
107
            continue;
2110
107
        }
2111
2112
169
        inputs += tx->vin.size(); // Don't count coinbase's fake input
2113
169
        total_out += tx_total_out; // Don't count coinbase reward
2114
2115
169
        int64_t tx_size = 0;
2116
169
        if (do_calculate_size) {
2117
2118
44
            tx_size = tx->ComputeTotalSize();
2119
44
            if (do_mediantxsize) {
2120
19
                txsize_array.push_back(tx_size);
2121
19
            }
2122
44
            maxtxsize = std::max(maxtxsize, tx_size);
2123
44
            mintxsize = std::min(mintxsize, tx_size);
2124
44
            total_size += tx_size;
2125
44
        }
2126
2127
169
        int64_t weight = 0;
2128
169
        if (do_calculate_weight) {
2129
44
            weight = GetTransactionWeight(*tx);
2130
44
            total_weight += weight;
2131
44
        }
2132
2133
169
        if (do_calculate_sw && tx->HasWitness()) {
2134
24
            ++swtxs;
2135
24
            swtotal_size += tx_size;
2136
24
            swtotal_weight += weight;
2137
24
        }
2138
2139
169
        if (loop_inputs) {
2140
79
            CAmount tx_total_in = 0;
2141
79
            const auto& txundo = blockUndo.vtxundo.at(i - 1);
2142
79
            for (const Coin& coin: txundo.vprevout) {
2143
79
                const CTxOut& prevoutput = coin.out;
2144
2145
79
                tx_total_in += prevoutput.nValue;
2146
79
                uint64_t prevout_size{GetSerializeSize(prevoutput) + PER_UTXO_OVERHEAD};
2147
79
                utxo_size_inc -= prevout_size;
2148
79
                utxo_size_inc_actual -= prevout_size;
2149
79
            }
2150
2151
79
            CAmount txfee = tx_total_in - tx_total_out;
2152
79
            CHECK_NONFATAL(MoneyRange(txfee));
2153
79
            if (do_medianfee) {
2154
19
                fee_array.push_back(txfee);
2155
19
            }
2156
79
            maxfee = std::max(maxfee, txfee);
2157
79
            minfee = std::min(minfee, txfee);
2158
79
            totalfee += txfee;
2159
2160
            // New feerate uses satoshis per virtual byte instead of per serialized byte
2161
79
            CAmount feerate = weight ? (txfee * WITNESS_SCALE_FACTOR) / weight : 0;
2162
79
            if (do_feerate_percentiles) {
2163
19
                feerate_array.emplace_back(feerate, weight);
2164
19
            }
2165
79
            maxfeerate = std::max(maxfeerate, feerate);
2166
79
            minfeerate = std::min(minfeerate, feerate);
2167
79
        }
2168
169
    }
2169
2170
112
    CAmount feerate_percentiles[NUM_GETBLOCKSTATS_PERCENTILES] = { 0 };
2171
112
    CalculatePercentilesByWeight(feerate_percentiles, feerate_array, total_weight);
2172
2173
112
    UniValue feerates_res(UniValue::VARR);
2174
647
    for (int64_t i = 0; i < NUM_GETBLOCKSTATS_PERCENTILES; i++) {
2175
535
        feerates_res.push_back(feerate_percentiles[i]);
2176
535
    }
2177
2178
112
    UniValue ret_all(UniValue::VOBJ);
2179
112
    ret_all.pushKV("avgfee", (block.vtx.size() > 1) ? totalfee / (block.vtx.size() - 1) : 0);
2180
112
    ret_all.pushKV("avgfeerate", total_weight ? (totalfee * WITNESS_SCALE_FACTOR) / total_weight : 0); // Unit: sat/vbyte
2181
112
    ret_all.pushKV("avgtxsize", (block.vtx.size() > 1) ? total_size / (block.vtx.size() - 1) : 0);
2182
112
    ret_all.pushKV("blockhash", pindex.GetBlockHash().GetHex());
2183
112
    ret_all.pushKV("feerate_percentiles", std::move(feerates_res));
2184
112
    ret_all.pushKV("height", pindex.nHeight);
2185
112
    ret_all.pushKV("ins", inputs);
2186
112
    ret_all.pushKV("maxfee", maxfee);
2187
112
    ret_all.pushKV("maxfeerate", maxfeerate);
2188
112
    ret_all.pushKV("maxtxsize", maxtxsize);
2189
112
    ret_all.pushKV("medianfee", CalculateTruncatedMedian(fee_array));
2190
112
    ret_all.pushKV("mediantime", pindex.GetMedianTimePast());
2191
112
    ret_all.pushKV("mediantxsize", CalculateTruncatedMedian(txsize_array));
2192
112
    ret_all.pushKV("minfee", (minfee == MAX_MONEY) ? 0 : minfee);
2193
112
    ret_all.pushKV("minfeerate", (minfeerate == MAX_MONEY) ? 0 : minfeerate);
2194
112
    ret_all.pushKV("mintxsize", mintxsize == MAX_BLOCK_SERIALIZED_SIZE ? 0 : mintxsize);
2195
112
    ret_all.pushKV("outs", outputs);
2196
112
    ret_all.pushKV("subsidy", GetBlockSubsidy(pindex.nHeight, chainman.GetParams().GetConsensus()));
2197
112
    ret_all.pushKV("swtotal_size", swtotal_size);
2198
112
    ret_all.pushKV("swtotal_weight", swtotal_weight);
2199
112
    ret_all.pushKV("swtxs", swtxs);
2200
112
    ret_all.pushKV("time", pindex.GetBlockTime());
2201
112
    ret_all.pushKV("total_out", total_out);
2202
112
    ret_all.pushKV("total_size", total_size);
2203
112
    ret_all.pushKV("total_weight", total_weight);
2204
112
    ret_all.pushKV("totalfee", totalfee);
2205
112
    ret_all.pushKV("txs", block.vtx.size());
2206
112
    ret_all.pushKV("utxo_increase", outputs - inputs);
2207
112
    ret_all.pushKV("utxo_size_inc", utxo_size_inc);
2208
112
    ret_all.pushKV("utxo_increase_actual", utxos - inputs);
2209
112
    ret_all.pushKV("utxo_size_inc_actual", utxo_size_inc_actual);
2210
2211
112
    if (do_all) {
2212
8
        return ret_all;
2213
8
    }
2214
2215
104
    UniValue ret(UniValue::VOBJ);
2216
104
    for (const std::string& stat : stats) {
2217
100
        const UniValue& value = ret_all[stat];
2218
100
        if (value.isNull()) {
2219
5
            throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid selected statistic '%s'", stat));
2220
5
        }
2221
95
        ret.pushKV(stat, value);
2222
95
    }
2223
99
    return ret;
2224
104
},
2225
2.48k
    };
2226
2.48k
}
2227
2228
namespace {
2229
//! Search for a given set of pubkey scripts
2230
bool FindScriptPubKey(std::atomic<int>& scan_progress, const std::atomic<bool>& should_abort, int64_t& count, CCoinsViewCursor* cursor, const std::set<CScript>& needles, std::map<COutPoint, Coin>& out_results, std::function<void()>& interruption_point)
2231
1.08k
{
2232
1.08k
    scan_progress = 0;
2233
1.08k
    count = 0;
2234
244k
    while (cursor->Valid()) {
2235
243k
        COutPoint key;
2236
243k
        Coin coin;
2237
243k
        if (!cursor->GetKey(key) || !cursor->GetValue(coin)) return false;
2238
243k
        if (++count % 8192 == 0) {
2239
0
            interruption_point();
2240
0
            if (should_abort) {
2241
                // allow to abort the scan via the abort reference
2242
0
                return false;
2243
0
            }
2244
0
        }
2245
243k
        if (count % 256 == 0) {
2246
            // update progress reference every 256 item
2247
445
            uint32_t high = 0x100 * *UCharCast(key.hash.begin()) + *(UCharCast(key.hash.begin()) + 1);
2248
445
            scan_progress = (int)(high * 100.0 / 65536.0 + 0.5);
2249
445
        }
2250
243k
        if (needles.contains(coin.out.scriptPubKey)) {
2251
168k
            out_results.emplace(key, coin);
2252
168k
        }
2253
243k
        cursor->Next();
2254
243k
    }
2255
1.08k
    scan_progress = 100;
2256
1.08k
    return true;
2257
1.08k
}
2258
} // namespace
2259
2260
/** RAII object to prevent concurrency issue when scanning the txout set */
2261
static std::atomic<int> g_scan_progress;
2262
static std::atomic<bool> g_scan_in_progress;
2263
static std::atomic<bool> g_should_abort_scan;
2264
class CoinsViewScanReserver
2265
{
2266
private:
2267
    bool m_could_reserve{false};
2268
public:
2269
1.09k
    explicit CoinsViewScanReserver() = default;
2270
2271
1.09k
    bool reserve() {
2272
1.09k
        CHECK_NONFATAL(!m_could_reserve);
2273
1.09k
        if (g_scan_in_progress.exchange(true)) {
2274
0
            return false;
2275
0
        }
2276
1.09k
        CHECK_NONFATAL(g_scan_progress == 0);
2277
1.09k
        m_could_reserve = true;
2278
1.09k
        return true;
2279
1.09k
    }
2280
2281
1.09k
    ~CoinsViewScanReserver() {
2282
1.09k
        if (m_could_reserve) {
2283
1.09k
            g_scan_in_progress = false;
2284
1.09k
            g_scan_progress = 0;
2285
1.09k
        }
2286
1.09k
    }
2287
};
2288
2289
static const auto scan_action_arg_desc = RPCArg{
2290
    "action", RPCArg::Type::STR, RPCArg::Optional::NO, "The action to execute\n"
2291
        "\"start\" for starting a scan\n"
2292
        "\"abort\" for aborting the current scan (returns true when abort was successful)\n"
2293
        "\"status\" for progress report (in %) of the current scan"
2294
};
2295
2296
static const auto output_descriptor_obj = RPCArg{
2297
    "", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "An object with output descriptor and metadata",
2298
    {
2299
        {"desc", RPCArg::Type::STR, RPCArg::Optional::NO, "An output descriptor"},
2300
        {"range", RPCArg::Type::RANGE, RPCArg::Default{1000}, "The range of HD chain indexes to explore (either end or [begin,end])"},
2301
    }
2302
};
2303
2304
static const auto scan_objects_arg_desc = RPCArg{
2305
    "scanobjects", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, "Array of scan objects. Required for \"start\" action\n"
2306
        "Every scan object is either a string descriptor or an object:",
2307
    {
2308
        {"descriptor", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "An output descriptor"},
2309
        output_descriptor_obj,
2310
    },
2311
    RPCArgOptions{.oneline_description="[scanobjects,...]"},
2312
};
2313
2314
static const auto scan_result_abort = RPCResult{
2315
    "when action=='abort'", RPCResult::Type::BOOL, "success",
2316
    "True if scan will be aborted (not necessarily before this RPC returns), or false if there is no scan to abort"
2317
};
2318
static const auto scan_result_status_none = RPCResult{
2319
    "when action=='status' and no scan is in progress - possibly already completed", RPCResult::Type::NONE, "", ""
2320
};
2321
static const auto scan_result_status_some = RPCResult{
2322
    "when action=='status' and a scan is currently in progress", RPCResult::Type::OBJ, "", "",
2323
    {{RPCResult::Type::NUM, "progress", "Approximate percent complete"},}
2324
};
2325
2326
2327
static RPCMethod scantxoutset()
2328
3.46k
{
2329
    // raw() descriptor corresponding to mainnet address 12cbQLTFMXRnSzktFkuoG3eHoMeFtpTu3S
2330
3.46k
    const std::string EXAMPLE_DESCRIPTOR_RAW = "raw(76a91411b366edfc0a8b66feebae5c2e25a7b6a5d1cf3188ac)#fm24fxxy";
2331
2332
3.46k
    return RPCMethod{
2333
3.46k
        "scantxoutset",
2334
3.46k
        "Scans the unspent transaction output set for entries that match certain output descriptors.\n"
2335
3.46k
        "Examples of output descriptors are:\n"
2336
3.46k
        "    addr(<address>)                      Outputs whose output script corresponds to the specified address (does not include P2PK)\n"
2337
3.46k
        "    raw(<hex script>)                    Outputs whose output script equals the specified hex-encoded bytes\n"
2338
3.46k
        "    combo(<pubkey>)                      P2PK, P2PKH, P2WPKH, and P2SH-P2WPKH outputs for the given pubkey\n"
2339
3.46k
        "    pkh(<pubkey>)                        P2PKH outputs for the given pubkey\n"
2340
3.46k
        "    sh(multi(<n>,<pubkey>,<pubkey>,...)) P2SH-multisig outputs for the given threshold and pubkeys\n"
2341
3.46k
        "    tr(<pubkey>)                         P2TR\n"
2342
3.46k
        "    tr(<pubkey>,{pk(<pubkey>)})          P2TR with single fallback pubkey in tapscript\n"
2343
3.46k
        "    rawtr(<pubkey>)                      P2TR with the specified key as output key rather than inner\n"
2344
3.46k
        "    wsh(and_v(v:pk(<pubkey>),after(2)))  P2WSH miniscript with mandatory pubkey and a timelock\n"
2345
3.46k
        "\nIn the above, <pubkey> either refers to a fixed public key in hexadecimal notation, or to an xpub/xprv optionally followed by one\n"
2346
3.46k
        "or more path elements separated by \"/\", and optionally ending in \"/*\" (unhardened), or \"/*'\" or \"/*h\" (hardened) to specify all\n"
2347
3.46k
        "unhardened or hardened child keys.\n"
2348
3.46k
        "In the latter case, a range needs to be specified by below if different from 1000.\n"
2349
3.46k
        "For more information on output descriptors, see the documentation in the doc/descriptors.md file.\n",
2350
3.46k
        {
2351
3.46k
            scan_action_arg_desc,
2352
3.46k
            scan_objects_arg_desc,
2353
3.46k
        },
2354
3.46k
        {
2355
3.46k
            RPCResult{"when action=='start'; only returns after scan completes", RPCResult::Type::OBJ, "", "", {
2356
3.46k
                {RPCResult::Type::BOOL, "success", "Whether the scan was completed"},
2357
3.46k
                {RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs scanned"},
2358
3.46k
                {RPCResult::Type::NUM, "height", "The block height at which the scan was done"},
2359
3.46k
                {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"},
2360
3.46k
                {RPCResult::Type::ARR, "unspents", "",
2361
3.46k
                {
2362
3.46k
                    {RPCResult::Type::OBJ, "", "",
2363
3.46k
                    {
2364
3.46k
                        {RPCResult::Type::STR_HEX, "txid", "The transaction id"},
2365
3.46k
                        {RPCResult::Type::NUM, "vout", "The vout value"},
2366
3.46k
                        {RPCResult::Type::STR_HEX, "scriptPubKey", "The output script"},
2367
3.46k
                        {RPCResult::Type::STR, "desc", "A specialized descriptor for the matched output script"},
2368
3.46k
                        {RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " of the unspent output"},
2369
3.46k
                        {RPCResult::Type::BOOL, "coinbase", "Whether this is a coinbase output"},
2370
3.46k
                        {RPCResult::Type::NUM, "height", "Height of the unspent transaction output"},
2371
3.46k
                        {RPCResult::Type::STR_HEX, "blockhash", "Blockhash of the unspent transaction output"},
2372
3.46k
                        {RPCResult::Type::NUM, "confirmations", "Number of confirmations of the unspent transaction output when the scan was done"},
2373
3.46k
                    }},
2374
3.46k
                }},
2375
3.46k
                {RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount of all found unspent outputs in " + CURRENCY_UNIT},
2376
3.46k
            }},
2377
3.46k
            scan_result_abort,
2378
3.46k
            scan_result_status_some,
2379
3.46k
            scan_result_status_none,
2380
3.46k
        },
2381
3.46k
        RPCExamples{
2382
3.46k
            HelpExampleCli("scantxoutset", "start \'[\"" + EXAMPLE_DESCRIPTOR_RAW + "\"]\'") +
2383
3.46k
            HelpExampleCli("scantxoutset", "status") +
2384
3.46k
            HelpExampleCli("scantxoutset", "abort") +
2385
3.46k
            HelpExampleRpc("scantxoutset", "\"start\", [\"" + EXAMPLE_DESCRIPTOR_RAW + "\"]") +
2386
3.46k
            HelpExampleRpc("scantxoutset", "\"status\"") +
2387
3.46k
            HelpExampleRpc("scantxoutset", "\"abort\"")
2388
3.46k
        },
2389
3.46k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
2390
3.46k
{
2391
1.09k
    UniValue result(UniValue::VOBJ);
2392
1.09k
    const auto action{self.Arg<std::string_view>("action")};
2393
1.09k
    if (action == "status") {
2394
1
        CoinsViewScanReserver reserver;
2395
1
        if (reserver.reserve()) {
2396
            // no scan in progress
2397
1
            return UniValue::VNULL;
2398
1
        }
2399
0
        result.pushKV("progress", g_scan_progress.load());
2400
0
        return result;
2401
1.09k
    } else if (action == "abort") {
2402
1
        CoinsViewScanReserver reserver;
2403
1
        if (reserver.reserve()) {
2404
            // reserve was possible which means no scan was running
2405
1
            return false;
2406
1
        }
2407
        // set the abort flag
2408
0
        g_should_abort_scan = true;
2409
0
        return true;
2410
1.09k
    } else if (action == "start") {
2411
1.09k
        CoinsViewScanReserver reserver;
2412
1.09k
        if (!reserver.reserve()) {
2413
0
            throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan already in progress, use action \"abort\" or \"status\"");
2414
0
        }
2415
2416
1.09k
        if (request.params.size() < 2) {
2417
1
            throw JSONRPCError(RPC_MISC_ERROR, "scanobjects argument is required for the start action");
2418
1
        }
2419
2420
1.09k
        std::set<CScript> needles;
2421
1.09k
        std::map<CScript, std::string> descriptors;
2422
1.09k
        CAmount total_in = 0;
2423
2424
        // loop through the scan objects
2425
1.50k
        for (const UniValue& scanobject : request.params[1].get_array().getValues()) {
2426
1.50k
            FlatSigningProvider provider;
2427
1.50k
            auto scripts = EvalDescriptorStringOrObject(scanobject, provider);
2428
73.5k
            for (CScript& script : scripts) {
2429
73.5k
                std::string inferred = InferDescriptor(script, provider)->ToString();
2430
73.5k
                needles.emplace(script);
2431
73.5k
                descriptors.emplace(std::move(script), std::move(inferred));
2432
73.5k
            }
2433
1.50k
        }
2434
2435
        // Scan the unspent transaction output set for inputs
2436
1.09k
        UniValue unspents(UniValue::VARR);
2437
1.09k
        std::vector<CTxOut> input_txos;
2438
1.09k
        std::map<COutPoint, Coin> coins;
2439
1.09k
        g_should_abort_scan = false;
2440
1.09k
        int64_t count = 0;
2441
1.09k
        std::unique_ptr<CCoinsViewCursor> pcursor;
2442
1.09k
        const CBlockIndex* tip;
2443
1.09k
        NodeContext& node = EnsureAnyNodeContext(request.context);
2444
1.09k
        {
2445
1.09k
            ChainstateManager& chainman = EnsureChainman(node);
2446
1.09k
            LOCK(cs_main);
2447
1.09k
            Chainstate& active_chainstate = chainman.ActiveChainstate();
2448
1.09k
            active_chainstate.ForceFlushStateToDisk(/*wipe_cache=*/false);
2449
1.09k
            pcursor = CHECK_NONFATAL(active_chainstate.CoinsDB().Cursor());
2450
1.09k
            tip = CHECK_NONFATAL(active_chainstate.m_chain.Tip());
2451
1.09k
        }
2452
1.09k
        bool res = FindScriptPubKey(g_scan_progress, g_should_abort_scan, count, pcursor.get(), needles, coins, node.rpc_interruption_point);
2453
1.09k
        result.pushKV("success", res);
2454
1.09k
        result.pushKV("txouts", count);
2455
1.09k
        result.pushKV("height", tip->nHeight);
2456
1.09k
        result.pushKV("bestblock", tip->GetBlockHash().GetHex());
2457
2458
168k
        for (const auto& it : coins) {
2459
168k
            const COutPoint& outpoint = it.first;
2460
168k
            const Coin& coin = it.second;
2461
168k
            const CTxOut& txo = coin.out;
2462
168k
            const CBlockIndex& coinb_block{*CHECK_NONFATAL(tip->GetAncestor(coin.nHeight))};
2463
168k
            input_txos.push_back(txo);
2464
168k
            total_in += txo.nValue;
2465
2466
168k
            UniValue unspent(UniValue::VOBJ);
2467
168k
            unspent.pushKV("txid", outpoint.hash.GetHex());
2468
168k
            unspent.pushKV("vout", outpoint.n);
2469
168k
            unspent.pushKV("scriptPubKey", HexStr(txo.scriptPubKey));
2470
168k
            unspent.pushKV("desc", descriptors[txo.scriptPubKey]);
2471
168k
            unspent.pushKV("amount", ValueFromAmount(txo.nValue));
2472
168k
            unspent.pushKV("coinbase", coin.IsCoinBase());
2473
168k
            unspent.pushKV("height", coin.nHeight);
2474
168k
            unspent.pushKV("blockhash", coinb_block.GetBlockHash().GetHex());
2475
168k
            unspent.pushKV("confirmations", tip->nHeight - coin.nHeight + 1);
2476
2477
168k
            unspents.push_back(std::move(unspent));
2478
168k
        }
2479
1.09k
        result.pushKV("unspents", std::move(unspents));
2480
1.09k
        result.pushKV("total_amount", ValueFromAmount(total_in));
2481
1.09k
    } else {
2482
1
        throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid action '%s'", action));
2483
1
    }
2484
1.09k
    return result;
2485
1.09k
},
2486
3.46k
    };
2487
3.46k
}
2488
2489
/** RAII object to prevent concurrency issue when scanning blockfilters */
2490
static std::atomic<int> g_scanfilter_progress;
2491
static std::atomic<int> g_scanfilter_progress_height;
2492
static std::atomic<bool> g_scanfilter_in_progress;
2493
static std::atomic<bool> g_scanfilter_should_abort_scan;
2494
class BlockFiltersScanReserver
2495
{
2496
private:
2497
    bool m_could_reserve{false};
2498
public:
2499
19
    explicit BlockFiltersScanReserver() = default;
2500
2501
19
    bool reserve() {
2502
19
        CHECK_NONFATAL(!m_could_reserve);
2503
19
        if (g_scanfilter_in_progress.exchange(true)) {
2504
0
            return false;
2505
0
        }
2506
19
        m_could_reserve = true;
2507
19
        return true;
2508
19
    }
2509
2510
19
    ~BlockFiltersScanReserver() {
2511
19
        if (m_could_reserve) {
2512
19
            g_scanfilter_in_progress = false;
2513
19
        }
2514
19
    }
2515
};
2516
2517
static bool CheckBlockFilterMatches(BlockManager& blockman, const CBlockIndex& blockindex, const GCSFilter::ElementSet& needles)
2518
3
{
2519
3
    const CBlock block{GetBlockChecked(blockman, blockindex)};
2520
3
    const CBlockUndo block_undo{GetUndoChecked(blockman, blockindex)};
2521
2522
    // Check if any of the outputs match the scriptPubKey
2523
5
    for (const auto& tx : block.vtx) {
2524
8
        if (std::any_of(tx->vout.cbegin(), tx->vout.cend(), [&](const auto& txout) {
2525
8
                return needles.contains(std::vector<unsigned char>(txout.scriptPubKey.begin(), txout.scriptPubKey.end()));
2526
8
            })) {
2527
2
            return true;
2528
2
        }
2529
5
    }
2530
    // Check if any of the inputs match the scriptPubKey
2531
1
    for (const auto& txundo : block_undo.vtxundo) {
2532
0
        if (std::any_of(txundo.vprevout.cbegin(), txundo.vprevout.cend(), [&](const auto& coin) {
2533
0
                return needles.contains(std::vector<unsigned char>(coin.out.scriptPubKey.begin(), coin.out.scriptPubKey.end()));
2534
0
            })) {
2535
0
            return true;
2536
0
        }
2537
0
    }
2538
2539
1
    return false;
2540
1
}
2541
2542
static RPCMethod scanblocks()
2543
2.38k
{
2544
2.38k
    return RPCMethod{
2545
2.38k
        "scanblocks",
2546
2.38k
        "Return relevant blockhashes for given descriptors (requires blockfilterindex).\n"
2547
2.38k
        "This call may take several minutes. Make sure to use no RPC timeout (bitcoin-cli -rpcclienttimeout=0)",
2548
2.38k
        {
2549
2.38k
            scan_action_arg_desc,
2550
2.38k
            scan_objects_arg_desc,
2551
2.38k
            RPCArg{"start_height", RPCArg::Type::NUM, RPCArg::Default{0}, "Height to start to scan from"},
2552
2.38k
            RPCArg{"stop_height", RPCArg::Type::NUM, RPCArg::DefaultHint{"chain tip"}, "Height to stop to scan"},
2553
2.38k
            RPCArg{"filtertype", RPCArg::Type::STR, RPCArg::Default{BlockFilterTypeName(BlockFilterType::BASIC)}, "The type name of the filter"},
2554
2.38k
            RPCArg{"options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "",
2555
2.38k
                {
2556
2.38k
                    {"filter_false_positives", RPCArg::Type::BOOL, RPCArg::Default{false}, "Filter false positives (slower and may fail on pruned nodes). Otherwise they may occur at a rate of 1/M"},
2557
2.38k
                },
2558
2.38k
                RPCArgOptions{.oneline_description="options"}},
2559
2.38k
        },
2560
2.38k
        {
2561
2.38k
            scan_result_status_none,
2562
2.38k
            RPCResult{"When action=='start'; only returns after scan completes", RPCResult::Type::OBJ, "", "", {
2563
2.38k
                {RPCResult::Type::NUM, "from_height", "The height we started the scan from"},
2564
2.38k
                {RPCResult::Type::NUM, "to_height", "The height we ended the scan at"},
2565
2.38k
                {RPCResult::Type::ARR, "relevant_blocks", "Blocks that may have matched a scanobject.", {
2566
2.38k
                    {RPCResult::Type::STR_HEX, "blockhash", "A relevant blockhash"},
2567
2.38k
                }},
2568
2.38k
                {RPCResult::Type::BOOL, "completed", "true if the scan process was not aborted"}
2569
2.38k
            }},
2570
2.38k
            RPCResult{"when action=='status' and a scan is currently in progress", RPCResult::Type::OBJ, "", "", {
2571
2.38k
                    {RPCResult::Type::NUM, "progress", "Approximate percent complete"},
2572
2.38k
                    {RPCResult::Type::NUM, "current_height", "Height of the block currently being scanned"},
2573
2.38k
                },
2574
2.38k
            },
2575
2.38k
            scan_result_abort,
2576
2.38k
        },
2577
2.38k
        RPCExamples{
2578
2.38k
            HelpExampleCli("scanblocks", "start '[\"addr(bcrt1q4u4nsgk6ug0sqz7r3rj9tykjxrsl0yy4d0wwte)\"]' 300000") +
2579
2.38k
            HelpExampleCli("scanblocks", "start '[\"addr(bcrt1q4u4nsgk6ug0sqz7r3rj9tykjxrsl0yy4d0wwte)\"]' 100 150 basic") +
2580
2.38k
            HelpExampleCli("scanblocks", "status") +
2581
2.38k
            HelpExampleRpc("scanblocks", "\"start\", [\"addr(bcrt1q4u4nsgk6ug0sqz7r3rj9tykjxrsl0yy4d0wwte)\"], 300000") +
2582
2.38k
            HelpExampleRpc("scanblocks", "\"start\", [\"addr(bcrt1q4u4nsgk6ug0sqz7r3rj9tykjxrsl0yy4d0wwte)\"], 100, 150, \"basic\"") +
2583
2.38k
            HelpExampleRpc("scanblocks", "\"status\"")
2584
2.38k
        },
2585
2.38k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
2586
2.38k
{
2587
20
    UniValue ret(UniValue::VOBJ);
2588
20
    auto action{self.Arg<std::string_view>("action")};
2589
20
    if (action == "status") {
2590
1
        BlockFiltersScanReserver reserver;
2591
1
        if (reserver.reserve()) {
2592
            // no scan in progress
2593
1
            return NullUniValue;
2594
1
        }
2595
0
        ret.pushKV("progress", g_scanfilter_progress.load());
2596
0
        ret.pushKV("current_height", g_scanfilter_progress_height.load());
2597
0
        return ret;
2598
19
    } else if (action == "abort") {
2599
1
        BlockFiltersScanReserver reserver;
2600
1
        if (reserver.reserve()) {
2601
            // reserve was possible which means no scan was running
2602
1
            return false;
2603
1
        }
2604
        // set the abort flag
2605
0
        g_scanfilter_should_abort_scan = true;
2606
0
        return true;
2607
18
    } else if (action == "start") {
2608
17
        BlockFiltersScanReserver reserver;
2609
17
        if (!reserver.reserve()) {
2610
0
            throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan already in progress, use action \"abort\" or \"status\"");
2611
0
        }
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 : request.params[1].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
20
},
2728
2.38k
    };
2729
2.38k
}
2730
2731
static RPCMethod getdescriptoractivity()
2732
2.38k
{
2733
2.38k
    return RPCMethod{
2734
2.38k
        "getdescriptoractivity",
2735
2.38k
        "Get spend and receive activity associated with a set of descriptors for a set of blocks. "
2736
2.38k
        "This command pairs well with the `relevant_blocks` output of `scanblocks()`.\n"
2737
2.38k
        "This call may take several minutes. If you encounter timeouts, try specifying no RPC timeout (bitcoin-cli -rpcclienttimeout=0)",
2738
2.38k
        {
2739
2.38k
            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.38k
                {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "A valid blockhash"},
2741
2.38k
            }},
2742
2.38k
            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.38k
                {
2744
2.38k
                    {"descriptor", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "An output descriptor"},
2745
2.38k
                    output_descriptor_obj,
2746
2.38k
                },
2747
2.38k
                RPCArgOptions{.oneline_description="[scanobjects,...]"},
2748
2.38k
            },
2749
2.38k
            {"include_mempool", RPCArg::Type::BOOL, RPCArg::Default{true}, "Whether to include unconfirmed activity"},
2750
2.38k
        },
2751
2.38k
        RPCResult{
2752
2.38k
            RPCResult::Type::OBJ, "", "", {
2753
2.38k
                {RPCResult::Type::ARR, "activity", "events", {
2754
2.38k
                    {RPCResult::Type::OBJ, "", "", {
2755
2.38k
                        {RPCResult::Type::STR, "type", "always 'spend'"},
2756
2.38k
                        {RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " of the spent output"},
2757
2.38k
                        {RPCResult::Type::STR_HEX, "blockhash", /*optional=*/true, "The blockhash this spend appears in (omitted if unconfirmed)"},
2758
2.38k
                        {RPCResult::Type::NUM, "height", /*optional=*/true, "Height of the spend (omitted if unconfirmed)"},
2759
2.38k
                        {RPCResult::Type::STR_HEX, "spend_txid", "The txid of the spending transaction"},
2760
2.38k
                        {RPCResult::Type::NUM, "spend_vin", "The input index of the spend"},
2761
2.38k
                        {RPCResult::Type::STR_HEX, "prevout_txid", "The txid of the prevout"},
2762
2.38k
                        {RPCResult::Type::NUM, "prevout_vout", "The vout of the prevout"},
2763
2.38k
                        {RPCResult::Type::OBJ, "prevout_spk", "", ScriptPubKeyDoc()},
2764
2.38k
                    }},
2765
2.38k
                    {RPCResult::Type::OBJ, "", "", {
2766
2.38k
                        {RPCResult::Type::STR, "type", "always 'receive'"},
2767
2.38k
                        {RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " of the new output"},
2768
2.38k
                        {RPCResult::Type::STR_HEX, "blockhash", /*optional=*/true, "The block that this receive is in (omitted if unconfirmed)"},
2769
2.38k
                        {RPCResult::Type::NUM, "height", /*optional=*/true, "The height of the receive (omitted if unconfirmed)"},
2770
2.38k
                        {RPCResult::Type::STR_HEX, "txid", "The txid of the receiving transaction"},
2771
2.38k
                        {RPCResult::Type::NUM, "vout", "The vout of the receiving output"},
2772
2.38k
                        {RPCResult::Type::OBJ, "output_spk", "", ScriptPubKeyDoc()},
2773
2.38k
                    }},
2774
                    // TODO is the skip_type_check avoidable with a heterogeneous ARR?
2775
2.38k
                }, {.skip_type_check=true}, },
2776
2.38k
            },
2777
2.38k
        },
2778
2.38k
        RPCExamples{
2779
2.38k
            HelpExampleCli("getdescriptoractivity", "'[\"000000000000000000001347062c12fded7c528943c8ce133987e2e2f5a840ee\"]' '[\"addr(bc1qzl6nsgqzu89a66l50cvwapnkw5shh23zarqkw9)\"]'")
2780
2.38k
        },
2781
2.38k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
2782
2.38k
{
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.38k
    };
2965
2.38k
}
2966
2967
static RPCMethod getblockfilter()
2968
2.38k
{
2969
2.38k
    return RPCMethod{
2970
2.38k
        "getblockfilter",
2971
2.38k
        "Retrieve a BIP 157 content filter for a particular block.\n",
2972
2.38k
                {
2973
2.38k
                    {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hash of the block"},
2974
2.38k
                    {"filtertype", RPCArg::Type::STR, RPCArg::Default{BlockFilterTypeName(BlockFilterType::BASIC)}, "The type name of the filter"},
2975
2.38k
                },
2976
2.38k
                RPCResult{
2977
2.38k
                    RPCResult::Type::OBJ, "", "",
2978
2.38k
                    {
2979
2.38k
                        {RPCResult::Type::STR_HEX, "filter", "the hex-encoded filter data"},
2980
2.38k
                        {RPCResult::Type::STR_HEX, "header", "the hex-encoded filter header"},
2981
2.38k
                    }},
2982
2.38k
                RPCExamples{
2983
2.38k
                    HelpExampleCli("getblockfilter", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" \"basic\"") +
2984
2.38k
                    HelpExampleRpc("getblockfilter", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\", \"basic\"")
2985
2.38k
                },
2986
2.38k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
2987
2.38k
{
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.38k
    };
3042
2.38k
}
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.38k
{
3074
2.38k
    return RPCMethod{
3075
2.38k
        "dumptxoutset",
3076
2.38k
        "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.38k
        "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.38k
        "For deep rollbacks, make sure to use no RPC timeout (bitcoin-cli -rpcclienttimeout=0) as it may take several minutes.",
3079
2.38k
        {
3080
2.38k
            {"path", RPCArg::Type::STR, RPCArg::Optional::NO, "Path to the output file. If relative, will be prefixed by datadir."},
3081
2.38k
            {"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.38k
            {"options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "",
3083
2.38k
                {
3084
2.38k
                    {"rollback", RPCArg::Type::NUM, RPCArg::Optional::OMITTED,
3085
2.38k
                        "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.38k
                    RPCArgOptions{.skip_type_check = true, .type_str = {"", "string or numeric"}}},
3087
2.38k
                    {"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.38k
                },
3089
2.38k
            },
3090
2.38k
        },
3091
2.38k
        RPCResult{
3092
2.38k
            RPCResult::Type::OBJ, "", "",
3093
2.38k
                {
3094
2.38k
                    {RPCResult::Type::NUM, "coins_written", "the number of coins written in the snapshot"},
3095
2.38k
                    {RPCResult::Type::STR_HEX, "base_hash", "the hash of the base of the snapshot"},
3096
2.38k
                    {RPCResult::Type::NUM, "base_height", "the height of the base of the snapshot"},
3097
2.38k
                    {RPCResult::Type::STR, "path", "the absolute path that the snapshot was written to"},
3098
2.38k
                    {RPCResult::Type::STR_HEX, "txoutset_hash", "the hash of the UTXO set contents"},
3099
2.38k
                    {RPCResult::Type::NUM, "nchaintx", "the number of transactions in the chain up to and including the base block"},
3100
2.38k
                }
3101
2.38k
        },
3102
2.38k
        RPCExamples{
3103
2.38k
            HelpExampleCli("-rpcclienttimeout=0 dumptxoutset", "utxo.dat latest") +
3104
2.38k
            HelpExampleCli("-rpcclienttimeout=0 dumptxoutset", "utxo.dat rollback") +
3105
2.38k
            HelpExampleCli("-rpcclienttimeout=0 -named dumptxoutset", R"(utxo.dat rollback=853456)") +
3106
2.38k
            HelpExampleCli("-rpcclienttimeout=0 -named dumptxoutset", R"(utxo.dat rollback=853456 in_memory=true)")
3107
2.38k
        },
3108
2.38k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
3109
2.38k
{
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.38k
    };
3189
2.38k
}
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.get(),
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
7
    if (!pcursor) {
3337
0
        throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to create UTXO cursor");
3338
0
    }
3339
3340
7
    LogInfo("Writing snapshot to disk.");
3341
7
    return WriteUTXOSnapshot(chainstate,
3342
7
                             pcursor.get(),
3343
7
                             &(*maybe_stats),
3344
7
                             target,
3345
7
                             std::move(afile),
3346
7
                             path,
3347
7
                             tmppath,
3348
7
                             node.rpc_interruption_point);
3349
7
}
3350
3351
std::tuple<std::unique_ptr<CCoinsViewCursor>, CCoinsStats, const CBlockIndex*>
3352
PrepareUTXOSnapshot(
3353
    Chainstate& chainstate,
3354
    const std::function<void()>& interruption_point)
3355
37
{
3356
37
    std::unique_ptr<CCoinsViewCursor> pcursor;
3357
37
    std::optional<CCoinsStats> maybe_stats;
3358
37
    const CBlockIndex* tip;
3359
3360
37
    {
3361
        // We need to lock cs_main to ensure that the coinsdb isn't written to
3362
        // between (i) flushing coins cache to disk (coinsdb), (ii) getting stats
3363
        // based upon the coinsdb, and (iii) constructing a cursor to the
3364
        // coinsdb for use in WriteUTXOSnapshot.
3365
        //
3366
        // Cursors returned by leveldb iterate over snapshots, so the contents
3367
        // of the pcursor will not be affected by simultaneous writes during
3368
        // use below this block.
3369
        //
3370
        // See discussion here:
3371
        //   https://github.com/bitcoin/bitcoin/pull/15606#discussion_r274479369
3372
        //
3373
37
        AssertLockHeld(::cs_main);
3374
3375
37
        chainstate.ForceFlushStateToDisk(/*wipe_cache=*/false);
3376
3377
37
        maybe_stats = GetUTXOStats(&chainstate.CoinsDB(), chainstate.m_blockman, CoinStatsHashType::HASH_SERIALIZED, interruption_point);
3378
37
        if (!maybe_stats) {
3379
0
            throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
3380
0
        }
3381
3382
37
        pcursor = chainstate.CoinsDB().Cursor();
3383
37
        tip = CHECK_NONFATAL(chainstate.m_blockman.LookupBlockIndex(maybe_stats->hashBlock));
3384
37
    }
3385
3386
37
    return {std::move(pcursor), *CHECK_NONFATAL(maybe_stats), tip};
3387
37
}
3388
3389
UniValue WriteUTXOSnapshot(
3390
    Chainstate& chainstate,
3391
    CCoinsViewCursor* pcursor,
3392
    CCoinsStats* maybe_stats,
3393
    const CBlockIndex* tip,
3394
    AutoFile&& afile,
3395
    const fs::path& path,
3396
    const fs::path& temppath,
3397
    const std::function<void()>& interruption_point)
3398
44
{
3399
44
    LOG_TIME_SECONDS(strprintf("writing UTXO snapshot at height %s (%s) to file %s (via %s)",
3400
44
        tip->nHeight, tip->GetBlockHash().ToString(),
3401
44
        fs::PathToString(path), fs::PathToString(temppath)));
3402
3403
44
    SnapshotMetadata metadata{chainstate.m_chainman.GetParams().MessageStart(), tip->GetBlockHash(), maybe_stats->coins_count};
3404
3405
44
    afile << metadata;
3406
3407
44
    COutPoint key;
3408
44
    Txid last_hash;
3409
44
    Coin coin;
3410
44
    unsigned int iter{0};
3411
44
    size_t written_coins_count{0};
3412
44
    std::vector<std::pair<uint32_t, Coin>> coins;
3413
3414
    // To reduce space the serialization format of the snapshot avoids
3415
    // duplication of tx hashes. The code takes advantage of the guarantee by
3416
    // leveldb that keys are lexicographically sorted.
3417
    // In the coins vector we collect all coins that belong to a certain tx hash
3418
    // (key.hash) and when we have them all (key.hash != last_hash) we write
3419
    // them to file using the below lambda function.
3420
    // See also https://github.com/bitcoin/bitcoin/issues/25675
3421
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) {
3422
6.55k
        afile << last_hash;
3423
6.55k
        WriteCompactSize(afile, coins.size());
3424
6.58k
        for (const auto& [n, coin] : coins) {
3425
6.58k
            WriteCompactSize(afile, n);
3426
6.58k
            afile << coin;
3427
6.58k
            ++written_coins_count;
3428
6.58k
        }
3429
6.55k
    };
3430
3431
44
    pcursor->GetKey(key);
3432
44
    last_hash = key.hash;
3433
6.62k
    while (pcursor->Valid()) {
3434
6.58k
        if (iter % 5000 == 0) interruption_point();
3435
6.58k
        ++iter;
3436
6.58k
        if (pcursor->GetKey(key) && pcursor->GetValue(coin)) {
3437
6.58k
            if (key.hash != last_hash) {
3438
6.51k
                write_coins_to_file(afile, last_hash, coins, written_coins_count);
3439
6.51k
                last_hash = key.hash;
3440
6.51k
                coins.clear();
3441
6.51k
            }
3442
6.58k
            coins.emplace_back(key.n, coin);
3443
6.58k
        }
3444
6.58k
        pcursor->Next();
3445
6.58k
    }
3446
3447
44
    if (!coins.empty()) {
3448
44
        write_coins_to_file(afile, last_hash, coins, written_coins_count);
3449
44
    }
3450
3451
44
    CHECK_NONFATAL(written_coins_count == maybe_stats->coins_count);
3452
3453
44
    if (afile.fclose() != 0) {
3454
0
        throw std::ios_base::failure(
3455
0
            strprintf("Error closing %s: %s", fs::PathToString(temppath), SysErrorString(errno)));
3456
0
    }
3457
3458
44
    UniValue result(UniValue::VOBJ);
3459
44
    result.pushKV("coins_written", written_coins_count);
3460
44
    result.pushKV("base_hash", tip->GetBlockHash().ToString());
3461
44
    result.pushKV("base_height", tip->nHeight);
3462
44
    result.pushKV("path", path.utf8string());
3463
44
    result.pushKV("txoutset_hash", maybe_stats->hashSerialized.ToString());
3464
44
    result.pushKV("nchaintx", tip->m_chain_tx_count);
3465
44
    return result;
3466
44
}
3467
3468
UniValue CreateUTXOSnapshot(
3469
    node::NodeContext& node,
3470
    Chainstate& chainstate,
3471
    AutoFile&& afile,
3472
    const fs::path& path,
3473
    const fs::path& tmppath)
3474
37
{
3475
37
    auto [cursor, stats, tip]{WITH_LOCK(::cs_main, return PrepareUTXOSnapshot(chainstate, node.rpc_interruption_point))};
3476
37
    return WriteUTXOSnapshot(chainstate,
3477
37
                             cursor.get(),
3478
37
                             &stats,
3479
37
                             tip,
3480
37
                             std::move(afile),
3481
37
                             path,
3482
37
                             tmppath,
3483
37
                             node.rpc_interruption_point);
3484
37
}
3485
3486
static RPCMethod loadtxoutset()
3487
2.40k
{
3488
2.40k
    return RPCMethod{
3489
2.40k
        "loadtxoutset",
3490
2.40k
        "Load the serialized UTXO set from a file.\n"
3491
2.40k
        "Once this snapshot is loaded, its contents will be "
3492
2.40k
        "deserialized into a second chainstate data structure, which is then used to sync to "
3493
2.40k
        "the network's tip. "
3494
2.40k
        "Meanwhile, the original chainstate will complete the initial block download process in "
3495
2.40k
        "the background, eventually validating up to the block that the snapshot is based upon.\n\n"
3496
3497
2.40k
        "The result is a usable bitcoind instance that is current with the network tip in a "
3498
2.40k
        "matter of minutes rather than hours. UTXO snapshot are typically obtained from "
3499
2.40k
        "third-party sources (HTTP, torrent, etc.) which is reasonable since their "
3500
2.40k
        "contents are always checked by hash.\n\n"
3501
3502
2.40k
        "You can find more information on this process in the `assumeutxo` design "
3503
2.40k
        "document (<https://github.com/bitcoin/bitcoin/blob/master/doc/design/assumeutxo.md>).",
3504
2.40k
        {
3505
2.40k
            {"path",
3506
2.40k
                RPCArg::Type::STR,
3507
2.40k
                RPCArg::Optional::NO,
3508
2.40k
                "path to the snapshot file. If relative, will be prefixed by datadir."},
3509
2.40k
        },
3510
2.40k
        RPCResult{
3511
2.40k
            RPCResult::Type::OBJ, "", "",
3512
2.40k
                {
3513
2.40k
                    {RPCResult::Type::NUM, "coins_loaded", "the number of coins loaded from the snapshot"},
3514
2.40k
                    {RPCResult::Type::STR_HEX, "tip_hash", "the hash of the base of the snapshot"},
3515
2.40k
                    {RPCResult::Type::NUM, "base_height", "the height of the base of the snapshot"},
3516
2.40k
                    {RPCResult::Type::STR, "path", "the absolute path that the snapshot was loaded from"},
3517
2.40k
                }
3518
2.40k
        },
3519
2.40k
        RPCExamples{
3520
2.40k
            HelpExampleCli("-rpcclienttimeout=0 loadtxoutset", "utxo.dat")
3521
2.40k
        },
3522
2.40k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
3523
2.40k
{
3524
41
    NodeContext& node = EnsureAnyNodeContext(request.context);
3525
41
    ChainstateManager& chainman = EnsureChainman(node);
3526
41
    const fs::path path{AbsPathForConfigVal(EnsureArgsman(node), fs::u8path(self.Arg<std::string_view>("path")))};
3527
3528
41
    FILE* file{fsbridge::fopen(path, "rb")};
3529
41
    AutoFile afile{file};
3530
41
    if (afile.IsNull()) {
3531
1
        throw JSONRPCError(
3532
1
            RPC_INVALID_PARAMETER,
3533
1
            "Couldn't open file " + path.utf8string() + " for reading.");
3534
1
    }
3535
3536
40
    SnapshotMetadata metadata{chainman.GetParams().MessageStart()};
3537
40
    try {
3538
40
        afile >> metadata;
3539
40
    } catch (const std::ios_base::failure& e) {
3540
9
        throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("Unable to parse metadata: %s", e.what()));
3541
9
    }
3542
3543
31
    auto activation_result{chainman.ActivateSnapshot(afile, metadata, false)};
3544
31
    if (!activation_result) {
3545
22
        throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf("Unable to load UTXO snapshot: %s. (%s)", util::ErrorString(activation_result).original, path.utf8string()));
3546
22
    }
3547
3548
    // Because we can't provide historical blocks during tip or background sync.
3549
    // Update local services to reflect we are a limited peer until we are fully sync.
3550
9
    node.connman->RemoveLocalServices(NODE_NETWORK);
3551
    // Setting the limited state is usually redundant because the node can always
3552
    // provide the last 288 blocks, but it doesn't hurt to set it.
3553
9
    node.connman->AddLocalServices(NODE_NETWORK_LIMITED);
3554
3555
9
    CBlockIndex& snapshot_index{*CHECK_NONFATAL(*activation_result)};
3556
3557
9
    UniValue result(UniValue::VOBJ);
3558
9
    result.pushKV("coins_loaded", metadata.m_coins_count);
3559
9
    result.pushKV("tip_hash", snapshot_index.GetBlockHash().ToString());
3560
9
    result.pushKV("base_height", snapshot_index.nHeight);
3561
9
    result.pushKV("path", fs::PathToString(path));
3562
9
    return result;
3563
31
},
3564
2.40k
    };
3565
2.40k
}
3566
3567
const std::vector<RPCResult> RPCHelpForChainstate{
3568
    {RPCResult::Type::NUM, "blocks", "number of blocks in this chainstate"},
3569
    {RPCResult::Type::STR_HEX, "bestblockhash", "blockhash of the tip"},
3570
    {RPCResult::Type::STR_HEX, "bits", "nBits: compact representation of the block difficulty target"},
3571
    {RPCResult::Type::STR_HEX, "target", "The difficulty target"},
3572
    {RPCResult::Type::NUM, "difficulty", "difficulty of the tip"},
3573
    {RPCResult::Type::NUM, "verificationprogress", "progress towards the network tip"},
3574
    {RPCResult::Type::STR_HEX, "snapshot_blockhash", /*optional=*/true, "the base block of the snapshot this chainstate is based on, if any"},
3575
    {RPCResult::Type::NUM, "coins_db_cache_bytes", "size of the coinsdb cache"},
3576
    {RPCResult::Type::NUM, "coins_tip_cache_bytes", "size of the coinstip cache"},
3577
    {RPCResult::Type::BOOL, "validated", "whether the chainstate is fully validated. True if all blocks in the chainstate were validated, false if the chain is based on a snapshot and the snapshot has not yet been validated."},
3578
};
3579
3580
static RPCMethod getchainstates()
3581
2.48k
{
3582
2.48k
return RPCMethod{
3583
2.48k
        "getchainstates",
3584
2.48k
        "Return information about chainstates.\n",
3585
2.48k
        {},
3586
2.48k
        RPCResult{
3587
2.48k
            RPCResult::Type::OBJ, "", "", {
3588
2.48k
                {RPCResult::Type::NUM, "headers", "the number of headers seen so far"},
3589
2.48k
                {RPCResult::Type::ARR, "chainstates", "list of the chainstates ordered by work, with the most-work (active) chainstate last", {{RPCResult::Type::OBJ, "", "", RPCHelpForChainstate},}},
3590
2.48k
            }
3591
2.48k
        },
3592
2.48k
        RPCExamples{
3593
2.48k
            HelpExampleCli("getchainstates", "")
3594
2.48k
    + HelpExampleRpc("getchainstates", "")
3595
2.48k
        },
3596
2.48k
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
3597
2.48k
{
3598
118
    LOCK(cs_main);
3599
118
    UniValue obj(UniValue::VOBJ);
3600
3601
118
    ChainstateManager& chainman = EnsureAnyChainman(request.context);
3602
3603
224
    auto make_chain_data = [&](const Chainstate& cs) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
3604
224
        AssertLockHeld(::cs_main);
3605
224
        UniValue data(UniValue::VOBJ);
3606
224
        if (!cs.m_chain.Tip()) {
3607
0
            return data;
3608
0
        }
3609
224
        const CChain& chain = cs.m_chain;
3610
224
        const CBlockIndex* tip = chain.Tip();
3611
3612
224
        data.pushKV("blocks", chain.Height());
3613
224
        data.pushKV("bestblockhash",         tip->GetBlockHash().GetHex());
3614
224
        data.pushKV("bits", strprintf("%08x", tip->nBits));
3615
224
        data.pushKV("target", GetTarget(*tip, chainman.GetConsensus().powLimit).GetHex());
3616
224
        data.pushKV("difficulty", GetDifficulty(*tip));
3617
224
        data.pushKV("verificationprogress", chainman.GuessVerificationProgress(tip));
3618
224
        data.pushKV("coins_db_cache_bytes",  cs.m_coinsdb_cache_size_bytes);
3619
224
        data.pushKV("coins_tip_cache_bytes", cs.m_coinstip_cache_size_bytes);
3620
224
        if (cs.m_from_snapshot_blockhash) {
3621
112
            data.pushKV("snapshot_blockhash", cs.m_from_snapshot_blockhash->ToString());
3622
112
        }
3623
224
        data.pushKV("validated", cs.m_assumeutxo == Assumeutxo::VALIDATED);
3624
224
        return data;
3625
224
    };
3626
3627
118
    obj.pushKV("headers", chainman.m_best_header ? chainman.m_best_header->nHeight : -1);
3628
118
    UniValue obj_chainstates{UniValue::VARR};
3629
118
    if (const Chainstate * cs{chainman.HistoricalChainstate()}) {
3630
106
        obj_chainstates.push_back(make_chain_data(*cs));
3631
106
    }
3632
118
    obj_chainstates.push_back(make_chain_data(chainman.CurrentChainstate()));
3633
118
    obj.pushKV("chainstates", std::move(obj_chainstates));
3634
118
    return obj;
3635
118
}
3636
2.48k
    };
3637
2.48k
}
3638
3639
3640
void RegisterBlockchainRPCCommands(CRPCTable& t)
3641
1.29k
{
3642
1.29k
    static const CRPCCommand commands[]{
3643
1.29k
        {"blockchain", &getblockchaininfo},
3644
1.29k
        {"blockchain", &getchaintxstats},
3645
1.29k
        {"blockchain", &getblockstats},
3646
1.29k
        {"blockchain", &getbestblockhash},
3647
1.29k
        {"blockchain", &getblockcount},
3648
1.29k
        {"blockchain", &getblock},
3649
1.29k
        {"blockchain", &getblockfrompeer},
3650
1.29k
        {"blockchain", &getblockhash},
3651
1.29k
        {"blockchain", &getblockheader},
3652
1.29k
        {"blockchain", &getchaintips},
3653
1.29k
        {"blockchain", &getdifficulty},
3654
1.29k
        {"blockchain", &getdeploymentinfo},
3655
1.29k
        {"blockchain", &gettxout},
3656
1.29k
        {"blockchain", &gettxoutsetinfo},
3657
1.29k
        {"blockchain", &pruneblockchain},
3658
1.29k
        {"blockchain", &verifychain},
3659
1.29k
        {"blockchain", &preciousblock},
3660
1.29k
        {"blockchain", &scantxoutset},
3661
1.29k
        {"blockchain", &scanblocks},
3662
1.29k
        {"blockchain", &getdescriptoractivity},
3663
1.29k
        {"blockchain", &getblockfilter},
3664
1.29k
        {"blockchain", &dumptxoutset},
3665
1.29k
        {"blockchain", &loadtxoutset},
3666
1.29k
        {"blockchain", &getchainstates},
3667
1.29k
        {"hidden", &invalidateblock},
3668
1.29k
        {"hidden", &reconsiderblock},
3669
1.29k
        {"blockchain", &waitfornewblock},
3670
1.29k
        {"blockchain", &waitforblock},
3671
1.29k
        {"blockchain", &waitforblockheight},
3672
1.29k
        {"hidden", &syncwithvalidationinterfacequeue},
3673
1.29k
    };
3674
38.8k
    for (const auto& c : commands) {
3675
38.8k
        t.appendCommand(c.name, &c);
3676
38.8k
    }
3677
1.29k
}