Bitcoin Core Fuzz Coverage Report

Coverage Report

Created: 2026-03-24 13:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/root/bitcoin/src/rpc/mempool.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 <node/mempool_persist.h>
9
10
#include <chainparams.h>
11
#include <common/args.h>
12
#include <consensus/validation.h>
13
#include <core_io.h>
14
#include <index/txospenderindex.h>
15
#include <kernel/mempool_entry.h>
16
#include <net_processing.h>
17
#include <netbase.h>
18
#include <node/mempool_persist_args.h>
19
#include <node/types.h>
20
#include <policy/rbf.h>
21
#include <policy/settings.h>
22
#include <primitives/transaction.h>
23
#include <rpc/server.h>
24
#include <rpc/server_util.h>
25
#include <rpc/util.h>
26
#include <txmempool.h>
27
#include <univalue.h>
28
#include <util/fs.h>
29
#include <util/moneystr.h>
30
#include <util/strencodings.h>
31
#include <util/time.h>
32
#include <util/vector.h>
33
34
#include <map>
35
#include <string_view>
36
#include <utility>
37
38
using node::DumpMempool;
39
40
using node::DEFAULT_MAX_BURN_AMOUNT;
41
using node::DEFAULT_MAX_RAW_TX_FEE_RATE;
42
using node::MempoolPath;
43
using node::NodeContext;
44
using node::TransactionError;
45
using util::ToString;
46
47
static RPCHelpMan sendrawtransaction()
48
0
{
49
0
    return RPCHelpMan{
50
0
        "sendrawtransaction",
51
0
        "Submit a raw transaction (serialized, hex-encoded) to the network.\n"
52
53
0
        "\nIf -privatebroadcast is disabled, then the transaction will be put into the\n"
54
0
        "local mempool of the node and will be sent unconditionally to all currently\n"
55
0
        "connected peers, so using sendrawtransaction for manual rebroadcast will degrade\n"
56
0
        "privacy by leaking the transaction's origin, as nodes will normally not\n"
57
0
        "rebroadcast non-wallet transactions already in their mempool.\n"
58
59
0
        "\nIf -privatebroadcast is enabled, then the transaction will be sent only via\n"
60
0
        "dedicated, short-lived connections to Tor or I2P peers or IPv4/IPv6 peers\n"
61
0
        "via the Tor network. This conceals the transaction's origin. The transaction\n"
62
0
        "will only enter the local mempool when it is received back from the network.\n"
63
64
0
        "\nA specific exception, RPC_TRANSACTION_ALREADY_IN_UTXO_SET, may throw if the transaction cannot be added to the mempool.\n"
65
66
0
        "\nRelated RPCs: createrawtransaction, signrawtransactionwithkey\n",
67
0
        {
68
0
            {"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
69
0
            {"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},
70
0
             "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
71
0
                 "/kvB.\nFee rates larger than 1BTC/kvB are rejected.\nSet to 0 to accept any fee rate."},
72
0
            {"maxburnamount", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_BURN_AMOUNT)},
73
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"
74
0
             "If burning funds through unspendable outputs is desired, increase this value.\n"
75
0
             "This check is based on heuristics and does not guarantee spendability of outputs.\n"},
76
0
        },
77
0
        RPCResult{
78
0
            RPCResult::Type::STR_HEX, "", "The transaction hash in hex"
79
0
        },
80
0
        RPCExamples{
81
0
            "\nCreate a transaction\n"
82
0
            + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") +
83
0
            "Sign the transaction, and get back the hex\n"
84
0
            + HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") +
85
0
            "\nSend the transaction (signed hex)\n"
86
0
            + HelpExampleCli("sendrawtransaction", "\"signedhex\"") +
87
0
            "\nAs a JSON-RPC call\n"
88
0
            + HelpExampleRpc("sendrawtransaction", "\"signedhex\"")
89
0
                },
90
0
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
91
0
        {
92
0
            const CAmount max_burn_amount = request.params[2].isNull() ? 0 : AmountFromValue(request.params[2]);
93
94
0
            CMutableTransaction mtx;
95
0
            if (!DecodeHexTx(mtx, request.params[0].get_str())) {
96
0
                throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed. Make sure the tx has at least one input.");
97
0
            }
98
99
0
            for (const auto& out : mtx.vout) {
100
0
                if((out.scriptPubKey.IsUnspendable() || !out.scriptPubKey.HasValidOps()) && out.nValue > max_burn_amount) {
101
0
                    throw JSONRPCTransactionError(TransactionError::MAX_BURN_EXCEEDED);
102
0
                }
103
0
            }
104
105
0
            CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
106
107
0
            const CFeeRate max_raw_tx_fee_rate{ParseFeeRate(self.Arg<UniValue>("maxfeerate"))};
108
109
0
            int64_t virtual_size = GetVirtualTransactionSize(*tx);
110
0
            CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size);
111
112
0
            std::string err_string;
113
0
            AssertLockNotHeld(cs_main);
Line
Count
Source
147
0
#define AssertLockNotHeld(cs) AssertLockNotHeldInline(#cs, __FILE__, __LINE__, &cs)
114
0
            NodeContext& node = EnsureAnyNodeContext(request.context);
115
0
            const bool private_broadcast_enabled{gArgs.GetBoolArg("-privatebroadcast", DEFAULT_PRIVATE_BROADCAST)};
116
0
            if (private_broadcast_enabled &&
117
0
                !g_reachable_nets.Contains(NET_ONION) &&
118
0
                !g_reachable_nets.Contains(NET_I2P)) {
119
0
                throw JSONRPCError(RPC_MISC_ERROR,
120
0
                                   "-privatebroadcast is enabled, but none of the Tor or I2P networks is "
121
0
                                   "reachable. Maybe the location of the Tor proxy couldn't be retrieved "
122
0
                                   "from the Tor daemon at startup. Check whether the Tor daemon is running "
123
0
                                   "and that -torcontrol, -torpassword and -i2psam are configured properly.");
124
0
            }
125
0
            const auto method = private_broadcast_enabled ? node::TxBroadcast::NO_MEMPOOL_PRIVATE_BROADCAST
126
0
                                                          : node::TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL;
127
0
            const TransactionError err = BroadcastTransaction(node,
128
0
                                                              tx,
129
0
                                                              err_string,
130
0
                                                              max_raw_tx_fee,
131
0
                                                              method,
132
0
                                                              /*wait_callback=*/true);
133
0
            if (TransactionError::OK != err) {
134
0
                throw JSONRPCTransactionError(err, err_string);
135
0
            }
136
137
0
            return tx->GetHash().GetHex();
138
0
        },
139
0
    };
