Coverage Report

Created: 2026-10-07 13:27

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