Coverage Report

Created: 2026-07-14 18:13

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