Coverage Report

Created: 2026-04-20 22:07

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