Coverage Report

Created: 2026-07-23 20:35

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