Coverage Report

Created: 2026-09-14 20:36

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