Bitcoin Core Fuzz Coverage Report for wallet_tx_can_be_bumped

Coverage Report

Created: 2025-11-19 11:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/Users/brunogarcia/projects/bitcoin-core-dev/src/rpc/mempool.cpp
Line
Count
Source
1
// Copyright (c) 2010 Satoshi Nakamoto
2
// Copyright (c) 2009-2022 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 <node/mempool_persist.h>
9
10
#include <chainparams.h>
11
#include <consensus/validation.h>
12
#include <core_io.h>
13
#include <kernel/mempool_entry.h>
14
#include <net_processing.h>
15
#include <node/mempool_persist_args.h>
16
#include <node/types.h>
17
#include <policy/rbf.h>
18
#include <policy/settings.h>
19
#include <primitives/transaction.h>
20
#include <rpc/server.h>
21
#include <rpc/server_util.h>
22
#include <rpc/util.h>
23
#include <txmempool.h>
24
#include <univalue.h>
25
#include <util/fs.h>
26
#include <util/moneystr.h>
27
#include <util/strencodings.h>
28
#include <util/time.h>
29
#include <util/vector.h>
30
31
#include <string_view>
32
#include <utility>
33
34
using node::DumpMempool;
35
36
using node::DEFAULT_MAX_BURN_AMOUNT;
37
using node::DEFAULT_MAX_RAW_TX_FEE_RATE;
38
using node::MempoolPath;
39
using node::NodeContext;
40
using node::TransactionError;
41
using util::ToString;
42
43
static RPCHelpMan sendrawtransaction()
44
0
{
45
0
    return RPCHelpMan{
46
0
        "sendrawtransaction",
47
0
        "Submit a raw transaction (serialized, hex-encoded) to local node and network.\n"
48
0
        "\nThe transaction will be sent unconditionally to all peers, so using sendrawtransaction\n"
49
0
        "for manual rebroadcast may degrade privacy by leaking the transaction's origin, as\n"
50
0
        "nodes will normally not rebroadcast non-wallet transactions already in their mempool.\n"
51
0
        "\nA specific exception, RPC_TRANSACTION_ALREADY_IN_UTXO_SET, may throw if the transaction cannot be added to the mempool.\n"
52
0
        "\nRelated RPCs: createrawtransaction, signrawtransactionwithkey\n",
53
0
        {
54
0
            {"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
55
0
            {"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},
56
0
             "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
57
0
                 "/kvB.\nFee rates larger than 1BTC/kvB are rejected.\nSet to 0 to accept any fee rate."},
58
0
            {"maxburnamount", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_BURN_AMOUNT)},
59
0
             "Reject transactions with provably unspendable outputs (e.g. 'datacarrier' outputs that use the OP_RETURN opcode) greater than the specified value, expressed in " + CURRENCY_UNIT + ".\n"
60
0
             "If burning funds through unspendable outputs is desired, increase this value.\n"
61
0
             "This check is based on heuristics and does not guarantee spendability of outputs.\n"},
62
0
        },
63
0
        RPCResult{
64
0
            RPCResult::Type::STR_HEX, "", "The transaction hash in hex"
65
0
        },
66
0
        RPCExamples{
67
0
            "\nCreate a transaction\n"
68
0
            + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") +
69
0
            "Sign the transaction, and get back the hex\n"
70
0
            + HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") +
71
0
            "\nSend the transaction (signed hex)\n"
72
0
            + HelpExampleCli("sendrawtransaction", "\"signedhex\"") +
73
0
            "\nAs a JSON-RPC call\n"
74
0
            + HelpExampleRpc("sendrawtransaction", "\"signedhex\"")
75
0
                },
76
0
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
77
0
        {
78
0
            const CAmount max_burn_amount = request.params[2].isNull() ? 0 : AmountFromValue(request.params[2]);
79
80
0
            CMutableTransaction mtx;
81
0
            if (!DecodeHexTx(mtx, request.params[0].get_str())) {
82
0
                throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed. Make sure the tx has at least one input.");
83
0
            }
84
85
0
            for (const auto& out : mtx.vout) {
86
0
                if((out.scriptPubKey.IsUnspendable() || !out.scriptPubKey.HasValidOps()) && out.nValue > max_burn_amount) {
87
0
                    throw JSONRPCTransactionError(TransactionError::MAX_BURN_EXCEEDED);
88
0
                }
89
0
            }
90
91
0
            CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
92
93
0
            const CFeeRate max_raw_tx_fee_rate{ParseFeeRate(self.Arg<UniValue>("maxfeerate"))};
94
95
0
            int64_t virtual_size = GetVirtualTransactionSize(*tx);
96
0
            CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size);
97
98
0
            std::string err_string;
99
0
            AssertLockNotHeld(cs_main);
Line
Count
Source
142
0
#define AssertLockNotHeld(cs) AssertLockNotHeldInline(#cs, __FILE__, __LINE__, &cs)
100
0
            NodeContext& node = EnsureAnyNodeContext(request.context);
101
0
            const TransactionError err = BroadcastTransaction(node,
102
0
                                                              tx,
103
0
                                                              err_string,
104
0
                                                              max_raw_tx_fee,
105
0
                                                              node::TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL,
106
0
                                                              /*wait_callback=*/true);
107
0
            if (TransactionError::OK != err) {
108
0
                throw JSONRPCTransactionError(err, err_string);
109
0
            }
110
111
0
            return tx->GetHash().GetHex();
112
0
        },
113
0
    };