140
0
}
141
142
static RPCHelpMan getprivatebroadcastinfo()
143
0
{
144
0
    return RPCHelpMan{
145
0
        "getprivatebroadcastinfo",
146
0
        "Returns information about transactions that are currently being privately broadcast.\n",
147
0
        {},
148
0
        RPCResult{
149
0
            RPCResult::Type::OBJ, "", "",
150
0
            {
151
0
                {RPCResult::Type::ARR, "transactions", "",
152
0
                    {
153
0
                        {RPCResult::Type::OBJ, "", "",
154
0
                            {
155
0
                                {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
156
0
                                {RPCResult::Type::STR_HEX, "wtxid", "The transaction witness hash in hex"},
157
0
                                {RPCResult::Type::STR_HEX, "hex", "The serialized, hex-encoded transaction data"},
158
0
                                {RPCResult::Type::ARR, "peers", "Per-peer send and acknowledgment information for this transaction",
159
0
                                    {
160
0
                                        {RPCResult::Type::OBJ, "", "",
161
0
                                            {
162
0
                                                {RPCResult::Type::STR, "address", "The address of the peer to which the transaction was sent"},
163
0
                                                {RPCResult::Type::NUM_TIME, "sent", "The time this transaction was picked for sending to this peer via private broadcast (seconds since epoch)"},
164
0
                                                {RPCResult::Type::NUM_TIME, "received", /*optional=*/true, "The time this peer acknowledged reception of the transaction (seconds since epoch)"},
165
0
                                            }},
166
0
                                    }},
167
0
                            }},
168
0
                    }},
169
0
            }},
170
0
        RPCExamples{
171
0
            HelpExampleCli("getprivatebroadcastinfo", "")
172
0
            + HelpExampleRpc("getprivatebroadcastinfo", "")
173
0
        },
174
0
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
175
0
        {
176
0
            const NodeContext& node{EnsureAnyNodeContext(request.context)};
177
0
            const PeerManager& peerman{EnsurePeerman(node)};
178
0
            const auto txs{peerman.GetPrivateBroadcastInfo()};
179
180
0
            UniValue transactions(UniValue::VARR);
181
0
            for (const auto& tx_info : txs) {
182
0
                UniValue o(UniValue::VOBJ);
183
0
                o.pushKV("txid", tx_info.tx->GetHash().ToString());
184
0
                o.pushKV("wtxid", tx_info.tx->GetWitnessHash().ToString());
185
0
                o.pushKV("hex", EncodeHexTx(*tx_info.tx));
186
0
                UniValue peers(UniValue::VARR);
187
0
                for (const auto& peer : tx_info.peers) {
188
0
                    UniValue p(UniValue::VOBJ);
189
0
                    p.pushKV("address", peer.address.ToStringAddrPort());
190
0
                    p.pushKV("sent", TicksSinceEpoch<std::chrono::seconds>(peer.sent));
191
0
                    if (peer.received.has_value()) {
192
0
                        p.pushKV("received", TicksSinceEpoch<std::chrono::seconds>(*peer.received));
193
0
                    }
194
0
                    peers.push_back(std::move(p));
195
0
                }
196
0
                o.pushKV("peers", std::move(peers));
197
0
                transactions.push_back(std::move(o));
198
0
            }
199
200
0
            UniValue ret(UniValue::VOBJ);
201
0
            ret.pushKV("transactions", std::move(transactions));
202
0
            return ret;
203
0
        },
204
0
    };
205
0
}
206
207
static RPCHelpMan abortprivatebroadcast()
208
0
{
209
0
    return RPCHelpMan{
210
0
        "abortprivatebroadcast",
211
0
        "Abort private broadcast attempts for a transaction currently being privately broadcast.\n"
212
0
        "The transaction will be removed from the private broadcast queue.\n",
213
0
        {
214
0
            {"id", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A transaction identifier to abort. It will be matched against both txid and wtxid for all transactions in the private broadcast queue.\n"
215
0
                                                                "If the provided id matches a txid that corresponds to multiple transactions with different wtxids, multiple transactions will be removed and returned."},
216
0
        },
217
0
        RPCResult{
218
0
            RPCResult::Type::OBJ, "", "",
219
0
            {
220
0
                {RPCResult::Type::ARR, "removed_transactions", "Transactions removed from the private broadcast queue",
221
0
                    {
222
0
                        {RPCResult::Type::OBJ, "", "",
223
0
                            {
224
0
                                {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
225
0
                                {RPCResult::Type::STR_HEX, "wtxid", "The transaction witness hash in hex"},
226
0
                                {RPCResult::Type::STR_HEX, "hex", "The serialized, hex-encoded transaction data"},
227
0
                            }},
228
0
                    }},
229
0
            }
230
0
        },
231
0
        RPCExamples{
232
0
            HelpExampleCli("abortprivatebroadcast", "\"id\"")
233
0
            + HelpExampleRpc("abortprivatebroadcast", "\"id\"")
234
0
        },
235
0
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
236
0
        {
237
0
            const uint256 id{ParseHashV(self.Arg<UniValue>("id"), "id")};
238
239
0
            const NodeContext& node{EnsureAnyNodeContext(request.context)};
240
0
            PeerManager& peerman{EnsurePeerman(node)};
241
242
0
            const auto removed_txs{peerman.AbortPrivateBroadcast(id)};
243
0
            if (removed_txs.empty()) {
244
0
                throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in private broadcast queue. Check getprivatebroadcastinfo.");
245
0
            }
246
247
0
            UniValue removed_transactions(UniValue::VARR);
248
0
            for (const auto& tx : removed_txs) {
249
0
                UniValue o(UniValue::VOBJ);
250
0
                o.pushKV("txid", tx->GetHash().ToString());
251
0
                o.pushKV("wtxid", tx->GetWitnessHash().ToString());
252
0
                o.pushKV("hex", EncodeHexTx(*tx));
253
0
                removed_transactions.push_back(std::move(o));
254
0
            }
255
0
            UniValue ret(UniValue::VOBJ);
256
0
            ret.pushKV("removed_transactions", std::move(removed_transactions));
257
0
            return ret;
258
0
        },
259
0
    };
260
0
}
261
262
static RPCHelpMan testmempoolaccept()
263
0
{
264
0
    return RPCHelpMan{
265
0
        "testmempoolaccept",
266
0
        "Returns result of mempool acceptance tests indicating if raw transaction(s) (serialized, hex-encoded) would be accepted by mempool.\n"
267
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"
268
0
        "\nIf one transaction fails, other transactions may not be fully validated (the 'allowed' key will be blank).\n"
269
0
        "\nThe maximum number of transactions allowed is " + ToString(MAX_PACKAGE_COUNT) + ".\n"
270
0
        "\nThis checks if transactions violate the consensus or policy rules.\n"
271
0
        "\nSee sendrawtransaction call.\n",
272
0
        {
273
0
            {"rawtxs", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of hex strings of raw transactions.",
274
0
                {
275
0
                    {"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
276
0
                },
277
0
            },
278
0
            {"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},
279
0
             "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
280
0
                 "/kvB.\nFee rates larger than 1BTC/kvB are rejected.\nSet to 0 to accept any fee rate."},
281
0
        },
282
0
        RPCResult{
283
0
            RPCResult::Type::ARR, "", "The result of the mempool acceptance test for each raw transaction in the input array.\n"
284
0
                                      "Returns results for each transaction in the same order they were passed in.\n"
285
0
                                      "Transactions that cannot be fully validated due to failures in other transactions will not contain an 'allowed' result.\n",
286
0
            {
287
0
                {RPCResult::Type::OBJ, "", "",
288
0
                {
289
0
                    {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
290
0
                    {RPCResult::Type::STR_HEX, "wtxid", "The transaction witness hash in hex"},
291
0
                    {RPCResult::Type::STR, "package-error", /*optional=*/true, "Package validation error, if any (only possible if rawtxs had more than 1 transaction)."},
292
0
                    {RPCResult::Type::BOOL, "allowed", /*optional=*/true, "Whether this tx would be accepted to the mempool and pass client-specified maxfeerate. "
293
0
                                                       "If not present, the tx was not fully validated due to a failure in another tx in the list."},
294
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)"},
295
0
                    {RPCResult::Type::OBJ, "fees", /*optional=*/true, "Transaction fees (only present if 'allowed' is true)",
296
0
                    {
297
0
                        {RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT},
298
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."},
299
0
                        {RPCResult::Type::ARR, "effective-includes", /*optional=*/false, "transactions whose fees and vsizes are included in effective-feerate.",
300
0
                            {RPCResult{RPCResult::Type::STR_HEX, "", "transaction wtxid in hex"},
301
0
                        }},
302
0
                    }},
303
0
                    {RPCResult::Type::STR, "reject-reason", /*optional=*/true, "Rejection reason (only present when 'allowed' is false)"},
304
0
                    {RPCResult::Type::STR, "reject-details", /*optional=*/true, "Rejection details (only present when 'allowed' is false and rejection details exist)"},
305
0
                }},
306
0
            }
307
0
        },
308
0
        RPCExamples{
309
0
            "\nCreate a transaction\n"
310
0
            + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") +
311
0
            "Sign the transaction, and get back the hex\n"
312
0
            + HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") +
313
0
            "\nTest acceptance of the transaction (signed hex)\n"
314
0
            + HelpExampleCli("testmempoolaccept", R"('["signedhex"]')") +
315
0
            "\nAs a JSON-RPC call\n"
316
0
            + HelpExampleRpc("testmempoolaccept", "[\"signedhex\"]")
317
0
                },
318
0
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
319
0
        {
320
0
            const UniValue raw_transactions = request.params[0].get_array();
321
0
            if (raw_transactions.size() < 1 || raw_transactions.size() > MAX_PACKAGE_COUNT) {
322
0
                throw JSONRPCError(RPC_INVALID_PARAMETER,
323
0
                                   "Array must contain between 1 and " + ToString(MAX_PACKAGE_COUNT) + " transactions.");
324
0
            }
325
326
0
            const CFeeRate max_raw_tx_fee_rate{ParseFeeRate(self.Arg<UniValue>("maxfeerate"))};
327
328
0
            std::vector<CTransactionRef> txns;
329
0
            txns.reserve(raw_transactions.size());
330
0
            for (const auto& rawtx : raw_transactions.getValues()) {
331
0
                CMutableTransaction mtx;
332
0
                if (!DecodeHexTx(mtx, rawtx.get_str())) {
333
0
                    throw JSONRPCError(RPC_DESERIALIZATION_ERROR,
334
0
                                       "TX decode failed: " + rawtx.get_str() + " Make sure the tx has at least one input.");
335
0
                }
336
0
                txns.emplace_back(MakeTransactionRef(std::move(mtx)));
337
0
            }
338
339
0
            NodeContext& node = EnsureAnyNodeContext(request.context);
340
0
            CTxMemPool& mempool = EnsureMemPool(node);
341
0
            ChainstateManager& chainman = EnsureChainman(node);
342
0
            Chainstate& chainstate = chainman.ActiveChainstate();
343
0
            const PackageMempoolAcceptResult package_result = [&] {
344
0
                LOCK(::cs_main);
Line
Count
Source
266
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
345
0
                if (txns.size() > 1) return ProcessNewPackage(chainstate, mempool, txns, /*test_accept=*/true, /*client_maxfeerate=*/{});
346
0
                return PackageMempoolAcceptResult(txns[0]->GetWitnessHash(),
347
0
                                                  chainman.ProcessTransaction(txns[0], /*test_accept=*/true));
348
0
            }();
349
350
0
            UniValue rpc_result(UniValue::VARR);
351
            // We will check transaction fees while we iterate through txns in order. If any transaction fee
352
            // exceeds maxfeerate, we will leave the rest of the validation results blank, because it
353
            // doesn't make sense to return a validation result for a transaction if its ancestor(s) would
354
            // not be submitted.
355
0
            bool exit_early{false};
356
0
            for (const auto& tx : txns) {
357
0
                UniValue result_inner(UniValue::VOBJ);
358
0
                result_inner.pushKV("txid", tx->GetHash().GetHex());
359
0
                result_inner.pushKV("wtxid", tx->GetWitnessHash().GetHex());
360
0
                if (package_result.m_state.GetResult() == PackageValidationResult::PCKG_POLICY) {
361
0
                    result_inner.pushKV("package-error", package_result.m_state.ToString());
362
0
                }
363
0
                auto it = package_result.m_tx_results.find(tx->GetWitnessHash());
364
0
                if (exit_early || it == package_result.m_tx_results.end()) {
365
                    // Validation unfinished. Just return the txid and wtxid.
366
0
                    rpc_result.push_back(std::move(result_inner));
367
0
                    continue;
368
0
                }
369
0
                const auto& tx_result = it->second;
370
                // Package testmempoolaccept doesn't allow transactions to already be in the mempool.
371
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)
372
0
                if (tx_result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
373
0
                    const CAmount fee = tx_result.m_base_fees.value();
374
                    // Check that fee does not exceed maximum fee
375
0
                    const int64_t virtual_size = tx_result.m_vsize.value();
376
0
                    const CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size);
377
0
                    if (max_raw_tx_fee && fee > max_raw_tx_fee) {
378
0
                        result_inner.pushKV("allowed", false);
379
0
                        result_inner.pushKV("reject-reason", "max-fee-exceeded");
380
0
                        exit_early = true;
381
0
                    } else {
382
                        // Only return the fee and vsize if the transaction would pass ATMP.
383
                        // These can be used to calculate the feerate.
384
0
                        result_inner.pushKV("allowed", true);
385
0
                        result_inner.pushKV("vsize", virtual_size);
386
0
                        UniValue fees(UniValue::VOBJ);
387
0
                        fees.pushKV("base", ValueFromAmount(fee));
388
0
                        fees.pushKV("effective-feerate", ValueFromAmount(tx_result.m_effective_feerate.value().GetFeePerK()));
389
0
                        UniValue effective_includes_res(UniValue::VARR);
390
0
                        for (const auto& wtxid : tx_result.m_wtxids_fee_calculations.value()) {
391
0
                            effective_includes_res.push_back(wtxid.ToString());
392
0
                        }
393
0
                        fees.pushKV("effective-includes", std::move(effective_includes_res));
394
0
                        result_inner.pushKV("fees", std::move(fees));
395
0
                    }
396
0
                } else {
397
0
                    result_inner.pushKV("allowed", false);
398
0
                    const TxValidationState state = tx_result.m_state;
399
0
                    if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
400
0
                        result_inner.pushKV("reject-reason", "missing-inputs");
401
0
                    } else {
402
0
                        result_inner.pushKV("reject-reason", state.GetRejectReason());
403
0
                        result_inner.pushKV("reject-details", state.ToString());
404
0
                    }
405
0
                }
406
0
                rpc_result.push_back(std::move(result_inner));
407
0
            }
408
0
            return rpc_result;
409
0
        },
410
0
    };
411
0
}
412
413
static std::vector<RPCResult> ClusterDescription()
414
0
{
415
0
    return {
416
0
        RPCResult{RPCResult::Type::NUM, "clusterweight", "total sigops-adjusted weight (as defined in BIP 141 and modified by '-bytespersigop')"},
417
0
        RPCResult{RPCResult::Type::NUM, "txcount", "number of transactions"},
418
0
        RPCResult{RPCResult::Type::ARR, "chunks", "chunks in this cluster (in mining order)",
419
0
            {RPCResult{RPCResult::Type::OBJ, "chunk", "",
420
0
                {
421
0
                    RPCResult{RPCResult::Type::NUM, "chunkfee", "fees of the transactions in this chunk"},
422
0
                    RPCResult{RPCResult::Type::NUM, "chunkweight", "sigops-adjusted weight of all transactions in this chunk"},
423
0
                    RPCResult{RPCResult::Type::ARR, "txs", "transactions in this chunk in mining order",
424
0
                        {RPCResult{RPCResult::Type::STR_HEX, "txid", "transaction id"}}},
425
0
                }
426
0
            }}
427
0
        }
428
0
    };
429
0
}
430
431
static std::vector<RPCResult> MempoolEntryDescription()
432
0
{
433
0
    return {
434
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."},
435
0
        RPCResult{RPCResult::Type::NUM, "weight", "transaction weight as defined in BIP 141."},
436
0
        RPCResult{RPCResult::Type::NUM_TIME, "time", "local time transaction entered pool in seconds since 1 Jan 1970 GMT"},
437
0
        RPCResult{RPCResult::Type::NUM, "height", "block height when transaction entered pool"},
438
0
        RPCResult{RPCResult::Type::NUM, "descendantcount", "number of in-mempool descendant transactions (including this one)"},
439
0
        RPCResult{RPCResult::Type::NUM, "descendantsize", "virtual transaction size of in-mempool descendants (including this one)"},
440
0
        RPCResult{RPCResult::Type::NUM, "ancestorcount", "number of in-mempool ancestor transactions (including this one)"},
441
0
        RPCResult{RPCResult::Type::NUM, "ancestorsize", "virtual transaction size of in-mempool ancestors (including this one)"},
442
0
        RPCResult{RPCResult::Type::NUM, "chunkweight", "sigops-adjusted weight (as defined in BIP 141 and modified by '-bytespersigop') of this transaction's chunk"},
443
0
        RPCResult{RPCResult::Type::STR_HEX, "wtxid", "hash of serialized transaction, including witness data"},
444
0
        RPCResult{RPCResult::Type::OBJ, "fees", "",
445
0
            {
446
0
                RPCResult{RPCResult::Type::STR_AMOUNT, "base", "transaction fee, denominated in " + CURRENCY_UNIT},
447
0
                RPCResult{RPCResult::Type::STR_AMOUNT, "modified", "transaction fee with fee deltas used for mining priority, denominated in " + CURRENCY_UNIT},
448
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},
449
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},
450
0
                RPCResult{RPCResult::Type::STR_AMOUNT, "chunk", "transaction fees of chunk, denominated in " + CURRENCY_UNIT},
451
0
            }},
