Bitcoin Core Fuzz Coverage Report

Coverage Report

Created: 2026-05-28 15:05

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