114
0
}
115
116
static RPCHelpMan testmempoolaccept()
117
0
{
118
0
    return RPCHelpMan{
119
0
        "testmempoolaccept",
120
0
        "Returns result of mempool acceptance tests indicating if raw transaction(s) (serialized, hex-encoded) would be accepted by mempool.\n"
121
0
        "\nIf multiple transactions are passed in, parents must come before children and package policies apply: the transactions cannot conflict with any mempool transactions or each other.\n"
122
0
        "\nIf one transaction fails, other transactions may not be fully validated (the 'allowed' key will be blank).\n"
123
0
        "\nThe maximum number of transactions allowed is " + ToString(MAX_PACKAGE_COUNT) + ".\n"
124
0
        "\nThis checks if transactions violate the consensus or policy rules.\n"
125
0
        "\nSee sendrawtransaction call.\n",
126
0
        {
127
0
            {"rawtxs", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of hex strings of raw transactions.",
128
0
                {
129
0
                    {"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
130
0
                },
131
0
            },
132
0
            {"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},
133
0
             "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
134
0
                 "/kvB.\nFee rates larger than 1BTC/kvB are rejected.\nSet to 0 to accept any fee rate."},
135
0
        },
136
0
        RPCResult{
137
0
            RPCResult::Type::ARR, "", "The result of the mempool acceptance test for each raw transaction in the input array.\n"
138
0
                                      "Returns results for each transaction in the same order they were passed in.\n"
139
0
                                      "Transactions that cannot be fully validated due to failures in other transactions will not contain an 'allowed' result.\n",
140
0
            {
141
0
                {RPCResult::Type::OBJ, "", "",
142
0
                {
143
0
                    {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
144
0
                    {RPCResult::Type::STR_HEX, "wtxid", "The transaction witness hash in hex"},
145
0
                    {RPCResult::Type::STR, "package-error", /*optional=*/true, "Package validation error, if any (only possible if rawtxs had more than 1 transaction)."},
146
0
                    {RPCResult::Type::BOOL, "allowed", /*optional=*/true, "Whether this tx would be accepted to the mempool and pass client-specified maxfeerate. "
147
0
                                                       "If not present, the tx was not fully validated due to a failure in another tx in the list."},
148
0
                    {RPCResult::Type::NUM, "vsize", /*optional=*/true, "Virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted (only present when 'allowed' is true)"},
149
0
                    {RPCResult::Type::OBJ, "fees", /*optional=*/true, "Transaction fees (only present if 'allowed' is true)",
150
0
                    {
151
0
                        {RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT},
152
0
                        {RPCResult::Type::STR_AMOUNT, "effective-feerate", /*optional=*/false, "the effective feerate in " + CURRENCY_UNIT + " per KvB. May differ from the base feerate if, for example, there are modified fees from prioritisetransaction or a package feerate was used."},
153
0
                        {RPCResult::Type::ARR, "effective-includes", /*optional=*/false, "transactions whose fees and vsizes are included in effective-feerate.",
154
0
                            {RPCResult{RPCResult::Type::STR_HEX, "", "transaction wtxid in hex"},
155
0
                        }},
156
0
                    }},
157
0
                    {RPCResult::Type::STR, "reject-reason", /*optional=*/true, "Rejection reason (only present when 'allowed' is false)"},
158
0
                    {RPCResult::Type::STR, "reject-details", /*optional=*/true, "Rejection details (only present when 'allowed' is false and rejection details exist)"},
159
0
                }},
160
0
            }
161
0
        },
162
0
        RPCExamples{
163
0
            "\nCreate a transaction\n"
164
0
            + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") +
165
0
            "Sign the transaction, and get back the hex\n"
166
0
            + HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") +
167
0
            "\nTest acceptance of the transaction (signed hex)\n"
168
0
            + HelpExampleCli("testmempoolaccept", R"('["signedhex"]')") +
169
0
            "\nAs a JSON-RPC call\n"
170
0
            + HelpExampleRpc("testmempoolaccept", "[\"signedhex\"]")
171
0
                },
172
0
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
173
0
        {
174
0
            const UniValue raw_transactions = request.params[0].get_array();
175
0
            if (raw_transactions.size() < 1 || raw_transactions.size() > MAX_PACKAGE_COUNT) {
176
0
                throw JSONRPCError(RPC_INVALID_PARAMETER,
177
0
                                   "Array must contain between 1 and " + ToString(MAX_PACKAGE_COUNT) + " transactions.");
178
0
            }
179
180
0
            const CFeeRate max_raw_tx_fee_rate{ParseFeeRate(self.Arg<UniValue>("maxfeerate"))};
181
182
0
            std::vector<CTransactionRef> txns;
183
0
            txns.reserve(raw_transactions.size());
184
0
            for (const auto& rawtx : raw_transactions.getValues()) {
185
0
                CMutableTransaction mtx;
186
0
                if (!DecodeHexTx(mtx, rawtx.get_str())) {
187
0
                    throw JSONRPCError(RPC_DESERIALIZATION_ERROR,
188
0
                                       "TX decode failed: " + rawtx.get_str() + " Make sure the tx has at least one input.");
189
0
                }
190
0
                txns.emplace_back(MakeTransactionRef(std::move(mtx)));
191
0
            }
192
193
0
            NodeContext& node = EnsureAnyNodeContext(request.context);
194
0
            CTxMemPool& mempool = EnsureMemPool(node);
195
0
            ChainstateManager& chainman = EnsureChainman(node);
196
0
            Chainstate& chainstate = chainman.ActiveChainstate();
197
0
            const PackageMempoolAcceptResult package_result = [&] {
198
0
                LOCK(::cs_main);
Line
Count
Source
259
0
#define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
Line
Count
Source
11
0
#define UNIQUE_NAME(name) PASTE2(name, __COUNTER__)
Line
Count
Source
9
0
#define PASTE2(x, y) PASTE(x, y)
Line
Count
Source
8
0
#define PASTE(x, y) x ## y
199
0
                if (txns.size() > 1) return ProcessNewPackage(chainstate, mempool, txns, /*test_accept=*/true, /*client_maxfeerate=*/{});
200
0
                return PackageMempoolAcceptResult(txns[0]->GetWitnessHash(),
201
0
                                                  chainman.ProcessTransaction(txns[0], /*test_accept=*/true));
202
0
            }();
203
204
0
            UniValue rpc_result(UniValue::VARR);
205
            // We will check transaction fees while we iterate through txns in order. If any transaction fee
206
            // exceeds maxfeerate, we will leave the rest of the validation results blank, because it
207
            // doesn't make sense to return a validation result for a transaction if its ancestor(s) would
208
            // not be submitted.
209
0
            bool exit_early{false};
210
0
            for (const auto& tx : txns) {
211
0
                UniValue result_inner(UniValue::VOBJ);
212
0
                result_inner.pushKV("txid", tx->GetHash().GetHex());
213
0
                result_inner.pushKV("wtxid", tx->GetWitnessHash().GetHex());
214
0
                if (package_result.m_state.GetResult() == PackageValidationResult::PCKG_POLICY) {
215
0
                    result_inner.pushKV("package-error", package_result.m_state.ToString());
216
0
                }
217
0
                auto it = package_result.m_tx_results.find(tx->GetWitnessHash());
218
0
                if (exit_early || it == package_result.m_tx_results.end()) {
219
                    // Validation unfinished. Just return the txid and wtxid.
220
0
                    rpc_result.push_back(std::move(result_inner));
221
0
                    continue;
222
0
                }
223
0
                const auto& tx_result = it->second;
224
                // Package testmempoolaccept doesn't allow transactions to already be in the mempool.
225
0
                CHECK_NONFATAL(tx_result.m_result_type != MempoolAcceptResult::ResultType::MEMPOOL_ENTRY);
Line
Count
Source
110
0
    inline_check_non_fatal(condition, std::source_location::current(), #condition)
226
0
                if (tx_result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
227
0
                    const CAmount fee = tx_result.m_base_fees.value();
228
                    // Check that fee does not exceed maximum fee
229
0
                    const int64_t virtual_size = tx_result.m_vsize.value();
230
0
                    const CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size);
231
0
                    if (max_raw_tx_fee && fee > max_raw_tx_fee) {
232
0
                        result_inner.pushKV("allowed", false);
233
0
                        result_inner.pushKV("reject-reason", "max-fee-exceeded");
234
0
                        exit_early = true;
235
0
                    } else {
236
                        // Only return the fee and vsize if the transaction would pass ATMP.
237
                        // These can be used to calculate the feerate.
238
0
                        result_inner.pushKV("allowed", true);
239
0
                        result_inner.pushKV("vsize", virtual_size);
240
0
                        UniValue fees(UniValue::VOBJ);
241
0
                        fees.pushKV("base", ValueFromAmount(fee));
242
0
                        fees.pushKV("effective-feerate", ValueFromAmount(tx_result.m_effective_feerate.value().GetFeePerK()));
243
0
                        UniValue effective_includes_res(UniValue::VARR);
244
0
                        for (const auto& wtxid : tx_result.m_wtxids_fee_calculations.value()) {
245
0
                            effective_includes_res.push_back(wtxid.ToString());
246
0
                        }
247
0
                        fees.pushKV("effective-includes", std::move(effective_includes_res));
248
0
                        result_inner.pushKV("fees", std::move(fees));
249
0
                    }
250
0
                } else {
251
0
                    result_inner.pushKV("allowed", false);
252
0
                    const TxValidationState state = tx_result.m_state;
253
0
                    if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
254
0
                        result_inner.pushKV("reject-reason", "missing-inputs");
255
0
                    } else {
256
0
                        result_inner.pushKV("reject-reason", state.GetRejectReason());
257
0
                        result_inner.pushKV("reject-details", state.ToString());
258
0
                    }
259
0
                }
260
0
                rpc_result.push_back(std::move(result_inner));
261
0
            }
262
0
            return rpc_result;
263
0
        },
264
0
    };
265
0
}
266
267
static std::vector<RPCResult> MempoolEntryDescription()
268
0
{
269
0
    return {
270
0
        RPCResult{RPCResult::Type::NUM, "vsize", "virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted."},
271
0
        RPCResult{RPCResult::Type::NUM, "weight", "transaction weight as defined in BIP 141."},
272
0
        RPCResult{RPCResult::Type::NUM_TIME, "time", "local time transaction entered pool in seconds since 1 Jan 1970 GMT"},
273
0
        RPCResult{RPCResult::Type::NUM, "height", "block height when transaction entered pool"},
274
0
        RPCResult{RPCResult::Type::NUM, "descendantcount", "number of in-mempool descendant transactions (including this one)"},
275
0
        RPCResult{RPCResult::Type::NUM, "descendantsize", "virtual transaction size of in-mempool descendants (including this one)"},
276
0
        RPCResult{RPCResult::Type::NUM, "ancestorcount", "number of in-mempool ancestor transactions (including this one)"},
277
0
        RPCResult{RPCResult::Type::NUM, "ancestorsize", "virtual transaction size of in-mempool ancestors (including this one)"},
278
0
        RPCResult{RPCResult::Type::STR_HEX, "wtxid", "hash of serialized transaction, including witness data"},
279
0
        RPCResult{RPCResult::Type::OBJ, "fees", "",
280
0
            {
281
0
                RPCResult{RPCResult::Type::STR_AMOUNT, "base", "transaction fee, denominated in " + CURRENCY_UNIT},
282
0
                RPCResult{RPCResult::Type::STR_AMOUNT, "modified", "transaction fee with fee deltas used for mining priority, denominated in " + CURRENCY_UNIT},
283
0
                RPCResult{RPCResult::Type::STR_AMOUNT, "ancestor", "transaction fees of in-mempool ancestors (including this one) with fee deltas used for mining priority, denominated in " + CURRENCY_UNIT},
284
0
                RPCResult{RPCResult::Type::STR_AMOUNT, "descendant", "transaction fees of in-mempool descendants (including this one) with fee deltas used for mining priority, denominated in " + CURRENCY_UNIT},
285
0
            }},
286
0
        RPCResult{RPCResult::Type::ARR, "depends", "unconfirmed transactions used as inputs for this transaction",
287
0
            {RPCResult{RPCResult::Type::STR_HEX, "transactionid", "parent transaction id"}}},
288
0
        RPCResult{RPCResult::Type::ARR, "spentby", "unconfirmed transactions spending outputs from this transaction",
289
0
            {RPCResult{RPCResult::Type::STR_HEX, "transactionid", "child transaction id"}}},
290
0
        RPCResult{RPCResult::Type::BOOL, "bip125-replaceable", "Whether this transaction signals BIP125 replaceability or has an unconfirmed ancestor signaling BIP125 replaceability. (DEPRECATED)\n"},
291
0
        RPCResult{RPCResult::Type::BOOL, "unbroadcast", "Whether this transaction is currently unbroadcast (initial broadcast not yet acknowledged by any peers)"},
292
0
    };
293
0
}
294
295
static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPoolEntry& e) EXCLUSIVE_LOCKS_REQUIRED(pool.cs)
296
0
{
297
0
    AssertLockHeld(pool.cs);
Line
Count
Source
137
0
#define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs)
298
299
0
    info.pushKV("vsize", (int)e.GetTxSize());
300
0
    info.pushKV("weight", (int)e.GetTxWeight());
301
0
    info.pushKV("time", count_seconds(e.GetTime()));
302
0
    info.pushKV("height", (int)e.GetHeight());
303
0
    info.pushKV("descendantcount", e.GetCountWithDescendants());
304
0
    info.pushKV("descendantsize", e.GetSizeWithDescendants());
305
0
    info.pushKV("ancestorcount", e.GetCountWithAncestors());
306
0
    info.pushKV("ancestorsize", e.GetSizeWithAncestors());
307
0
    info.pushKV("wtxid", e.GetTx().GetWitnessHash().ToString());
308
309
0
    UniValue fees(UniValue::VOBJ);
310
0
    fees.pushKV("base", ValueFromAmount(e.GetFee()));
311
0
    fees.pushKV("modified", ValueFromAmount(e.GetModifiedFee()));
312
0
    fees.pushKV("ancestor", ValueFromAmount(e.GetModFeesWithAncestors()));
313
0
    fees.pushKV("descendant", ValueFromAmount(e.GetModFeesWithDescendants()));
314
0
    info.pushKV("fees", std::move(fees));
315
316
0
    const CTransaction& tx = e.GetTx();
317
0
    std::set<std::string> setDepends;
318
0
    for (const CTxIn& txin : tx.vin)
319
0
    {
320
0
        if (pool.exists(txin.prevout.hash))
321
0
            setDepends.insert(txin.prevout.hash.ToString());
322
0
    }
323
324
0
    UniValue depends(UniValue::VARR);
325
0
    for (const std::string& dep : setDepends)
326
0
    {
327
0
        depends.push_back(dep);
328
0
    }
329
330
0
    info.pushKV("depends", std::move(depends));
331
332
0
    UniValue spent(UniValue::VARR);
333
0
    for (const CTxMemPoolEntry& child : e.GetMemPoolChildrenConst()) {
334
0
        spent.push_back(child.GetTx().GetHash().ToString());
335
0
    }
336
337
0
    info.pushKV("spentby", std::move(spent));
338
339
    // Add opt-in RBF status
340
0
    bool rbfStatus = false;
341
0
    RBFTransactionState rbfState = IsRBFOptIn(tx, pool);
342
0
    if (rbfState == RBFTransactionState::UNKNOWN) {
343
0
        throw JSONRPCError(RPC_MISC_ERROR, "Transaction is not in mempool");
344
0
    } else if (rbfState == RBFTransactionState::REPLACEABLE_BIP125) {
345
0
        rbfStatus = true;
346
0
    }
347
348
0
    info.pushKV("bip125-replaceable", rbfStatus);
349
0
    info.pushKV("unbroadcast", pool.IsUnbroadcastTx(tx.GetHash()));
350
0
}
351
352
UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose, bool include_mempool_sequence)
353
0
{
354
0
    if (verbose) {
355
0
        if (include_mempool_sequence) {
356
0
            throw JSONRPCError(RPC_INVALID_PARAMETER, "Verbose results cannot contain mempool sequence values.");
357
0
        }
358
0
        LOCK(pool.cs);
Line
Count
Source
259
0
#define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
Line
Count
Source
11
0
#define UNIQUE_NAME(name) PASTE2(name, __COUNTER__)
Line
Count
Source
9
0
#define PASTE2(x, y) PASTE(x, y)
Line
Count
Source
8
0
#define PASTE(x, y) x ## y
359
0
        UniValue o(UniValue::VOBJ);
360
0
        for (const CTxMemPoolEntry& e : pool.entryAll()) {
361
0
            UniValue info(UniValue::VOBJ);
362
0
            entryToJSON(pool, info, e);
363
            // Mempool has unique entries so there is no advantage in using
364
            // UniValue::pushKV, which checks if the key already exists in O(N).
365
            // UniValue::pushKVEnd is used instead which currently is O(1).
366
0
            o.pushKVEnd(e.GetTx().GetHash().ToString(), std::move(info));
367
0
        }
368
0
        return o;
369
0
    } else {
370
0
        UniValue a(UniValue::VARR);
371
0
        uint64_t mempool_sequence;
372
0
        {
373
0
            LOCK(pool.cs);
Line
Count
Source
259
0
#define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
Line
Count
Source
11
0
#define UNIQUE_NAME(name) PASTE2(name, __COUNTER__)
Line
Count
Source
9
0
#define PASTE2(x, y) PASTE(x, y)
Line
Count
Source
8
0
#define PASTE(x, y) x ## y
374
0
            for (const CTxMemPoolEntry& e : pool.entryAll()) {
375
0
                a.push_back(e.GetTx().GetHash().ToString());
376
0
            }
377
0
            mempool_sequence = pool.GetSequence();
378
0
        }
379
0
        if (!include_mempool_sequence) {
380
0
            return a;
381
0
        } else {
382
0
            UniValue o(UniValue::VOBJ);
383
0
            o.pushKV("txids", std::move(a));
384
0
            o.pushKV("mempool_sequence", mempool_sequence);
385
0
            return o;
386
0
        }
387
0
    }
388
0
}
389
390
static RPCHelpMan getrawmempool()
391
0
{
392
0
    return RPCHelpMan{
393
0
        "getrawmempool",
394
0
        "Returns all transaction ids in memory pool as a json array of string transaction ids.\n"
395
0
        "\nHint: use getmempoolentry to fetch a specific transaction from the mempool.\n",
396
0
        {
397
0
            {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"},
398
0
            {"mempool_sequence", RPCArg::Type::BOOL, RPCArg::Default{false}, "If verbose=false, returns a json object with transaction list and mempool sequence number attached."},
399
0
        },
400
0
        {
401
0
            RPCResult{"for verbose = false",
402
0
                RPCResult::Type::ARR, "", "",
403
0
                {
404
0
                    {RPCResult::Type::STR_HEX, "", "The transaction id"},
405
0
                }},
406
0
            RPCResult{"for verbose = true",
407
0
                RPCResult::Type::OBJ_DYN, "", "",
408
0
                {
409
0
                    {RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()},
410
0
                }},
411
0
            RPCResult{"for verbose = false and mempool_sequence = true",
412
0
                RPCResult::Type::OBJ, "", "",
413
0
                {
414
0
                    {RPCResult::Type::ARR, "txids", "",
415
0
                    {
416
0
                        {RPCResult::Type::STR_HEX, "", "The transaction id"},
417
0
                    }},
418
0
                    {RPCResult::Type::NUM, "mempool_sequence", "The mempool sequence value."},
419
0
                }},
420
0
        },
421
0
        RPCExamples{
422
0
            HelpExampleCli("getrawmempool", "true")
423
0
            + HelpExampleRpc("getrawmempool", "true")
424
0
        },
425
0
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
426
0
{
427
0
    bool fVerbose = false;
428
0
    if (!request.params[0].isNull())
429
0
        fVerbose = request.params[0].get_bool();
430
431
0
    bool include_mempool_sequence = false;
432
0
    if (!request.params[1].isNull()) {
433
0
        include_mempool_sequence = request.params[1].get_bool();
434
0
    }
435
436
0
    return MempoolToJSON(EnsureAnyMemPool(request.context), fVerbose, include_mempool_sequence);
437
0
},
438
0
    };
439
0
}
440
441
static RPCHelpMan getmempoolancestors()
442
0
{
443
0
    return RPCHelpMan{
444
0
        "getmempoolancestors",
445
0
        "If txid is in the mempool, returns all in-mempool ancestors.\n",
446
0
        {
447
0
            {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
448
0
            {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"},
449
0
        },
450
0
        {
451
0
            RPCResult{"for verbose = false",
452
0
                RPCResult::Type::ARR, "", "",
453
0
                {{RPCResult::Type::STR_HEX, "", "The transaction id of an in-mempool ancestor transaction"}}},
454
0
            RPCResult{"for verbose = true",
455
0
                RPCResult::Type::OBJ_DYN, "", "",
456
0
                {
457
0
                    {RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()},
458
0
                }},
459
0
        },
460
0
        RPCExamples{
461
0
            HelpExampleCli("getmempoolancestors", "\"mytxid\"")
462
0
            + HelpExampleRpc("getmempoolancestors", "\"mytxid\"")
463
0
        },
464
0
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
465
0
{
466
0
    bool fVerbose = false;
467
0
    if (!request.params[1].isNull())
468
0
        fVerbose = request.params[1].get_bool();
469
470
0
    auto txid{Txid::FromUint256(ParseHashV(request.params[0], "txid"))};
471
472
0
    const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
473
0
    LOCK(mempool.cs);
Line
Count
Source
259
0
#define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
Line
Count
Source
11
0
#define UNIQUE_NAME(name) PASTE2(name, __COUNTER__)
Line
Count
Source
9
0
#define PASTE2(x, y) PASTE(x, y)
Line
Count
Source
8
0
#define PASTE(x, y) x ## y
474
475
0
    const auto entry{mempool.GetEntry(txid)};
476
0
    if (entry == nullptr) {
477
0
        throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
478
0
    }
479
480
0
    auto ancestors{mempool.AssumeCalculateMemPoolAncestors(self.m_name, *entry, CTxMemPool::Limits::NoLimits(), /*fSearchForParents=*/false)};
481
482
0
    if (!fVerbose) {
483
0
        UniValue o(UniValue::VARR);
484
0
        for (CTxMemPool::txiter ancestorIt : ancestors) {
485
0
            o.push_back(ancestorIt->GetTx().GetHash().ToString());
486
0
        }
487
0
        return o;
488
0
    } else {
489
0
        UniValue o(UniValue::VOBJ);
490
0
        for (CTxMemPool::txiter ancestorIt : ancestors) {
491
0
            const CTxMemPoolEntry &e = *ancestorIt;
492
0
            UniValue info(UniValue::VOBJ);
493
0
            entryToJSON(mempool, info, e);
494
0
            o.pushKV(e.GetTx().GetHash().ToString(), std::move(info));
495
0
        }
496
0
        return o;
497
0
    }
498
0
},
499
0
    };
500
0
}
501
502
static RPCHelpMan getmempooldescendants()
503
0
{
504
0
    return RPCHelpMan{
505
0
        "getmempooldescendants",
506
0
        "If txid is in the mempool, returns all in-mempool descendants.\n",
507
0
        {
508
0
            {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
509
0
            {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"},
510
0
        },
511
0
        {
512
0
            RPCResult{"for verbose = false",
513
0
                RPCResult::Type::ARR, "", "",
514
0
                {{RPCResult::Type::STR_HEX, "", "The transaction id of an in-mempool descendant transaction"}}},
515
0
            RPCResult{"for verbose = true",
516
0
                RPCResult::Type::OBJ_DYN, "", "",
517
0
                {
518
0
                    {RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()},
519
0
                }},
520
0
        },
521
0
        RPCExamples{
522
0
            HelpExampleCli("getmempooldescendants", "\"mytxid\"")
523
0
            + HelpExampleRpc("getmempooldescendants", "\"mytxid\"")
524
0
        },
525
0
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
526
0
{
527
0
    bool fVerbose = false;
528
0
    if (!request.params[1].isNull())
529
0
        fVerbose = request.params[1].get_bool();
530
531
0
    auto txid{Txid::FromUint256(ParseHashV(request.params[0], "txid"))};
532
533
0
    const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
534
0
    LOCK(mempool.cs);
Line
Count
Source
259
0
#define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
Line
Count
Source
11
0
#define UNIQUE_NAME(name) PASTE2(name, __COUNTER__)
Line
Count
Source
9
0
#define PASTE2(x, y) PASTE(x, y)
Line
Count
Source
8
0
#define PASTE(x, y) x ## y
535
536
0
    const auto it{mempool.GetIter(txid)};
537
0
    if (!it) {
538
0
        throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
539
0
    }
540
541
0
    CTxMemPool::setEntries setDescendants;
542
0
    mempool.CalculateDescendants(*it, setDescendants);
543
    // CTxMemPool::CalculateDescendants will include the given tx
544
0
    setDescendants.erase(*it);
545
546
0
    if (!fVerbose) {
547
0
        UniValue o(UniValue::VARR);
548
0
        for (CTxMemPool::txiter descendantIt : setDescendants) {
549
0
            o.push_back(descendantIt->GetTx().GetHash().ToString());
550
0
        }
551
552
0
        return o;
553
0
    } else {
554
0
        UniValue o(UniValue::VOBJ);
555
0
        for (CTxMemPool::txiter descendantIt : setDescendants) {
556
0
            const CTxMemPoolEntry &e = *descendantIt;
557
0
            UniValue info(UniValue::VOBJ);
558
0
            entryToJSON(mempool, info, e);
559
0
            o.pushKV(e.GetTx().GetHash().ToString(), std::move(info));
560
0
        }
561
0
        return o;
562
0
    }
563
0
},
564
0
    };
565
0
}
566
567
static RPCHelpMan getmempoolentry()
568
0
{
569
0
    return RPCHelpMan{
570
0
        "getmempoolentry",
571
0
        "Returns mempool data for given transaction\n",
572
0
        {
573
0
            {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
574
0
        },
575
0
        RPCResult{
576
0
            RPCResult::Type::OBJ, "", "", MempoolEntryDescription()},
577
0
        RPCExamples{
578
0
            HelpExampleCli("getmempoolentry", "\"mytxid\"")
579
0
            + HelpExampleRpc("getmempoolentry", "\"mytxid\"")
580
0
        },
581
0
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
582
0
{
583
0
    auto txid{Txid::FromUint256(ParseHashV(request.params[0], "txid"))};
584
585
0
    const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
586
0
    LOCK(mempool.cs);
Line
Count
Source
259
0
#define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
Line
Count
Source
11
0
#define UNIQUE_NAME(name) PASTE2(name, __COUNTER__)
Line
Count
Source
9
0
#define PASTE2(x, y) PASTE(x, y)
Line
Count
Source
8
0
#define PASTE(x, y) x ## y
587
588
0
    const auto entry{mempool.GetEntry(txid)};
589
0
    if (entry == nullptr) {
590
0
        throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
591
0
    }
592
593
0
    UniValue info(UniValue::VOBJ);
594
0
    entryToJSON(mempool, info, *entry);
595
0
    return info;
596
0
},
597
0
    };
598
0
}
599
600
static RPCHelpMan gettxspendingprevout()
601
0
{
602
0
    return RPCHelpMan{"gettxspendingprevout",
603
0
        "Scans the mempool to find transactions spending any of the given outputs",
604
0
        {
605
0
            {"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The transaction outputs that we want to check, and within each, the txid (string) vout (numeric).",
606
0
                {
607
0
                    {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
608
0
                        {
609
0
                            {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
610
0
                            {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
611
0
                        },
612
0
                    },
613
0
                },
614
0
            },
615
0
        },
616
0
        RPCResult{
617
0
            RPCResult::Type::ARR, "", "",
618
0
            {
619
0
                {RPCResult::Type::OBJ, "", "",
620
0
                {
621
0
                    {RPCResult::Type::STR_HEX, "txid", "the transaction id of the checked output"},
622
0
                    {RPCResult::Type::NUM, "vout", "the vout value of the checked output"},
623
0
                    {RPCResult::Type::STR_HEX, "spendingtxid", /*optional=*/true, "the transaction id of the mempool transaction spending this output (omitted if unspent)"},
624
0
                }},
625
0
            }
626
0
        },
627
0
        RPCExamples{
628
0
            HelpExampleCli("gettxspendingprevout", "\"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":3}]\"")
629
0
            + HelpExampleRpc("gettxspendingprevout", "\"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":3}]\"")
630
0
        },
631
0
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
632
0
        {
633
0
            const UniValue& output_params = request.params[0].get_array();
634
0
            if (output_params.empty()) {
635
0
                throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, outputs are missing");
636
0
            }
637
638
0
            std::vector<COutPoint> prevouts;
639
0
            prevouts.reserve(output_params.size());
640
641
0
            for (unsigned int idx = 0; idx < output_params.size(); idx++) {
642
0
                const UniValue& o = output_params[idx].get_obj();
643
644
0
                RPCTypeCheckObj(o,
645
0
                                {
646
0
                                    {"txid", UniValueType(UniValue::VSTR)},
647
0
                                    {"vout", UniValueType(UniValue::VNUM)},
648
0
                                }, /*fAllowNull=*/false, /*fStrict=*/true);
649
650
0
                const Txid txid = Txid::FromUint256(ParseHashO(o, "txid"));
651
0
                const int nOutput{o.find_value("vout").getInt<int>()};
652
0
                if (nOutput < 0) {
653
0
                    throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
654
0
                }
655
656
0
                prevouts.emplace_back(txid, nOutput);
657
0
            }
658
659
0
            const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
660
0
            LOCK(mempool.cs);
Line
Count
Source
259
0
#define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
Line
Count
Source
11
0
#define UNIQUE_NAME(name) PASTE2(name, __COUNTER__)
Line
Count
Source
9
0
#define PASTE2(x, y) PASTE(x, y)
Line
Count
Source
8
0
#define PASTE(x, y) x ## y
661
662
0
            UniValue result{UniValue::VARR};
663
664
0
            for (const COutPoint& prevout : prevouts) {
665
0
                UniValue o(UniValue::VOBJ);
666
0
                o.pushKV("txid", prevout.hash.ToString());
667
0
                o.pushKV("vout", (uint64_t)prevout.n);
668
669
0
                const CTransaction* spendingTx = mempool.GetConflictTx(prevout);
670
0
                if (spendingTx != nullptr) {
671
0
                    o.pushKV("spendingtxid", spendingTx->GetHash().ToString());
672
0
                }
673
674
0
                result.push_back(std::move(o));
675
0
            }
676
677
0
            return result;
678
0
        },
679
0
    };
680
0
}
681
682
UniValue MempoolInfoToJSON(const CTxMemPool& pool)
683
0
{
684
    // Make sure this call is atomic in the pool.
685
0
    LOCK(pool.cs);
Line
Count
Source
259
0
#define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
Line
Count
Source
11
0
#define UNIQUE_NAME(name) PASTE2(name, __COUNTER__)
Line
Count
Source
9
0
#define PASTE2(x, y) PASTE(x, y)
Line
Count
Source
8
0
#define PASTE(x, y) x ## y
686
0
    UniValue ret(UniValue::VOBJ);
687
0
    ret.pushKV("loaded", pool.GetLoadTried());
688
0
    ret.pushKV("size", (int64_t)pool.size());
689
0
    ret.pushKV("bytes", (int64_t)pool.GetTotalTxSize());
690
0
    ret.pushKV("usage", (int64_t)pool.DynamicMemoryUsage());
691
0
    ret.pushKV("total_fee", ValueFromAmount(pool.GetTotalFee()));
692
0
    ret.pushKV("maxmempool", pool.m_opts.max_size_bytes);
693
0
    ret.pushKV("mempoolminfee", ValueFromAmount(std::max(pool.GetMinFee(), pool.m_opts.min_relay_feerate).GetFeePerK()));
694
0
    ret.pushKV("minrelaytxfee", ValueFromAmount(pool.m_opts.min_relay_feerate.GetFeePerK()));
695
0
    ret.pushKV("incrementalrelayfee", ValueFromAmount(pool.m_opts.incremental_relay_feerate.GetFeePerK()));
696
0
    ret.pushKV("unbroadcastcount", uint64_t{pool.GetUnbroadcastTxs().size()});
697
0
    ret.pushKV("fullrbf", true);
698
0
    ret.pushKV("permitbaremultisig", pool.m_opts.permit_bare_multisig);
699
0
    ret.pushKV("maxdatacarriersize", pool.m_opts.max_datacarrier_bytes.value_or(0));
700
0
    return ret;
701
0
}
702
703
static RPCHelpMan getmempoolinfo()
704
0
{
705
0
    return RPCHelpMan{"getmempoolinfo",
706
0
        "Returns details on the active state of the TX memory pool.",
707
0
        {},
708
0
        RPCResult{
709
0
            RPCResult::Type::OBJ, "", "",
710
0
            {
711
0
                {RPCResult::Type::BOOL, "loaded", "True if the initial load attempt of the persisted mempool finished"},
712
0
                {RPCResult::Type::NUM, "size", "Current tx count"},
713
0
                {RPCResult::Type::NUM, "bytes", "Sum of all virtual transaction sizes as defined in BIP 141. Differs from actual serialized size because witness data is discounted"},
714
0
                {RPCResult::Type::NUM, "usage", "Total memory usage for the mempool"},
715
0
                {RPCResult::Type::STR_AMOUNT, "total_fee", "Total fees for the mempool in " + CURRENCY_UNIT + ", ignoring modified fees through prioritisetransaction"},
716
0
                {RPCResult::Type::NUM, "maxmempool", "Maximum memory usage for the mempool"},
717
0
                {RPCResult::Type::STR_AMOUNT, "mempoolminfee", "Minimum fee rate in " + CURRENCY_UNIT + "/kvB for tx to be accepted. Is the maximum of minrelaytxfee and minimum mempool fee"},
718
0
                {RPCResult::Type::STR_AMOUNT, "minrelaytxfee", "Current minimum relay fee for transactions"},
719
0
                {RPCResult::Type::NUM, "incrementalrelayfee", "minimum fee rate increment for mempool limiting or replacement in " + CURRENCY_UNIT + "/kvB"},
720
0
                {RPCResult::Type::NUM, "unbroadcastcount", "Current number of transactions that haven't passed initial broadcast yet"},
721
0
                {RPCResult::Type::BOOL, "fullrbf", "True if the mempool accepts RBF without replaceability signaling inspection (DEPRECATED)"},
722
0
                {RPCResult::Type::BOOL, "permitbaremultisig", "True if the mempool accepts transactions with bare multisig outputs"},
723
0
                {RPCResult::Type::NUM, "maxdatacarriersize", "Maximum number of bytes that can be used by OP_RETURN outputs in the mempool"},
724
0
            }},
725
0
        RPCExamples{
726
0
            HelpExampleCli("getmempoolinfo", "")
727
0
            + HelpExampleRpc("getmempoolinfo", "")
728
0
        },
729
0
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
730
0
{
731
0
    return MempoolInfoToJSON(EnsureAnyMemPool(request.context));
732
0
},
733
0
    };
734
0
}
735
736
static RPCHelpMan importmempool()
737
0
{
738
0
    return RPCHelpMan{
739
0
        "importmempool",
740
0
        "Import a mempool.dat file and attempt to add its contents to the mempool.\n"
741
0
        "Warning: Importing untrusted files is dangerous, especially if metadata from the file is taken over.",
742
0
        {
743
0
            {"filepath", RPCArg::Type::STR, RPCArg::Optional::NO, "The mempool file"},
744
0
            {"options",
745
0
             RPCArg::Type::OBJ_NAMED_PARAMS,
746
0
             RPCArg::Optional::OMITTED,
747
0
             "",
748
0
             {
749
0
                 {"use_current_time", RPCArg::Type::BOOL, RPCArg::Default{true},
750
0
                  "Whether to use the current system time or use the entry time metadata from the mempool file.\n"
751
0
                  "Warning: Importing untrusted metadata may lead to unexpected issues and undesirable behavior."},
752
0
                 {"apply_fee_delta_priority", RPCArg::Type::BOOL, RPCArg::Default{false},
753
0
                  "Whether to apply the fee delta metadata from the mempool file.\n"
754
0
                  "It will be added to any existing fee deltas.\n"
755
0
                  "The fee delta can be set by the prioritisetransaction RPC.\n"
756
0
                  "Warning: Importing untrusted metadata may lead to unexpected issues and undesirable behavior.\n"
757
0
                  "Only set this bool if you understand what it does."},
758
0
                 {"apply_unbroadcast_set", RPCArg::Type::BOOL, RPCArg::Default{false},
759
0
                  "Whether to apply the unbroadcast set metadata from the mempool file.\n"
760
0
                  "Warning: Importing untrusted metadata may lead to unexpected issues and undesirable behavior."},
761
0
             },
762
0
             RPCArgOptions{.oneline_description = "options"}},
763
0
        },
764
0
        RPCResult{RPCResult::Type::OBJ, "", "", std::vector<RPCResult>{}},
765
0
        RPCExamples{HelpExampleCli("importmempool", "/path/to/mempool.dat") + HelpExampleRpc("importmempool", "/path/to/mempool.dat")},
766
0
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
767
0
            const NodeContext& node{EnsureAnyNodeContext(request.context)};
768
769
0
            CTxMemPool& mempool{EnsureMemPool(node)};
770
0
            ChainstateManager& chainman = EnsureChainman(node);
771
0
            Chainstate& chainstate = chainman.ActiveChainstate();
772
773
0
            if (chainman.IsInitialBlockDownload()) {
774
0
                throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Can only import the mempool after the block download and sync is done.");
775
0
            }
776
777
0
            const fs::path load_path{fs::u8path(self.Arg<std::string_view>("filepath"))};
778
0
            const UniValue& use_current_time{request.params[1]["use_current_time"]};
779
0
            const UniValue& apply_fee_delta{request.params[1]["apply_fee_delta_priority"]};
780
0
            const UniValue& apply_unbroadcast{request.params[1]["apply_unbroadcast_set"]};
781
0
            node::ImportMempoolOptions opts{
782
0
                .use_current_time = use_current_time.isNull() ? true : use_current_time.get_bool(),
783
0
                .apply_fee_delta_priority = apply_fee_delta.isNull() ? false : apply_fee_delta.get_bool(),
784
0
                .apply_unbroadcast_set = apply_unbroadcast.isNull() ? false : apply_unbroadcast.get_bool(),
785
0
            };
786
787
0
            if (!node::LoadMempool(mempool, load_path, chainstate, std::move(opts))) {
788
0
                throw JSONRPCError(RPC_MISC_ERROR, "Unable to import mempool file, see debug.log for details.");
789
0
            }
790
791
0
            UniValue ret{UniValue::VOBJ};
792
0
            return ret;
793
0
        },
794
0
    };
795
0
}
796
797
static RPCHelpMan savemempool()
798
0
{
799
0
    return RPCHelpMan{
800
0
        "savemempool",
801
0
        "Dumps the mempool to disk. It will fail until the previous dump is fully loaded.\n",
802
0
        {},
803
0
        RPCResult{
804
0
            RPCResult::Type::OBJ, "", "",
805
0
            {
806
0
                {RPCResult::Type::STR, "filename", "the directory and file where the mempool was saved"},
807
0
            }},
808
0
        RPCExamples{
809
0
            HelpExampleCli("savemempool", "")
810
0
            + HelpExampleRpc("savemempool", "")
811
0
        },
812
0
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
813
0
{
814
0
    const ArgsManager& args{EnsureAnyArgsman(request.context)};
815
0
    const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
816
817
0
    if (!mempool.GetLoadTried()) {
818
0
        throw JSONRPCError(RPC_MISC_ERROR, "The mempool was not loaded yet");
819
0
    }
820
821
0
    const fs::path& dump_path = MempoolPath(args);
822
823
0
    if (!DumpMempool(mempool, dump_path)) {
824
0
        throw JSONRPCError(RPC_MISC_ERROR, "Unable to dump mempool to disk");
825
0
    }
826
827
0
    UniValue ret(UniValue::VOBJ);
828
0
    ret.pushKV("filename", dump_path.utf8string());
829
830
0
    return ret;
831
0
},
832
0
    };
833
0
}
834
835
static std::vector<RPCResult> OrphanDescription()
836
0
{
837
0
    return {
838
0
        RPCResult{RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
839
0
        RPCResult{RPCResult::Type::STR_HEX, "wtxid", "The transaction witness hash in hex"},
840
0
        RPCResult{RPCResult::Type::NUM, "bytes", "The serialized transaction size in bytes"},
841
0
        RPCResult{RPCResult::Type::NUM, "vsize", "The virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted."},
842
0
        RPCResult{RPCResult::Type::NUM, "weight", "The transaction weight as defined in BIP 141."},
843
0
        RPCResult{RPCResult::Type::ARR, "from", "",
844
0
        {
845
0
            RPCResult{RPCResult::Type::NUM, "peer_id", "Peer ID"},
846
0
        }},
847
0
    };
848
0
}
849
850
static UniValue OrphanToJSON(const node::TxOrphanage::OrphanInfo& orphan)
851
0
{
852
0
    UniValue o(UniValue::VOBJ);
853
0
    o.pushKV("txid", orphan.tx->GetHash().ToString());
854
0
    o.pushKV("wtxid", orphan.tx->GetWitnessHash().ToString());
855
0
    o.pushKV("bytes", orphan.tx->GetTotalSize());
856
0
    o.pushKV("vsize", GetVirtualTransactionSize(*orphan.tx));
857
0
    o.pushKV("weight", GetTransactionWeight(*orphan.tx));
858
0
    UniValue from(UniValue::VARR);
859
0
    for (const auto fromPeer: orphan.announcers) {
860
0
        from.push_back(fromPeer);
861
0
    }
862
0
    o.pushKV("from", from);
863
0
    return o;
864
0
}
865
866
static RPCHelpMan getorphantxs()
867
0
{
868
0
    return RPCHelpMan{
869
0
        "getorphantxs",
870
0
        "Shows transactions in the tx orphanage.\n"
871
0
        "\nEXPERIMENTAL warning: this call may be changed in future releases.\n",
872
0
        {
873
0
            {"verbosity", RPCArg::Type::NUM, RPCArg::Default{0}, "0 for an array of txids (may contain duplicates), 1 for an array of objects with tx details, and 2 for details from (1) and tx hex",
874
0
             RPCArgOptions{.skip_type_check = true}},
875
0
        },
876
0
        {
877
0
            RPCResult{"for verbose = 0",
878
0
                RPCResult::Type::ARR, "", "",
879
0
                {
880
0
                    {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
881
0
                }},
882
0
            RPCResult{"for verbose = 1",
883
0
                RPCResult::Type::ARR, "", "",
884
0
                {
885
0
                    {RPCResult::Type::OBJ, "", "", OrphanDescription()},
886
0
                }},
887
0
            RPCResult{"for verbose = 2",
888
0
                RPCResult::Type::ARR, "", "",
889
0
                {
890
0
                    {RPCResult::Type::OBJ, "", "",
891
0
                        Cat<std::vector<RPCResult>>(
892
0
                            OrphanDescription(),
893
0
                            {{RPCResult::Type::STR_HEX, "hex", "The serialized, hex-encoded transaction data"}}
894
0
                        )
895
0
                    },
896
0
                }},
897
0
        },
898
0
        RPCExamples{
899
0
            HelpExampleCli("getorphantxs", "2")
900
0
            + HelpExampleRpc("getorphantxs", "2")
901
0
        },
902
0
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
903
0
        {
904
0
            const NodeContext& node = EnsureAnyNodeContext(request.context);
905
0
            PeerManager& peerman = EnsurePeerman(node);
906
0
            std::vector<node::TxOrphanage::OrphanInfo> orphanage = peerman.GetOrphanTransactions();
907
908
0
            int verbosity{ParseVerbosity(request.params[0], /*default_verbosity=*/0, /*allow_bool*/false)};
909
910
0
            UniValue ret(UniValue::VARR);
911
912
0
            if (verbosity == 0) {
913
0
                for (auto const& orphan : orphanage) {
914
0
                    ret.push_back(orphan.tx->GetHash().ToString());
915
0
                }
916
0
            } else if (verbosity == 1) {
917
0
                for (auto const& orphan : orphanage) {
918
0
                    ret.push_back(OrphanToJSON(orphan));
919
0
                }
920
0
            } else if (verbosity == 2) {
921
0
                for (auto const& orphan : orphanage) {
922
0
                    UniValue o{OrphanToJSON(orphan)};
923
0
                    o.pushKV("hex", EncodeHexTx(*orphan.tx));
924
0
                    ret.push_back(o);
925
0
                }
926
0
            } else {
927
0
                throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid verbosity value " + ToString(verbosity));
928
0
            }
929
930
0
            return ret;
931
0
        },
932
0
    };
933
0
}
934
935
static RPCHelpMan submitpackage()
936
0
{
937
0
    return RPCHelpMan{"submitpackage",
938
0
        "Submit a package of raw transactions (serialized, hex-encoded) to local node.\n"
939
0
        "The package will be validated according to consensus and mempool policy rules. If any transaction passes, it will be accepted to mempool.\n"
940
0
        "This RPC is experimental and the interface may be unstable. Refer to doc/policy/packages.md for documentation on package policies.\n"
941
0
        "Warning: successful submission does not mean the transactions will propagate throughout the network.\n"
942
0
        ,
943
0
        {
944
0
            {"package", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of raw transactions.\n"
945
0
                "The package must consist of a transaction with (some, all, or none of) its unconfirmed parents. A single transaction is permitted.\n"
946
0
                "None of the parents may depend on each other. Parents that are already in mempool do not need to be present in the package.\n"
947
0
                "The package must be topologically sorted, with the child being the last element in the array if there are multiple elements.",
948
0
                {
949
0
                    {"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
950
0
                },
951
0
            },
952
0
            {"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},
953
0
             "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
954
0
                 "/kvB.\nFee rates larger than 1BTC/kvB are rejected.\nSet to 0 to accept any fee rate."},
955
0
            {"maxburnamount", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_BURN_AMOUNT)},
956
0
             "Reject transactions with provably unspendable outputs (e.g. 'datacarrier' outputs that use the OP_RETURN opcode) greater than the specified value, expressed in " + CURRENCY_UNIT + ".\n"
957
0
             "If burning funds through unspendable outputs is desired, increase this value.\n"
958
0
             "This check is based on heuristics and does not guarantee spendability of outputs.\n"
959
0
            },
960
0
        },
961
0
        RPCResult{
962
0
            RPCResult::Type::OBJ, "", "",
963
0
            {
964
0
                {RPCResult::Type::STR, "package_msg", "The transaction package result message. \"success\" indicates all transactions were accepted into or are already in the mempool."},
965
0
                {RPCResult::Type::OBJ_DYN, "tx-results", "The transaction results keyed by wtxid. An entry is returned for every submitted wtxid.",
966
0
                {
967
0
                    {RPCResult::Type::OBJ, "wtxid", "transaction wtxid", {
968
0
                        {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
969
0
                        {RPCResult::Type::STR_HEX, "other-wtxid", /*optional=*/true, "The wtxid of a different transaction with the same txid but different witness found in the mempool. This means the submitted transaction was ignored."},
970
0
                        {RPCResult::Type::NUM, "vsize", /*optional=*/true, "Sigops-adjusted virtual transaction size."},
971
0
                        {RPCResult::Type::OBJ, "fees", /*optional=*/true, "Transaction fees", {
972
0
                            {RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT},
973
0
                            {RPCResult::Type::STR_AMOUNT, "effective-feerate", /*optional=*/true, "if the transaction was not already in the mempool, the effective feerate in " + CURRENCY_UNIT + " per KvB. For example, the package feerate and/or feerate with modified fees from prioritisetransaction."},
974
0
                            {RPCResult::Type::ARR, "effective-includes", /*optional=*/true, "if effective-feerate is provided, the wtxids of the transactions whose fees and vsizes are included in effective-feerate.",
975
0
                                {{RPCResult::Type::STR_HEX, "", "transaction wtxid in hex"},
976
0
                            }},
977
0
                        }},
978
0
                        {RPCResult::Type::STR, "error", /*optional=*/true, "Error string if rejected from mempool, or \"package-not-validated\" when the package aborts before any per-tx processing."},
979
0
                    }}
980
0
                }},
981
0
                {RPCResult::Type::ARR, "replaced-transactions", /*optional=*/true, "List of txids of replaced transactions",
982
0
                {
983
0
                    {RPCResult::Type::STR_HEX, "", "The transaction id"},
984
0
                }},
985
0
            },
986
0
        },
987
0
        RPCExamples{
988
0
            HelpExampleRpc("submitpackage", R"(["raw-parent-tx-1", "raw-parent-tx-2", "raw-child-tx"])") +
989
0
            HelpExampleCli("submitpackage", R"('["raw-tx-without-unconfirmed-parents"]')")
990
0
        },
991
0
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
992
0
        {
993
0
            const UniValue raw_transactions = request.params[0].get_array();
994
0
            if (raw_transactions.empty() || raw_transactions.size() > MAX_PACKAGE_COUNT) {
995
0
                throw JSONRPCError(RPC_INVALID_PARAMETER,
996
0
                                   "Array must contain between 1 and " + ToString(MAX_PACKAGE_COUNT) + " transactions.");
997
0
            }
998
999
            // Fee check needs to be run with chainstate and package context
1000
0
            const CFeeRate max_raw_tx_fee_rate{ParseFeeRate(self.Arg<UniValue>("maxfeerate"))};
1001
0
            std::optional<CFeeRate> client_maxfeerate{max_raw_tx_fee_rate};
1002
            // 0-value is special; it's mapped to no sanity check
1003
0
            if (max_raw_tx_fee_rate == CFeeRate(0)) {
1004
0
                client_maxfeerate = std::nullopt;
1005
0
            }
1006
1007
            // Burn sanity check is run with no context
1008
0
            const CAmount max_burn_amount = request.params[2].isNull() ? 0 : AmountFromValue(request.params[2]);
1009
1010
0
            std::vector<CTransactionRef> txns;
1011
0
            txns.reserve(raw_transactions.size());
1012
0
            for (const auto& rawtx : raw_transactions.getValues()) {
1013
0
                CMutableTransaction mtx;
1014
0
                if (!DecodeHexTx(mtx, rawtx.get_str())) {
1015
0
                    throw JSONRPCError(RPC_DESERIALIZATION_ERROR,
1016
0
                                       "TX decode failed: " + rawtx.get_str() + " Make sure the tx has at least one input.");
1017
0
                }
1018
1019
0
                for (const auto& out : mtx.vout) {
1020
0
                    if((out.scriptPubKey.IsUnspendable() || !out.scriptPubKey.HasValidOps()) && out.nValue > max_burn_amount) {
1021
0
                        throw JSONRPCTransactionError(TransactionError::MAX_BURN_EXCEEDED);
1022
0
                    }
1023
0
                }
1024
1025
0
                txns.emplace_back(MakeTransactionRef(std::move(mtx)));
1026
0
            }
1027
0
            CHECK_NONFATAL(!txns.empty());
Line
Count
Source
110
0
    inline_check_non_fatal(condition, std::source_location::current(), #condition)
1028
0
            if (txns.size() > 1 && !IsChildWithParentsTree(txns)) {
1029
0
                throw JSONRPCTransactionError(TransactionError::INVALID_PACKAGE, "package topology disallowed. not child-with-parents or parents depend on each other.");
1030
0
            }
1031
1032
0
            NodeContext& node = EnsureAnyNodeContext(request.context);
1033
0
            CTxMemPool& mempool = EnsureMemPool(node);
1034
0
            Chainstate& chainstate = EnsureChainman(node).ActiveChainstate();
1035
0
            const auto package_result = WITH_LOCK(::cs_main, return ProcessNewPackage(chainstate, mempool, txns, /*test_accept=*/ false, client_maxfeerate));
Line
Count
Source
290
0
#define WITH_LOCK(cs, code) (MaybeCheckNotHeld(cs), [&]() -> decltype(auto) { LOCK(cs); code; }())
1036
1037
0
            std::string package_msg = "success";
1038
1039
            // First catch package-wide errors, continue if we can
1040
0
            switch(package_result.m_state.GetResult()) {
1041
0
                case PackageValidationResult::PCKG_RESULT_UNSET:
1042
0
                {
1043
                    // Belt-and-suspenders check; everything should be successful here
1044
0
                    CHECK_NONFATAL(package_result.m_tx_results.size() == txns.size());
Line
Count
Source
110
0
    inline_check_non_fatal(condition, std::source_location::current(), #condition)
1045
0
                    for (const auto& tx : txns) {
1046
0
                        CHECK_NONFATAL(mempool.exists(tx->GetHash()));
Line
Count
Source
110
0
    inline_check_non_fatal(condition, std::source_location::current(), #condition)
1047
0
                    }
1048
0
                    break;
1049
0
                }
1050
0
                case PackageValidationResult::PCKG_MEMPOOL_ERROR:
1051
0
                {
1052
                    // This only happens with internal bug; user should stop and report
1053
0
                    throw JSONRPCTransactionError(TransactionError::MEMPOOL_ERROR,
1054
0
                        package_result.m_state.GetRejectReason());
1055
0
                }
1056
0
                case PackageValidationResult::PCKG_POLICY:
1057
0
                case PackageValidationResult::PCKG_TX:
1058
0
                {
1059
                    // Package-wide error we want to return, but we also want to return individual responses
1060
0
                    package_msg = package_result.m_state.ToString();
1061
0
                    CHECK_NONFATAL(package_result.m_tx_results.size() == txns.size() ||
Line
Count
Source
110
0
    inline_check_non_fatal(condition, std::source_location::current(), #condition)
1062
0
                            package_result.m_tx_results.empty());
1063
0
                    break;
1064
0
                }
1065
0
            }
1066
1067
0
            size_t num_broadcast{0};
1068
0
            for (const auto& tx : txns) {
1069
                // We don't want to re-submit the txn for validation in BroadcastTransaction
1070
0
                if (!mempool.exists(tx->GetHash())) {
1071
0
                    continue;
1072
0
                }
1073
1074
                // We do not expect an error here; we are only broadcasting things already/still in mempool
1075
0
                std::string err_string;
1076
0
                const auto err = BroadcastTransaction(node,
1077
0
                                                      tx,
1078
0
                                                      err_string,
1079
0
                                                      /*max_tx_fee=*/0,
1080
0
                                                      node::TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL,
1081
0
                                                      /*wait_callback=*/true);
1082
0
                if (err != TransactionError::OK) {
1083
0
                    throw JSONRPCTransactionError(err,
1084
0
                        strprintf("transaction broadcast failed: %s (%d transactions were broadcast successfully)",
Line
Count
Source
1172
0
#define strprintf tfm::format
1085
0
                            err_string, num_broadcast));
1086
0
                }
1087
0
                num_broadcast++;
1088
0
            }
1089
1090
0
            UniValue rpc_result{UniValue::VOBJ};
1091
0
            rpc_result.pushKV("package_msg", package_msg);
1092
0
            UniValue tx_result_map{UniValue::VOBJ};
1093
0
            std::set<Txid> replaced_txids;
1094
0
            for (const auto& tx : txns) {
1095
0
                UniValue result_inner{UniValue::VOBJ};
1096
0
                result_inner.pushKV("txid", tx->GetHash().GetHex());
1097
0
                const auto wtxid_hex = tx->GetWitnessHash().GetHex();
1098
0
                auto it = package_result.m_tx_results.find(tx->GetWitnessHash());
1099
0
                if (it == package_result.m_tx_results.end()) {
1100
                    // No per-tx result for this wtxid
1101
                    // Current invariant: per-tx results are all-or-none (every member or empty on package abort).
1102
                    // If any exist yet this one is missing, it's an unexpected partial map.
1103
0
                    CHECK_NONFATAL(package_result.m_tx_results.empty());
Line
Count
Source
110
0
    inline_check_non_fatal(condition, std::source_location::current(), #condition)
1104
0
                    result_inner.pushKV("error", "package-not-validated");
1105
0
                    tx_result_map.pushKV(wtxid_hex, std::move(result_inner));
1106
0
                    continue;
1107
0
                }
1108
0
                const auto& tx_result = it->second;
1109
0
                switch(it->second.m_result_type) {
1110
0
                case MempoolAcceptResult::ResultType::DIFFERENT_WITNESS:
1111
0
                    result_inner.pushKV("other-wtxid", it->second.m_other_wtxid.value().GetHex());
1112
0
                    break;
1113
0
                case MempoolAcceptResult::ResultType::INVALID:
1114
0
                    result_inner.pushKV("error", it->second.m_state.ToString());
1115
0
                    break;
1116
0
                case MempoolAcceptResult::ResultType::VALID:
1117
0
                case MempoolAcceptResult::ResultType::MEMPOOL_ENTRY:
1118
0
                    result_inner.pushKV("vsize", int64_t{it->second.m_vsize.value()});
1119
0
                    UniValue fees(UniValue::VOBJ);
1120
0
                    fees.pushKV("base", ValueFromAmount(it->second.m_base_fees.value()));
1121
0
                    if (tx_result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
1122
                        // Effective feerate is not provided for MEMPOOL_ENTRY transactions even
1123
                        // though modified fees is known, because it is unknown whether package
1124
                        // feerate was used when it was originally submitted.
1125
0
                        fees.pushKV("effective-feerate", ValueFromAmount(tx_result.m_effective_feerate.value().GetFeePerK()));
1126
0
                        UniValue effective_includes_res(UniValue::VARR);
1127
0
                        for (const auto& wtxid : tx_result.m_wtxids_fee_calculations.value()) {
1128
0
                            effective_includes_res.push_back(wtxid.ToString());
1129
0
                        }
1130
0
                        fees.pushKV("effective-includes", std::move(effective_includes_res));
1131
0
                    }
1132
0
                    result_inner.pushKV("fees", std::move(fees));
1133
0
                    for (const auto& ptx : it->second.m_replaced_transactions) {
1134
0
                        replaced_txids.insert(ptx->GetHash());
1135
0
                    }
1136
0
                    break;
1137
0
                }
1138
0
                tx_result_map.pushKV(wtxid_hex, std::move(result_inner));
1139
0
            }
1140
0
            rpc_result.pushKV("tx-results", std::move(tx_result_map));
1141
0
            UniValue replaced_list(UniValue::VARR);
1142
0
            for (const auto& txid : replaced_txids) replaced_list.push_back(txid.ToString());
1143
0
            rpc_result.pushKV("replaced-transactions", std::move(replaced_list));
1144
0
            return rpc_result;
1145
0
        },
1146
0
    };
1147
0
}
1148
1149
void RegisterMempoolRPCCommands(CRPCTable& t)
1150
0
{
1151
0
    static const CRPCCommand commands[]{
1152
0
        {"rawtransactions", &sendrawtransaction},
1153
0
        {"rawtransactions", &testmempoolaccept},
1154
0
        {"blockchain", &getmempoolancestors},
1155
0
        {"blockchain", &getmempooldescendants},
1156
0
        {"blockchain", &getmempoolentry},
1157
0
        {"blockchain", &gettxspendingprevout},
1158
0
        {"blockchain", &getmempoolinfo},
1159
0
        {"blockchain", &getrawmempool},
1160
0
        {"blockchain", &importmempool},
1161
0
        {"blockchain", &savemempool},
1162
0
        {"hidden", &getorphantxs},
1163
0
        {"rawtransactions", &submitpackage},
1164
0
    };
1165
0
    for (const auto& c : commands) {
1166
0
        t.appendCommand(c.name, &c);
1167
0
    }
1168
0
}