452
0
        RPCResult{RPCResult::Type::ARR, "depends", "unconfirmed transactions used as inputs for this transaction",
453
0
            {RPCResult{RPCResult::Type::STR_HEX, "transactionid", "parent transaction id"}}},
454
0
        RPCResult{RPCResult::Type::ARR, "spentby", "unconfirmed transactions spending outputs from this transaction",
455
0
            {RPCResult{RPCResult::Type::STR_HEX, "transactionid", "child transaction id"}}},
456
0
        RPCResult{RPCResult::Type::BOOL, "bip125-replaceable", "Whether this transaction signals BIP125 replaceability or has an unconfirmed ancestor signaling BIP125 replaceability. (DEPRECATED)\n"},
457
0
        RPCResult{RPCResult::Type::BOOL, "unbroadcast", "Whether this transaction is currently unbroadcast (initial broadcast not yet acknowledged by any peers)"},
458
0
    };
459
0
}
460
461
void AppendChunkInfo(UniValue& all_chunks, FeePerWeight chunk_feerate, std::vector<const CTxMemPoolEntry *> chunk_txs)
462
0
{
463
0
    UniValue chunk(UniValue::VOBJ);
464
0
    chunk.pushKV("chunkfee", ValueFromAmount(chunk_feerate.fee));
465
0
    chunk.pushKV("chunkweight", chunk_feerate.size);
466
0
    UniValue chunk_txids(UniValue::VARR);
467
0
    for (const auto& chunk_tx : chunk_txs) {
468
0
        chunk_txids.push_back(chunk_tx->GetTx().GetHash().ToString());
469
0
    }
470
0
    chunk.pushKV("txs", std::move(chunk_txids));
471
0
    all_chunks.push_back(std::move(chunk));
472
0
}
473
474
static void clusterToJSON(const CTxMemPool& pool, UniValue& info, std::vector<const CTxMemPoolEntry *> cluster) EXCLUSIVE_LOCKS_REQUIRED(pool.cs)
475
0
{
476
0
    AssertLockHeld(pool.cs);
Line
Count
Source
142
0
#define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs)
477
0
    int total_weight{0};
478
0
    for (const auto& tx : cluster) {
479
0
        total_weight += tx->GetAdjustedWeight();
480
0
    }
481
0
    info.pushKV("clusterweight", total_weight);
482
0
    info.pushKV("txcount", cluster.size());
483
484
    // Output the cluster by chunk. This isn't handed to us by the mempool, but
485
    // we can calculate it by looking at the chunk feerates of each transaction
486
    // in the cluster.
487
0
    FeePerWeight current_chunk_feerate = pool.GetMainChunkFeerate(*cluster[0]);
488
0
    std::vector<const CTxMemPoolEntry *> current_chunk;
489
0
    current_chunk.reserve(cluster.size());
490
491
0
    UniValue all_chunks(UniValue::VARR);
492
0
    for (const auto& tx : cluster) {
493
0
        if (current_chunk_feerate.size == 0) {
494
            // We've iterated all the transactions in the previous chunk; so
495
            // append it to the output.
496
0
            AppendChunkInfo(all_chunks, pool.GetMainChunkFeerate(*current_chunk[0]), current_chunk);
497
0
            current_chunk.clear();
498
0
            current_chunk_feerate = pool.GetMainChunkFeerate(*tx);
499
0
        }
500
0
        current_chunk.push_back(tx);
501
0
        current_chunk_feerate.size -= tx->GetAdjustedWeight();
502
0
    }
503
0
    AppendChunkInfo(all_chunks, pool.GetMainChunkFeerate(*current_chunk[0]), current_chunk);
504
0
    current_chunk.clear();
505
0
    info.pushKV("chunks", std::move(all_chunks));
506
0
}
507
508
static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPoolEntry& e) EXCLUSIVE_LOCKS_REQUIRED(pool.cs)
509
0
{
510
0
    AssertLockHeld(pool.cs);
Line
Count
Source
142
0
#define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs)
511
512
0
    auto [ancestor_count, ancestor_size, ancestor_fees] = pool.CalculateAncestorData(e);
513
0
    auto [descendant_count, descendant_size, descendant_fees] = pool.CalculateDescendantData(e);
514
515
0
    info.pushKV("vsize", e.GetTxSize());
516
0
    info.pushKV("weight", e.GetTxWeight());
517
0
    info.pushKV("time", count_seconds(e.GetTime()));
518
0
    info.pushKV("height", e.GetHeight());
519
0
    info.pushKV("descendantcount", descendant_count);
520
0
    info.pushKV("descendantsize", descendant_size);
521
0
    info.pushKV("ancestorcount", ancestor_count);
522
0
    info.pushKV("ancestorsize", ancestor_size);
523
0
    info.pushKV("wtxid", e.GetTx().GetWitnessHash().ToString());
524
0
    auto feerate = pool.GetMainChunkFeerate(e);
525
0
    info.pushKV("chunkweight", feerate.size);
526
527
0
    UniValue fees(UniValue::VOBJ);
528
0
    fees.pushKV("base", ValueFromAmount(e.GetFee()));
529
0
    fees.pushKV("modified", ValueFromAmount(e.GetModifiedFee()));
530
0
    fees.pushKV("ancestor", ValueFromAmount(ancestor_fees));
531
0
    fees.pushKV("descendant", ValueFromAmount(descendant_fees));
532
0
    fees.pushKV("chunk", ValueFromAmount(feerate.fee));
533
0
    info.pushKV("fees", std::move(fees));
534
535
0
    const CTransaction& tx = e.GetTx();
536
0
    std::set<std::string> setDepends;
537
0
    for (const CTxIn& txin : tx.vin)
538
0
    {
539
0
        if (pool.exists(txin.prevout.hash))
540
0
            setDepends.insert(txin.prevout.hash.ToString());
541
0
    }
542
543
0
    UniValue depends(UniValue::VARR);
544
0
    for (const std::string& dep : setDepends)
545
0
    {
546
0
        depends.push_back(dep);
547
0
    }
548
549
0
    info.pushKV("depends", std::move(depends));
550
551
0
    UniValue spent(UniValue::VARR);
552
0
    for (const CTxMemPoolEntry& child : pool.GetChildren(e)) {
553
0
        spent.push_back(child.GetTx().GetHash().ToString());
554
0
    }
555
556
0
    info.pushKV("spentby", std::move(spent));
557
558
    // Add opt-in RBF status
559
0
    bool rbfStatus = false;
560
0
    RBFTransactionState rbfState = IsRBFOptIn(tx, pool);
561
0
    if (rbfState == RBFTransactionState::UNKNOWN) {
562
0
        throw JSONRPCError(RPC_MISC_ERROR, "Transaction is not in mempool");
563
0
    } else if (rbfState == RBFTransactionState::REPLACEABLE_BIP125) {
564
0
        rbfStatus = true;
565
0
    }
566
567
0
    info.pushKV("bip125-replaceable", rbfStatus);
568
0
    info.pushKV("unbroadcast", pool.IsUnbroadcastTx(tx.GetHash()));
569
0
}
570
571
UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose, bool include_mempool_sequence)
572
0
{
573
0
    if (verbose) {
574
0
        if (include_mempool_sequence) {
575
0
            throw JSONRPCError(RPC_INVALID_PARAMETER, "Verbose results cannot contain mempool sequence values.");
576
0
        }
577
0
        LOCK(pool.cs);
Line
Count
Source
266
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
578
0
        UniValue o(UniValue::VOBJ);
579
0
        for (const CTxMemPoolEntry& e : pool.entryAll()) {
580
0
            UniValue info(UniValue::VOBJ);
581
0
            entryToJSON(pool, info, e);
582
            // Mempool has unique entries so there is no advantage in using
583
            // UniValue::pushKV, which checks if the key already exists in O(N).
584
            // UniValue::pushKVEnd is used instead which currently is O(1).
585
0
            o.pushKVEnd(e.GetTx().GetHash().ToString(), std::move(info));
586
0
        }
587
0
        return o;
588
0
    } else {
589
0
        UniValue a(UniValue::VARR);
590
0
        uint64_t mempool_sequence;
591
0
        {
592
0
            LOCK(pool.cs);
Line
Count
Source
266
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
593
0
            for (const CTxMemPoolEntry& e : pool.entryAll()) {
594
0
                a.push_back(e.GetTx().GetHash().ToString());
595
0
            }
596
0
            mempool_sequence = pool.GetSequence();
597
0
        }
598
0
        if (!include_mempool_sequence) {
599
0
            return a;
600
0
        } else {
601
0
            UniValue o(UniValue::VOBJ);
602
0
            o.pushKV("txids", std::move(a));
603
0
            o.pushKV("mempool_sequence", mempool_sequence);
604
0
            return o;
605
0
        }
606
0
    }
607
0
}
608
609
static RPCHelpMan getmempoolfeeratediagram()
610
0
{
611
0
    return RPCHelpMan{"getmempoolfeeratediagram",
612
0
        "Returns the feerate diagram for the whole mempool.",
613
0
        {},
614
0
        {
615
0
            RPCResult{"mempool chunks",
616
0
                RPCResult::Type::ARR, "", "",
617
0
                {
618
0
                    {
619
0
                        RPCResult::Type::OBJ, "", "",
620
0
                        {
621
0
                            {RPCResult::Type::NUM, "weight", "cumulative sigops-adjusted weight"},
622
0
                            {RPCResult::Type::NUM, "fee", "cumulative fee"}
623
0
                        }
624
0
                    }
625
0
                }
626
0
            }
627
0
        },
628
0
        RPCExamples{
629
0
            HelpExampleCli("getmempoolfeeratediagram", "")
630
0
            + HelpExampleRpc("getmempoolfeeratediagram", "")
631
0
        },
632
0
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
633
0
        {
634
0
            const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
635
0
            LOCK(mempool.cs);
Line
Count
Source
266
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
636
637
0
            UniValue result(UniValue::VARR);
638
639
0
            auto diagram = mempool.GetFeerateDiagram();
640
641
0
            for (auto f : diagram) {
642
0
                UniValue o(UniValue::VOBJ);
643
0
                o.pushKV("weight", f.size);
644
0
                o.pushKV("fee", ValueFromAmount(f.fee));
645
0
                result.push_back(o);
646
0
            }
647
0
            return result;
648
0
        }
649
0
    };
650
0
}
651
652
static RPCHelpMan getrawmempool()
653
0
{
654
0
    return RPCHelpMan{
655
0
        "getrawmempool",
656
0
        "Returns all transaction ids in memory pool as a json array of string transaction ids.\n"
657
0
        "\nHint: use getmempoolentry to fetch a specific transaction from the mempool.\n",
658
0
        {
659
0
            {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"},
660
0
            {"mempool_sequence", RPCArg::Type::BOOL, RPCArg::Default{false}, "If verbose=false, returns a json object with transaction list and mempool sequence number attached."},
661
0
        },
662
0
        {
663
0
            RPCResult{"for verbose = false",
664
0
                RPCResult::Type::ARR, "", "",
665
0
                {
666
0
                    {RPCResult::Type::STR_HEX, "", "The transaction id"},
667
0
                }},
668
0
            RPCResult{"for verbose = true",
669
0
                RPCResult::Type::OBJ_DYN, "", "",
670
0
                {
671
0
                    {RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()},
672
0
                }},
673
0
            RPCResult{"for verbose = false and mempool_sequence = true",
674
0
                RPCResult::Type::OBJ, "", "",
675
0
                {
676
0
                    {RPCResult::Type::ARR, "txids", "",
677
0
                    {
678
0
                        {RPCResult::Type::STR_HEX, "", "The transaction id"},
679
0
                    }},
680
0
                    {RPCResult::Type::NUM, "mempool_sequence", "The mempool sequence value."},
681
0
                }},
682
0
        },
683
0
        RPCExamples{
684
0
            HelpExampleCli("getrawmempool", "true")
685
0
            + HelpExampleRpc("getrawmempool", "true")
686
0
        },
687
0
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
688
0
{
689
0
    bool fVerbose = false;
690
0
    if (!request.params[0].isNull())
691
0
        fVerbose = request.params[0].get_bool();
692
693
0
    bool include_mempool_sequence = false;
694
0
    if (!request.params[1].isNull()) {
695
0
        include_mempool_sequence = request.params[1].get_bool();
696
0
    }
697
698
0
    return MempoolToJSON(EnsureAnyMemPool(request.context), fVerbose, include_mempool_sequence);
699
0
},
700
0
    };
701
0
}
702
703
static RPCHelpMan getmempoolancestors()
704
0
{
705
0
    return RPCHelpMan{
706
0
        "getmempoolancestors",
707
0
        "If txid is in the mempool, returns all in-mempool ancestors.\n",
708
0
        {
709
0
            {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
710
0
            {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"},
711
0
        },
712
0
        {
713
0
            RPCResult{"for verbose = false",
714
0
                RPCResult::Type::ARR, "", "",
715
0
                {{RPCResult::Type::STR_HEX, "", "The transaction id of an in-mempool ancestor transaction"}}},
716
0
            RPCResult{"for verbose = true",
717
0
                RPCResult::Type::OBJ_DYN, "", "",
718
0
                {
719
0
                    {RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()},
720
0
                }},
721
0
        },
722
0
        RPCExamples{
723
0
            HelpExampleCli("getmempoolancestors", "\"mytxid\"")
724
0
            + HelpExampleRpc("getmempoolancestors", "\"mytxid\"")
725
0
        },
726
0
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
727
0
{
728
0
    bool fVerbose = false;
729
0
    if (!request.params[1].isNull())
730
0
        fVerbose = request.params[1].get_bool();
731
732
0
    auto txid{Txid::FromUint256(ParseHashV(request.params[0], "txid"))};
733
734
0
    const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
735
0
    LOCK(mempool.cs);
Line
Count
Source
266
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
736
737
0
    const auto entry{mempool.GetEntry(txid)};
738
0
    if (entry == nullptr) {
739
0
        throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
740
0
    }
741
742
0
    auto ancestors{mempool.CalculateMemPoolAncestors(*entry)};
743
744
0
    if (!fVerbose) {
745
0
        UniValue o(UniValue::VARR);
746
0
        for (CTxMemPool::txiter ancestorIt : ancestors) {
747
0
            o.push_back(ancestorIt->GetTx().GetHash().ToString());
748
0
        }
749
0
        return o;
750
0
    } else {
751
0
        UniValue o(UniValue::VOBJ);
752
0
        for (CTxMemPool::txiter ancestorIt : ancestors) {
753
0
            const CTxMemPoolEntry &e = *ancestorIt;
754
0
            UniValue info(UniValue::VOBJ);
755
0
            entryToJSON(mempool, info, e);
756
0
            o.pushKV(e.GetTx().GetHash().ToString(), std::move(info));
757
0
        }
758
0
        return o;
759
0
    }
760
0
},
761
0
    };
762
0
}
763
764
static RPCHelpMan getmempooldescendants()
765
0
{
766
0
    return RPCHelpMan{
767
0
        "getmempooldescendants",
768
0
        "If txid is in the mempool, returns all in-mempool descendants.\n",
769
0
        {
770
0
            {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
771
0
            {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"},
772
0
        },
773
0
        {
774
0
            RPCResult{"for verbose = false",
775
0
                RPCResult::Type::ARR, "", "",
776
0
                {{RPCResult::Type::STR_HEX, "", "The transaction id of an in-mempool descendant transaction"}}},
777
0
            RPCResult{"for verbose = true",
778
0
                RPCResult::Type::OBJ_DYN, "", "",
779
0
                {
780
0
                    {RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()},
781
0
                }},
782
0
        },
783
0
        RPCExamples{
784
0
            HelpExampleCli("getmempooldescendants", "\"mytxid\"")
785
0
            + HelpExampleRpc("getmempooldescendants", "\"mytxid\"")
786
0
        },
787
0
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
788
0
{
789
0
    bool fVerbose = false;
790
0
    if (!request.params[1].isNull())
791
0
        fVerbose = request.params[1].get_bool();
792
793
0
    auto txid{Txid::FromUint256(ParseHashV(request.params[0], "txid"))};
794
795
0
    const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
796
0
    LOCK(mempool.cs);
Line
Count
Source
266
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
797
798
0
    const auto it{mempool.GetIter(txid)};
799
0
    if (!it) {
800
0
        throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
801
0
    }
802
803
0
    CTxMemPool::setEntries setDescendants;
804
0
    mempool.CalculateDescendants(*it, setDescendants);
805
    // CTxMemPool::CalculateDescendants will include the given tx
806
0
    setDescendants.erase(*it);
807
808
0
    if (!fVerbose) {
809
0
        UniValue o(UniValue::VARR);
810
0
        for (CTxMemPool::txiter descendantIt : setDescendants) {
811
0
            o.push_back(descendantIt->GetTx().GetHash().ToString());
812
0
        }
813
814
0
        return o;
815
0
    } else {
816
0
        UniValue o(UniValue::VOBJ);
817
0
        for (CTxMemPool::txiter descendantIt : setDescendants) {
818
0
            const CTxMemPoolEntry &e = *descendantIt;
819
0
            UniValue info(UniValue::VOBJ);
820
0
            entryToJSON(mempool, info, e);
821
0
            o.pushKV(e.GetTx().GetHash().ToString(), std::move(info));
822
0
        }
823
0
        return o;
824
0
    }
825
0
},
826
0
    };
827
0
}
828
829
static RPCHelpMan getmempoolcluster()
830
0
{
831
0
    return RPCHelpMan{"getmempoolcluster",
832
0
        "Returns mempool data for given cluster\n",
833
0
        {
834
0
            {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The txid of a transaction in the cluster"},
835
0
        },
836
0
        RPCResult{
837
0
            RPCResult::Type::OBJ, "", "", ClusterDescription()},
838
0
        RPCExamples{
839
0
            HelpExampleCli("getmempoolcluster", "txid")
840
0
            + HelpExampleRpc("getmempoolcluster", "txid")
841
0
        },
842
0
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
843
0
{
844
0
    uint256 hash = ParseHashV(request.params[0], "txid");
845
846
0
    const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
847
0
    LOCK(mempool.cs);
Line
Count
Source
266
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
848
849
0
    auto txid = Txid::FromUint256(hash);
850
0
    const auto entry{mempool.GetEntry(txid)};
851
0
    if (entry == nullptr) {
852
0
        throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
853
0
    }
854
855
0
    auto cluster = mempool.GetCluster(txid);
856
857
0
    UniValue info(UniValue::VOBJ);
858
0
    clusterToJSON(mempool, info, cluster);
859
0
    return info;
860
0
},
861
0
    };
862
0
}
863
864
static RPCHelpMan getmempoolentry()
865
0
{
866
0
    return RPCHelpMan{
867
0
        "getmempoolentry",
868
0
        "Returns mempool data for given transaction\n",
869
0
        {
870
0
            {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
871
0
        },
872
0
        RPCResult{
873
0
            RPCResult::Type::OBJ, "", "", MempoolEntryDescription()},
874
0
        RPCExamples{
875
0
            HelpExampleCli("getmempoolentry", "\"mytxid\"")
876
0
            + HelpExampleRpc("getmempoolentry", "\"mytxid\"")
877
0
        },
878
0
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
879
0
{
880
0
    auto txid{Txid::FromUint256(ParseHashV(request.params[0], "txid"))};
881
882
0
    const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
883
0
    LOCK(mempool.cs);
Line
Count
Source
266
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
884
885
0
    const auto entry{mempool.GetEntry(txid)};
886
0
    if (entry == nullptr) {
887
0
        throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
888
0
    }
889
890
0
    UniValue info(UniValue::VOBJ);
891
0
    entryToJSON(mempool, info, *entry);
892
0
    return info;
893
0
},
894
0
    };
895
0
}
896
897
static RPCHelpMan gettxspendingprevout()
898
0
{
899
0
    return RPCHelpMan{"gettxspendingprevout",
900
0
        "Scans the mempool (and the txospenderindex, if available) to find transactions spending any of the given outputs",
901
0
        {
902
0
            {"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The transaction outputs that we want to check, and within each, the txid (string) vout (numeric).",
903
0
                {
904
0
                    {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
905
0
                        {
906
0
                            {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
907
0
                            {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
908
0
                        },
909
0
                    },
910
0
                },
911
0
            },
912
0
            {"options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "",
913
0
                {
914
0
                    {"mempool_only", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true if txospenderindex unavailable, otherwise false"}, "If false and mempool lacks a relevant spend, use txospenderindex (throws an exception if not available)."},
915
0
                    {"return_spending_tx", RPCArg::Type::BOOL, RPCArg::DefaultHint{"false"}, "If true, return the full spending tx."},
916
0
                },
917
0
            },
918
0
        },
919
0
        RPCResult{
920
0
            RPCResult::Type::ARR, "", "",
921
0
            {
922
0
                {RPCResult::Type::OBJ, "", "",
923
0
                {
924
0
                    {RPCResult::Type::STR_HEX, "txid", "the transaction id of the checked output"},
925
0
                    {RPCResult::Type::NUM, "vout", "the vout value of the checked output"},
926
0
                    {RPCResult::Type::STR_HEX, "spendingtxid", /*optional=*/true, "the transaction id of the mempool transaction spending this output (omitted if unspent)"},
927
0
                    {RPCResult::Type::STR_HEX, "spendingtx", /*optional=*/true, "the transaction spending this output (only if return_spending_tx is set, omitted if unspent)"},
928
0
                    {RPCResult::Type::STR_HEX, "blockhash", /*optional=*/true, "the hash of the spending block (omitted if unspent or the spending tx is not confirmed)"},
929
0
                }},
930
0
            }
931
0
        },
932
0
        RPCExamples{
933
0
            HelpExampleCli("gettxspendingprevout", "\"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":3}]\"")
934
0
            + HelpExampleRpc("gettxspendingprevout", "\"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":3}]\"")
935
0
            + HelpExampleCliNamed("gettxspendingprevout", {{"outputs", "[{\"txid\":\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\",\"vout\":3}]"}, {"return_spending_tx", true}})
936
0
        },
937
0
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
938
0
        {
939
0
            const UniValue& output_params = request.params[0].get_array();
940
0
            if (output_params.empty()) {
941
0
                throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, outputs are missing");
942
0
            }
943
0
            const UniValue options{request.params[1].isNull() ? UniValue::VOBJ : request.params[1]};
944
0
            RPCTypeCheckObj(options,
945
0
                            {
946
0
                                {"mempool_only", UniValueType(UniValue::VBOOL)},
947
0
                                {"return_spending_tx", UniValueType(UniValue::VBOOL)},
948
0
                            }, /*fAllowNull=*/true, /*fStrict=*/true);
949
950
0
            const bool mempool_only{options.exists("mempool_only") ? options["mempool_only"].get_bool() : !g_txospenderindex};
951
0
            const bool return_spending_tx{options.exists("return_spending_tx") ? options["return_spending_tx"].get_bool() : false};
952
953
            // Worklist of outpoints to resolve
954
0
            struct Entry {
955
0
                COutPoint outpoint;
956
0
                const UniValue* raw;
957
0
            };
958
0
            std::vector<Entry> prevouts_to_process;
959
0
            prevouts_to_process.reserve(output_params.size());
960
0
            for (unsigned int idx = 0; idx < output_params.size(); idx++) {
961
0
                const UniValue& o = output_params[idx].get_obj();
962
963
0
                RPCTypeCheckObj(o,
964
0
                                {
965
0
                                    {"txid", UniValueType(UniValue::VSTR)},
966
0
                                    {"vout", UniValueType(UniValue::VNUM)},
967
0
                                }, /*fAllowNull=*/false, /*fStrict=*/true);
968
969
0
                const Txid txid = Txid::FromUint256(ParseHashO(o, "txid"));
970
0
                const int nOutput{o.find_value("vout").getInt<int>()};
971
0
                if (nOutput < 0) {
972
0
                    throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
973
0
                }
974
0
                prevouts_to_process.emplace_back(COutPoint{txid, static_cast<uint32_t>(nOutput)}, &o);
975
0
            }
976
977
0
            auto make_output = [return_spending_tx](const Entry& prevout, const CTransaction* spending_tx = nullptr) {
978
0
                UniValue o{*prevout.raw};
979
0
                if (spending_tx) {
980
0
                    o.pushKV("spendingtxid", spending_tx->GetHash().ToString());
981
0
                    if (return_spending_tx) {
982
0
                        o.pushKV("spendingtx", EncodeHexTx(*spending_tx));
983
0
                    }
984
0
                }
985
0
                return o;
986
0
            };
987
988
0
            UniValue result{UniValue::VARR};
989
990
            // Search the mempool first
991
0
            {
992
0
                const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
993
0
                LOCK(mempool.cs);
Line
Count
Source
266
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
994
995
                // Make the result if the spending tx appears in the mempool or this is a mempool_only request
996
0
                for (auto it = prevouts_to_process.begin(); it != prevouts_to_process.end(); ) {
997
0
                    const CTransaction* spending_tx{mempool.GetConflictTx(it->outpoint)};
998
999
                    // If the outpoint is not spent in the mempool and this is not a mempool-only
1000
                    // request, we cannot answer it yet.
1001
0
                    if (!spending_tx && !mempool_only) {
1002
0
                        ++it;
1003
0
                        continue;
1004
0
                    }
1005
1006
0
                    result.push_back(make_output(*it, spending_tx));
1007
0
                    it = prevouts_to_process.erase(it);
1008
0
                }
1009
0
            }
1010
1011
            // Return early if all requests have been handled by the mempool search
1012
0
            if (prevouts_to_process.empty()) {
1013
0
                return result;
1014
0
            }
1015
1016
            // At this point the request was not limited to the mempool and some outpoints remain
1017
            // unresolved. We now rely on the index to determine whether they were spent or not.
1018
0
            if (!g_txospenderindex || !g_txospenderindex->BlockUntilSyncedToCurrentChain()) {
1019
0
                throw JSONRPCError(RPC_MISC_ERROR, "Mempool lacks a relevant spend, and txospenderindex is unavailable.");
1020
0
            }
1021
1022
0
            for (const auto& prevout : prevouts_to_process) {
1023
0
                const auto spender{g_txospenderindex->FindSpender(prevout.outpoint)};
1024
0
                if (!spender) {
1025
0
                    throw JSONRPCError(RPC_MISC_ERROR, spender.error());
1026
0
                }
1027
1028
0
                if (const auto& spender_opt{spender.value()}) {
1029
0
                    UniValue o{make_output(prevout, spender_opt->tx.get())};
1030
0
                    o.pushKV("blockhash", spender_opt->block_hash.GetHex());
1031
0
                    result.push_back(std::move(o));
1032
0
                } else {
1033
                    // Only return the input outpoint itself, which indicates it is unspent.
1034
0
                    result.push_back(make_output(prevout));
1035
0
                }
1036
0
            }
1037
1038
0
            return result;
1039
0
        },
1040
0
    };
1041
0
}
1042
1043
UniValue MempoolInfoToJSON(const CTxMemPool& pool)
1044
0
{
1045
    // Make sure this call is atomic in the pool.
1046
0
    LOCK(pool.cs);
Line
Count
Source
266
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
1047
0
    UniValue ret(UniValue::VOBJ);
1048
0
    ret.pushKV("loaded", pool.GetLoadTried());
1049
0
    ret.pushKV("size", pool.size());
1050
0
    ret.pushKV("bytes", pool.GetTotalTxSize());
1051
0
    ret.pushKV("usage", pool.DynamicMemoryUsage());
1052
0
    ret.pushKV("total_fee", ValueFromAmount(pool.GetTotalFee()));
1053
0
    ret.pushKV("maxmempool", pool.m_opts.max_size_bytes);
1054
0
    ret.pushKV("mempoolminfee", ValueFromAmount(std::max(pool.GetMinFee(), pool.m_opts.min_relay_feerate).GetFeePerK()));
1055
0
    ret.pushKV("minrelaytxfee", ValueFromAmount(pool.m_opts.min_relay_feerate.GetFeePerK()));
1056
0
    ret.pushKV("incrementalrelayfee", ValueFromAmount(pool.m_opts.incremental_relay_feerate.GetFeePerK()));
1057
0
    ret.pushKV("unbroadcastcount", pool.GetUnbroadcastTxs().size());
1058
0
    ret.pushKV("fullrbf", true);
1059
0
    ret.pushKV("permitbaremultisig", pool.m_opts.permit_bare_multisig);
1060
0
    ret.pushKV("maxdatacarriersize", pool.m_opts.max_datacarrier_bytes.value_or(0));
1061
0
    ret.pushKV("limitclustercount", pool.m_opts.limits.cluster_count);
1062
0
    ret.pushKV("limitclustersize", pool.m_opts.limits.cluster_size_vbytes);
1063
0
    ret.pushKV("optimal", pool.m_txgraph->DoWork(0)); // 0 work is a quick check for known optimality
1064
0
    return ret;
1065
0
}
1066
1067
static RPCHelpMan getmempoolinfo()
1068
0
{
1069
0
    return RPCHelpMan{"getmempoolinfo",
1070
0
        "Returns details on the active state of the TX memory pool.",
1071
0
        {},
1072
0
        RPCResult{
1073
0
            RPCResult::Type::OBJ, "", "",
1074
0
            {
1075
0
                {RPCResult::Type::BOOL, "loaded", "True if the initial load attempt of the persisted mempool finished"},
1076
0
                {RPCResult::Type::NUM, "size", "Current tx count"},
1077
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"},
1078
0
                {RPCResult::Type::NUM, "usage", "Total memory usage for the mempool"},
1079
0
                {RPCResult::Type::STR_AMOUNT, "total_fee", "Total fees for the mempool in " + CURRENCY_UNIT + ", ignoring modified fees through prioritisetransaction"},
1080
0
                {RPCResult::Type::NUM, "maxmempool", "Maximum memory usage for the mempool"},
1081
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"},
1082
0
                {RPCResult::Type::STR_AMOUNT, "minrelaytxfee", "Current minimum relay fee for transactions"},
1083
0
                {RPCResult::Type::NUM, "incrementalrelayfee", "minimum fee rate increment for mempool limiting or replacement in " + CURRENCY_UNIT + "/kvB"},
1084
0
                {RPCResult::Type::NUM, "unbroadcastcount", "Current number of transactions that haven't passed initial broadcast yet"},
1085
0
                {RPCResult::Type::BOOL, "fullrbf", "True if the mempool accepts RBF without replaceability signaling inspection (DEPRECATED)"},
1086
0
                {RPCResult::Type::BOOL, "permitbaremultisig", "True if the mempool accepts transactions with bare multisig outputs"},
1087
0
                {RPCResult::Type::NUM, "maxdatacarriersize", "Maximum number of bytes that can be used by OP_RETURN outputs in the mempool"},
1088
0
                {RPCResult::Type::NUM, "limitclustercount", "Maximum number of transactions that can be in a cluster (configured by -limitclustercount)"},
1089
0
                {RPCResult::Type::NUM, "limitclustersize", "Maximum size of a cluster in virtual bytes (configured by -limitclustersize)"},
1090
0
                {RPCResult::Type::BOOL, "optimal", "If the mempool is in a known-optimal transaction ordering"},
1091
0
            }},
1092
0
        RPCExamples{
1093
0
            HelpExampleCli("getmempoolinfo", "")
1094
0
            + HelpExampleRpc("getmempoolinfo", "")
1095
0
        },
1096
0
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1097
0
{
1098
0
    return MempoolInfoToJSON(EnsureAnyMemPool(request.context));
1099
0
},
1100
0
    };
1101
0
}
1102
1103
static RPCHelpMan importmempool()
1104
0
{
1105
0
    return RPCHelpMan{
1106
0
        "importmempool",
1107
0
        "Import a mempool.dat file and attempt to add its contents to the mempool.\n"
1108
0
        "Warning: Importing untrusted files is dangerous, especially if metadata from the file is taken over.",
1109
0
        {
1110
0
            {"filepath", RPCArg::Type::STR, RPCArg::Optional::NO, "The mempool file"},
1111
0
            {"options",
1112
0
             RPCArg::Type::OBJ_NAMED_PARAMS,
1113
0
             RPCArg::Optional::OMITTED,
1114
0
             "",
1115
0
             {
1116
0
                 {"use_current_time", RPCArg::Type::BOOL, RPCArg::Default{true},
1117
0
                  "Whether to use the current system time or use the entry time metadata from the mempool file.\n"
1118
0
                  "Warning: Importing untrusted metadata may lead to unexpected issues and undesirable behavior."},
1119
0
                 {"apply_fee_delta_priority", RPCArg::Type::BOOL, RPCArg::Default{false},
1120
0
                  "Whether to apply the fee delta metadata from the mempool file.\n"
1121
0
                  "It will be added to any existing fee deltas.\n"
1122
0
                  "The fee delta can be set by the prioritisetransaction RPC.\n"
1123
0
                  "Warning: Importing untrusted metadata may lead to unexpected issues and undesirable behavior.\n"
1124
0
                  "Only set this bool if you understand what it does."},
1125
0
                 {"apply_unbroadcast_set", RPCArg::Type::BOOL, RPCArg::Default{false},
1126
0
                  "Whether to apply the unbroadcast set metadata from the mempool file.\n"
1127
0
                  "Warning: Importing untrusted metadata may lead to unexpected issues and undesirable behavior."},
1128
0
             },
1129
0
             RPCArgOptions{.oneline_description = "options"}},
1130
0
        },
1131
0
        RPCResult{RPCResult::Type::OBJ, "", "", std::vector<RPCResult>{}},
1132
0
        RPCExamples{HelpExampleCli("importmempool", "/path/to/mempool.dat") + HelpExampleRpc("importmempool", "/path/to/mempool.dat")},
1133
0
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
1134
0
            const NodeContext& node{EnsureAnyNodeContext(request.context)};
1135
1136
0
            CTxMemPool& mempool{EnsureMemPool(node)};
1137
0
            ChainstateManager& chainman = EnsureChainman(node);
1138
0
            Chainstate& chainstate = chainman.ActiveChainstate();
1139
1140
0
            if (chainman.IsInitialBlockDownload()) {
1141
0
                throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Can only import the mempool after the block download and sync is done.");
1142
0
            }
1143
1144
0
            const fs::path load_path{fs::u8path(self.Arg<std::string_view>("filepath"))};
1145
0
            const UniValue& use_current_time{request.params[1]["use_current_time"]};
1146
0
            const UniValue& apply_fee_delta{request.params[1]["apply_fee_delta_priority"]};
1147
0
            const UniValue& apply_unbroadcast{request.params[1]["apply_unbroadcast_set"]};
1148
0
            node::ImportMempoolOptions opts{
1149
0
                .use_current_time = use_current_time.isNull() ? true : use_current_time.get_bool(),
1150
0
                .apply_fee_delta_priority = apply_fee_delta.isNull() ? false : apply_fee_delta.get_bool(),
1151
0
                .apply_unbroadcast_set = apply_unbroadcast.isNull() ? false : apply_unbroadcast.get_bool(),
1152
0
            };
1153
1154
0
            if (!node::LoadMempool(mempool, load_path, chainstate, std::move(opts))) {
1155
0
                throw JSONRPCError(RPC_MISC_ERROR, "Unable to import mempool file, see debug log for details.");
1156
0
            }
1157
1158
0
            UniValue ret{UniValue::VOBJ};
1159
0
            return ret;
1160
0
        },
1161
0
    };
1162
0
}
1163
1164
static RPCHelpMan savemempool()
1165
0
{
1166
0
    return RPCHelpMan{
1167
0
        "savemempool",
1168
0
        "Dumps the mempool to disk. It will fail until the previous dump is fully loaded.\n",
1169
0
        {},
1170
0
        RPCResult{
1171
0
            RPCResult::Type::OBJ, "", "",
1172
0
            {
1173
0
                {RPCResult::Type::STR, "filename", "the directory and file where the mempool was saved"},
1174
0
            }},
1175
0
        RPCExamples{
1176
0
            HelpExampleCli("savemempool", "")
1177
0
            + HelpExampleRpc("savemempool", "")
1178
0
        },
1179
0
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1180
0
{
1181
0
    const ArgsManager& args{EnsureAnyArgsman(request.context)};
1182
0
    const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
1183
1184
0
    if (!mempool.GetLoadTried()) {
1185
0
        throw JSONRPCError(RPC_MISC_ERROR, "The mempool was not loaded yet");
1186
0
    }
1187
1188
0
    const fs::path& dump_path = MempoolPath(args);
1189
1190
0
    if (!DumpMempool(mempool, dump_path)) {
1191
0
        throw JSONRPCError(RPC_MISC_ERROR, "Unable to dump mempool to disk");
1192
0
    }
1193
1194
0
    UniValue ret(UniValue::VOBJ);
1195
0
    ret.pushKV("filename", dump_path.utf8string());
1196
1197
0
    return ret;
1198
0
},
1199
0
    };
1200
0
}
1201
1202
static std::vector<RPCResult> OrphanDescription()
1203
0
{
1204
0
    return {
1205
0
        RPCResult{RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
1206
0
        RPCResult{RPCResult::Type::STR_HEX, "wtxid", "The transaction witness hash in hex"},
1207
0
        RPCResult{RPCResult::Type::NUM, "bytes", "The serialized transaction size in bytes"},
1208
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."},
1209
0
        RPCResult{RPCResult::Type::NUM, "weight", "The transaction weight as defined in BIP 141."},
1210
0
        RPCResult{RPCResult::Type::ARR, "from", "",
1211
0
        {
1212
0
            RPCResult{RPCResult::Type::NUM, "peer_id", "Peer ID"},
1213
0
        }},
1214
0
    };
1215
0
}
1216
1217
static UniValue OrphanToJSON(const node::TxOrphanage::OrphanInfo& orphan)
1218
0
{
1219
0
    UniValue o(UniValue::VOBJ);
1220
0
    o.pushKV("txid", orphan.tx->GetHash().ToString());
1221
0
    o.pushKV("wtxid", orphan.tx->GetWitnessHash().ToString());
1222
0
    o.pushKV("bytes", orphan.tx->ComputeTotalSize());
1223
0
    o.pushKV("vsize", GetVirtualTransactionSize(*orphan.tx));
1224
0
    o.pushKV("weight", GetTransactionWeight(*orphan.tx));
1225
0
    UniValue from(UniValue::VARR);
1226
0
    for (const auto fromPeer: orphan.announcers) {
1227
0
        from.push_back(fromPeer);
1228
0
    }
1229
0
    o.pushKV("from", from);
1230
0
    return o;
1231
0
}
1232
1233
static RPCHelpMan getorphantxs()
1234
0
{
1235
0
    return RPCHelpMan{
1236
0
        "getorphantxs",
1237
0
        "Shows transactions in the tx orphanage.\n"
1238
0
        "\nEXPERIMENTAL warning: this call may be changed in future releases.\n",
1239
0
        {
1240
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",
1241
0
             RPCArgOptions{.skip_type_check = true}},
1242
0
        },
1243
0
        {
1244
0
            RPCResult{"for verbose = 0",
1245
0
                RPCResult::Type::ARR, "", "",
1246
0
                {
1247
0
                    {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
1248
0
                }},
1249
0
            RPCResult{"for verbose = 1",
1250
0
                RPCResult::Type::ARR, "", "",
1251
0
                {
1252
0
                    {RPCResult::Type::OBJ, "", "", OrphanDescription()},
1253
0
                }},
1254
0
            RPCResult{"for verbose = 2",
1255
0
                RPCResult::Type::ARR, "", "",
1256
0
                {
1257
0
                    {RPCResult::Type::OBJ, "", "",
1258
0
                        Cat<std::vector<RPCResult>>(
1259
0
                            OrphanDescription(),
1260
0
                            {{RPCResult::Type::STR_HEX, "hex", "The serialized, hex-encoded transaction data"}}
1261
0
                        )
1262
0
                    },
1263
0
                }},
1264
0
        },
1265
0
        RPCExamples{
1266
0
            HelpExampleCli("getorphantxs", "2")
1267
0
            + HelpExampleRpc("getorphantxs", "2")
1268
0
        },
1269
0
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1270
0
        {
1271
0
            const NodeContext& node = EnsureAnyNodeContext(request.context);
1272
0
            PeerManager& peerman = EnsurePeerman(node);
1273
0
            std::vector<node::TxOrphanage::OrphanInfo> orphanage = peerman.GetOrphanTransactions();
1274
1275
0
            int verbosity{ParseVerbosity(request.params[0], /*default_verbosity=*/0, /*allow_bool=*/false)};
1276
1277
0
            UniValue ret(UniValue::VARR);
1278
1279
0
            if (verbosity == 0) {
1280
0
                for (auto const& orphan : orphanage) {
1281
0
                    ret.push_back(orphan.tx->GetHash().ToString());
1282
0
                }
1283
0
            } else if (verbosity == 1) {
1284
0
                for (auto const& orphan : orphanage) {
1285
0
                    ret.push_back(OrphanToJSON(orphan));
1286
0
                }
1287
0
            } else if (verbosity == 2) {
1288
0
                for (auto const& orphan : orphanage) {
1289
0
                    UniValue o{OrphanToJSON(orphan)};
1290
0
                    o.pushKV("hex", EncodeHexTx(*orphan.tx));
1291
0
                    ret.push_back(o);
1292
0
                }
1293
0
            } else {
1294
0
                throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid verbosity value " + ToString(verbosity));
1295
0
            }
1296
1297
0
            return ret;
1298
0
        },
1299
0
    };
1300
0
}
1301
1302
static RPCHelpMan submitpackage()
1303
0
{
1304
0
    return RPCHelpMan{"submitpackage",
1305
0
        "Submit a package of raw transactions (serialized, hex-encoded) to local node.\n"
1306
0
        "The package will be validated according to consensus and mempool policy rules. If any transaction passes, it will be accepted to mempool.\n"
1307
0
        "This RPC is experimental and the interface may be unstable. Refer to doc/policy/packages.md for documentation on package policies.\n"
1308
0
        "Warning: successful submission does not mean the transactions will propagate throughout the network.\n"
1309
0
        ,
1310
0
        {
1311
0
            {"package", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of raw transactions.\n"
1312
0
                "The package must consist of a transaction with (some, all, or none of) its unconfirmed parents. A single transaction is permitted.\n"
1313
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"
1314
0
                "The package must be topologically sorted, with the child being the last element in the array if there are multiple elements.",
1315
0
                {
1316
0
                    {"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
1317
0
                },
1318
0
            },
1319
0
            {"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},
1320
0
             "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
1321
0
                 "/kvB.\nFee rates larger than 1BTC/kvB are rejected.\nSet to 0 to accept any fee rate."},
1322
0
            {"maxburnamount", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_BURN_AMOUNT)},
1323
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"
1324
0
             "If burning funds through unspendable outputs is desired, increase this value.\n"
1325
0
             "This check is based on heuristics and does not guarantee spendability of outputs.\n"
1326
0
            },
1327
0
        },
1328
0
        RPCResult{
1329
0
            RPCResult::Type::OBJ, "", "",
1330
0
            {
1331
0
                {RPCResult::Type::STR, "package_msg", "The transaction package result message. \"success\" indicates all transactions were accepted into or are already in the mempool."},
1332
0
                {RPCResult::Type::OBJ_DYN, "tx-results", "The transaction results keyed by wtxid. An entry is returned for every submitted wtxid.",
1333
0
                {
1334
0
                    {RPCResult::Type::OBJ, "wtxid", "transaction wtxid", {
1335
0
                        {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
1336
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."},
1337
0
                        {RPCResult::Type::NUM, "vsize", /*optional=*/true, "Sigops-adjusted virtual transaction size."},
1338
0
                        {RPCResult::Type::OBJ, "fees", /*optional=*/true, "Transaction fees", {
1339
0
                            {RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT},
1340
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."},
1341
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.",
1342
0
                                {{RPCResult::Type::STR_HEX, "", "transaction wtxid in hex"},
1343
0
                            }},
1344
0
                        }},
1345
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."},
1346
0
                    }}
1347
0
                }},
1348
0
                {RPCResult::Type::ARR, "replaced-transactions", /*optional=*/true, "List of txids of replaced transactions",
1349
0
                {
1350
0
                    {RPCResult::Type::STR_HEX, "", "The transaction id"},
1351
0
                }},
1352
0
            },
1353
0
        },
1354
0
        RPCExamples{
1355
0
            HelpExampleRpc("submitpackage", R"(["raw-parent-tx-1", "raw-parent-tx-2", "raw-child-tx"])") +
1356
0
            HelpExampleCli("submitpackage", R"('["raw-tx-without-unconfirmed-parents"]')")
1357
0
        },
1358
0
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1359
0
        {
1360
0
            const UniValue raw_transactions = request.params[0].get_array();
1361
0
            if (raw_transactions.empty() || raw_transactions.size() > MAX_PACKAGE_COUNT) {
1362
0
                throw JSONRPCError(RPC_INVALID_PARAMETER,
1363
0
                                   "Array must contain between 1 and " + ToString(MAX_PACKAGE_COUNT) + " transactions.");
1364
0
            }
1365
1366
            // Fee check needs to be run with chainstate and package context
1367
0
            const CFeeRate max_raw_tx_fee_rate{ParseFeeRate(self.Arg<UniValue>("maxfeerate"))};
1368
0
            std::optional<CFeeRate> client_maxfeerate{max_raw_tx_fee_rate};
1369
            // 0-value is special; it's mapped to no sanity check
1370
0
            if (max_raw_tx_fee_rate == CFeeRate(0)) {
1371
0
                client_maxfeerate = std::nullopt;
1372
0
            }
1373
1374
            // Burn sanity check is run with no context
1375
0
            const CAmount max_burn_amount = request.params[2].isNull() ? 0 : AmountFromValue(request.params[2]);
1376
1377
0
            std::vector<CTransactionRef> txns;
1378
0
            txns.reserve(raw_transactions.size());
1379
0
            for (const auto& rawtx : raw_transactions.getValues()) {
1380
0
                CMutableTransaction mtx;
1381
0
                if (!DecodeHexTx(mtx, rawtx.get_str())) {
1382
0
                    throw JSONRPCError(RPC_DESERIALIZATION_ERROR,
1383
0
                                       "TX decode failed: " + rawtx.get_str() + " Make sure the tx has at least one input.");
1384
0
                }
1385
1386
0
                for (const auto& out : mtx.vout) {
1387
0
                    if((out.scriptPubKey.IsUnspendable() || !out.scriptPubKey.HasValidOps()) && out.nValue > max_burn_amount) {
1388
0
                        throw JSONRPCTransactionError(TransactionError::MAX_BURN_EXCEEDED);
1389
0
                    }
1390
0
                }
1391
1392
0
                txns.emplace_back(MakeTransactionRef(std::move(mtx)));
1393
0
            }
1394
0
            CHECK_NONFATAL(!txns.empty());
Line
Count
Source
110
0
    inline_check_non_fatal(condition, std::source_location::current(), #condition)
1395
0
            if (txns.size() > 1 && !IsChildWithParentsTree(txns)) {
1396
0
                throw JSONRPCTransactionError(TransactionError::INVALID_PACKAGE, "package topology disallowed. not child-with-parents or parents depend on each other.");
1397
0
            }
1398
1399
0
            NodeContext& node = EnsureAnyNodeContext(request.context);
1400
0
            CTxMemPool& mempool = EnsureMemPool(node);
1401
0
            Chainstate& chainstate = EnsureChainman(node).ActiveChainstate();
1402
0
            const auto package_result = WITH_LOCK(::cs_main, return ProcessNewPackage(chainstate, mempool, txns, /*test_accept=*/ false, client_maxfeerate));
Line
Count
Source
297
0
#define WITH_LOCK(cs, code) (MaybeCheckNotHeld(cs), [&]() -> decltype(auto) { LOCK(cs); code; }())
1403
1404
0
            std::string package_msg = "success";
1405
1406
            // First catch package-wide errors, continue if we can
1407
0
            switch(package_result.m_state.GetResult()) {
1408
0
                case PackageValidationResult::PCKG_RESULT_UNSET:
1409
0
                {
1410
                    // Belt-and-suspenders check; everything should be successful here
1411
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)
1412
0
                    for (const auto& tx : txns) {
1413
0
                        CHECK_NONFATAL(mempool.exists(tx->GetHash()));
Line
Count
Source
110
0
    inline_check_non_fatal(condition, std::source_location::current(), #condition)
1414
0
                    }
1415
0
                    break;
1416
0
                }
1417
0
                case PackageValidationResult::PCKG_MEMPOOL_ERROR:
1418
0
                {
1419
                    // This only happens with internal bug; user should stop and report
1420
0
                    throw JSONRPCTransactionError(TransactionError::MEMPOOL_ERROR,
1421
0
                        package_result.m_state.GetRejectReason());
1422
0
                }
1423
0
                case PackageValidationResult::PCKG_POLICY:
1424
0
                case PackageValidationResult::PCKG_TX:
1425
0
                {
1426
                    // Package-wide error we want to return, but we also want to return individual responses
1427
0
                    package_msg = package_result.m_state.ToString();
1428
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)
1429
0
                            package_result.m_tx_results.empty());
1430
0
                    break;
1431
0
                }
1432
0
            }
1433
1434
0
            size_t num_broadcast{0};
1435
0
            for (const auto& tx : txns) {
1436
                // We don't want to re-submit the txn for validation in BroadcastTransaction
1437
0
                if (!mempool.exists(tx->GetHash())) {
1438
0
                    continue;
1439
0
                }
1440
1441
                // We do not expect an error here; we are only broadcasting things already/still in mempool
1442
0
                std::string err_string;
1443
0
                const auto err = BroadcastTransaction(node,
1444
0
                                                      tx,
1445
0
                                                      err_string,
1446
0
                                                      /*max_tx_fee=*/0,
1447
0
                                                      node::TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL,
1448
0
                                                      /*wait_callback=*/true);
1449
0
                if (err != TransactionError::OK) {
1450
0
                    throw JSONRPCTransactionError(err,
1451
0
                        strprintf("transaction broadcast failed: %s (%d transactions were broadcast successfully)",
Line
Count
Source
1172
0
#define strprintf tfm::format
1452
0
                            err_string, num_broadcast));
1453
0
                }
1454
0
                num_broadcast++;
1455
0
            }
1456
1457
0
            UniValue rpc_result{UniValue::VOBJ};
1458
0
            rpc_result.pushKV("package_msg", package_msg);
1459
0
            UniValue tx_result_map{UniValue::VOBJ};
1460
0
            std::set<Txid> replaced_txids;
1461
0
            for (const auto& tx : txns) {
1462
0
                UniValue result_inner{UniValue::VOBJ};
1463
0
                result_inner.pushKV("txid", tx->GetHash().GetHex());
1464
0
                const auto wtxid_hex = tx->GetWitnessHash().GetHex();
1465
0
                auto it = package_result.m_tx_results.find(tx->GetWitnessHash());
1466
0
                if (it == package_result.m_tx_results.end()) {
1467
                    // No per-tx result for this wtxid
1468
                    // Current invariant: per-tx results are all-or-none (every member or empty on package abort).
1469
                    // If any exist yet this one is missing, it's an unexpected partial map.
1470
0
                    CHECK_NONFATAL(package_result.m_tx_results.empty());
Line
Count
Source
110
0
    inline_check_non_fatal(condition, std::source_location::current(), #condition)
1471
0
                    result_inner.pushKV("error", "package-not-validated");
1472
0
                    tx_result_map.pushKV(wtxid_hex, std::move(result_inner));
1473
0
                    continue;
1474
0
                }
1475
0
                const auto& tx_result = it->second;
1476
0
                switch(it->second.m_result_type) {
1477
0
                case MempoolAcceptResult::ResultType::DIFFERENT_WITNESS:
1478
0
                    result_inner.pushKV("other-wtxid", it->second.m_other_wtxid.value().GetHex());
1479
0
                    break;
1480
0
                case MempoolAcceptResult::ResultType::INVALID:
1481
0
                    result_inner.pushKV("error", it->second.m_state.ToString());
1482
0
                    break;
1483
0
                case MempoolAcceptResult::ResultType::VALID:
1484
0
                case MempoolAcceptResult::ResultType::MEMPOOL_ENTRY:
1485
0
                    result_inner.pushKV("vsize", it->second.m_vsize.value());
1486
0
                    UniValue fees(UniValue::VOBJ);
1487
0
                    fees.pushKV("base", ValueFromAmount(it->second.m_base_fees.value()));
1488
0
                    if (tx_result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
1489
                        // Effective feerate is not provided for MEMPOOL_ENTRY transactions even
1490
                        // though modified fees is known, because it is unknown whether package
1491
                        // feerate was used when it was originally submitted.
1492
0
                        fees.pushKV("effective-feerate", ValueFromAmount(tx_result.m_effective_feerate.value().GetFeePerK()));
1493
0
                        UniValue effective_includes_res(UniValue::VARR);
1494
0
                        for (const auto& wtxid : tx_result.m_wtxids_fee_calculations.value()) {
1495
0
                            effective_includes_res.push_back(wtxid.ToString());
1496
0
                        }
1497
0
                        fees.pushKV("effective-includes", std::move(effective_includes_res));
1498
0
                    }
1499
0
                    result_inner.pushKV("fees", std::move(fees));
1500
0
                    for (const auto& ptx : it->second.m_replaced_transactions) {
1501
0
                        replaced_txids.insert(ptx->GetHash());
1502
0
                    }
1503
0
                    break;
1504
0
                }
1505
0
                tx_result_map.pushKV(wtxid_hex, std::move(result_inner));
1506
0
            }
1507
0
            rpc_result.pushKV("tx-results", std::move(tx_result_map));
1508
0
            UniValue replaced_list(UniValue::VARR);
1509
0
            for (const auto& txid : replaced_txids) replaced_list.push_back(txid.ToString());
1510
0
            rpc_result.pushKV("replaced-transactions", std::move(replaced_list));
1511
0
            return rpc_result;
1512
0
        },
1513
0
    };
1514
0
}
1515
1516
void RegisterMempoolRPCCommands(CRPCTable& t)
1517
0
{
1518
0
    static const CRPCCommand commands[]{
1519
0
        {"rawtransactions", &sendrawtransaction},
1520
0
        {"rawtransactions", &getprivatebroadcastinfo},
1521
0
        {"rawtransactions", &abortprivatebroadcast},
1522
0
        {"rawtransactions", &testmempoolaccept},
1523
0
        {"blockchain", &getmempoolancestors},
1524
0
        {"blockchain", &getmempooldescendants},
1525
0
        {"blockchain", &getmempoolentry},
1526
0
        {"blockchain", &getmempoolcluster},
1527
0
        {"blockchain", &gettxspendingprevout},
1528
0
        {"blockchain", &getmempoolinfo},
1529
0
        {"hidden", &getmempoolfeeratediagram},
1530
0
        {"blockchain", &getrawmempool},
1531
0
        {"blockchain", &importmempool},
1532
0
        {"blockchain", &savemempool},
1533
0
        {"hidden", &getorphantxs},
1534
0
        {"rawtransactions", &submitpackage},
1535
0
    };
1536
0
    for (const auto& c : commands) {
1537
0
        t.appendCommand(c.name, &c);
1538
0
    }
1539
0
}