Coverage Report

Created: 2026-04-29 19:21

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