Coverage Report

Created: 2026-06-18 19:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/root/bitcoin/src/rpc/mempool.cpp
Line
Count
Source
1
// Copyright (c) 2010 Satoshi Nakamoto
2
// Copyright (c) 2009-present The Bitcoin Core developers
3
// Distributed under the MIT software license, see the accompanying
4
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6
#include <rpc/blockchain.h>
7
8
#include <node/mempool_persist.h>
9
10
#include <chainparams.h>
11
#include <common/args.h>
12
#include <consensus/validation.h>
13
#include <core_io.h>
14
#include <index/txospenderindex.h>
15
#include <kernel/mempool_entry.h>
16
#include <net_processing.h>
17
#include <netbase.h>
18
#include <node/mempool_persist_args.h>
19
#include <node/types.h>
20
#include <policy/rbf.h>
21
#include <policy/settings.h>
22
#include <primitives/transaction.h>
23
#include <rpc/server.h>
24
#include <rpc/server_util.h>
25
#include <rpc/util.h>
26
#include <txmempool.h>
27
#include <univalue.h>
28
#include <util/fs.h>
29
#include <util/moneystr.h>
30
#include <util/strencodings.h>
31
#include <util/time.h>
32
#include <util/vector.h>
33
34
#include <map>
35
#include <string_view>
36
#include <utility>
37
38
using node::DumpMempool;
39
40
using node::DEFAULT_MAX_BURN_AMOUNT;
41
using node::DEFAULT_MAX_RAW_TX_FEE_RATE;
42
using node::MempoolPath;
43
using node::NodeContext;
44
using node::TransactionError;
45
using util::ToString;
46
47
static RPCMethod sendrawtransaction()
48
0
{
49
0
    return RPCMethod{
50
0
        "sendrawtransaction",
51
0
        "Submit a raw transaction (serialized, hex-encoded) to the network.\n"
52
53
0
        "\nIf -privatebroadcast is disabled, then the transaction will be put into the\n"
54
0
        "local mempool of the node and will be sent unconditionally to all currently\n"
55
0
        "connected peers, so using sendrawtransaction for manual rebroadcast will degrade\n"
56
0
        "privacy by leaking the transaction's origin, as nodes will normally not\n"
57
0
        "rebroadcast non-wallet transactions already in their mempool.\n"
58
59
0
        "\nIf -privatebroadcast is enabled, then the transaction will be sent only via\n"
60
0
        "dedicated, short-lived connections to Tor or I2P peers or IPv4/IPv6 peers\n"
61
0
        "via the Tor network. This conceals the transaction's origin. The transaction\n"
62
0
        "will only enter the local mempool when it is received back from the network.\n"
63
64
0
        "\nA specific exception, RPC_TRANSACTION_ALREADY_IN_UTXO_SET, may throw if the transaction cannot be added to the mempool.\n"
65
66
0
        "\nRelated RPCs: createrawtransaction, signrawtransactionwithkey\n",
67
0
        {
68
0
            {"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
69
0
            {"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},
70
0
             "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
71
0
                 "/kvB.\nFee rates larger than 1BTC/kvB are rejected.\nSet to 0 to accept any fee rate."},
72
0
            {"maxburnamount", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_BURN_AMOUNT)},
73
0
             "Reject transactions with provably unspendable outputs (e.g. 'datacarrier' outputs that use the OP_RETURN opcode) greater than the specified value, expressed in " + CURRENCY_UNIT + ".\n"
74
0
             "If burning funds through unspendable outputs is desired, increase this value.\n"
75
0
             "This check is based on heuristics and does not guarantee spendability of outputs.\n"},
76
0
        },
77
0
        RPCResult{
78
0
            RPCResult::Type::STR_HEX, "", "The transaction hash in hex"
79
0
        },
80
0
        RPCExamples{
81
0
            "\nCreate a transaction\n"
82
0
            + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") +
83
0
            "Sign the transaction, and get back the hex\n"
84
0
            + HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") +
85
0
            "\nSend the transaction (signed hex)\n"
86
0
            + HelpExampleCli("sendrawtransaction", "\"signedhex\"") +
87
0
            "\nAs a JSON-RPC call\n"
88
0
            + HelpExampleRpc("sendrawtransaction", "\"signedhex\"")
89
0
                },
90
0
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
91
0
        {
92
0
            const CAmount max_burn_amount = request.params[2].isNull() ? 0 : AmountFromValue(request.params[2]);
  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
0
    };
140
0
}
141
142
static RPCMethod getprivatebroadcastinfo()
143
0
{
144
0
    return RPCMethod{
145
0
        "getprivatebroadcastinfo",
146
0
        "Returns information about transactions that are currently being privately broadcast.\n"
147
0
        "This method is only available when running with -privatebroadcast enabled.\n",
148
0
        {},
149
0
        RPCResult{
150
0
            RPCResult::Type::OBJ, "", "",
151
0
            {
152
0
                {RPCResult::Type::ARR, "transactions", "",
153
0
                    {
154
0
                        {RPCResult::Type::OBJ, "", "",
155
0
                            {
156
0
                                {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
157
0
                                {RPCResult::Type::STR_HEX, "wtxid", "The transaction witness hash in hex"},
158
0
                                {RPCResult::Type::STR_HEX, "hex", "The serialized, hex-encoded transaction data"},
159
0
                                {RPCResult::Type::NUM_TIME, "time_added", "The time this transaction was added to the private broadcast queue (seconds since epoch)"},
160
0
                                {RPCResult::Type::ARR, "peers", "Per-peer send and acknowledgment information for this transaction",
161
0
                                    {
162
0
                                        {RPCResult::Type::OBJ, "", "",
163
0
                                            {
164
0
                                                {RPCResult::Type::STR, "address", "The address of the peer to which the transaction was sent"},
165
0
                                                {RPCResult::Type::NUM_TIME, "sent", "The time this transaction was picked for sending to this peer via private broadcast (seconds since epoch)"},
166
0
                                                {RPCResult::Type::NUM_TIME, "received", /*optional=*/true, "The time this peer acknowledged reception of the transaction (seconds since epoch)"},
167
0
                                            }},
168
0
                                    }},
169
0
                            }},
170
0
                    }},
171
0
            }},
172
0
        RPCExamples{
173
0
            HelpExampleCli("getprivatebroadcastinfo", "")
174
0
            + HelpExampleRpc("getprivatebroadcastinfo", "")
175
0
        },
176
0
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
177
0
        {
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
0
    };
212
0
}
213
214
static RPCMethod abortprivatebroadcast()
215
0
{
216
0
    return RPCMethod{
217
0
        "abortprivatebroadcast",
218
0
        "Abort private broadcast attempts for a transaction currently being privately broadcast.\n"
219
0
        "The transaction will be removed from the private broadcast queue.\n"
220
0
        "This method is only available when running with -privatebroadcast enabled.\n",
221
0
        {
222
0
            {"id", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A transaction identifier to abort. It will be matched against both txid and wtxid for all transactions in the private broadcast queue.\n"
223
0
                                                                "If the provided id matches a txid that corresponds to multiple transactions with different wtxids, multiple transactions will be removed and returned."},
224
0
        },
225
0
        RPCResult{
226
0
            RPCResult::Type::OBJ, "", "",
227
0
            {
228
0
                {RPCResult::Type::ARR, "removed_transactions", "Transactions removed from the private broadcast queue",
229
0
                    {
230
0
                        {RPCResult::Type::OBJ, "", "",
231
0
                            {
232
0
                                {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
233
0
                                {RPCResult::Type::STR_HEX, "wtxid", "The transaction witness hash in hex"},
234
0
                                {RPCResult::Type::STR_HEX, "hex", "The serialized, hex-encoded transaction data"},
235
0
                            }},
236
0
                    }},
237
0
            }
238
0
        },
239
0
        RPCExamples{
240
0
            HelpExampleCli("abortprivatebroadcast", "\"id\"")
241
0
            + HelpExampleRpc("abortprivatebroadcast", "\"id\"")
242
0
        },
243
0
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
244
0
        {
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
0
    };
272
0
}
273
274
static RPCMethod testmempoolaccept()
275
0
{
276
0
    return RPCMethod{
277
0
        "testmempoolaccept",
278
0
        "Returns result of mempool acceptance tests indicating if raw transaction(s) (serialized, hex-encoded) would be accepted by mempool.\n"
279
0
        "\nIf multiple transactions are passed in, parents must come before children and package policies apply: the transactions cannot conflict with any mempool transactions or each other.\n"
280
0
        "\nIf one transaction fails, other transactions may not be fully validated (the 'allowed' key will be blank).\n"
281
0
        "\nThe maximum number of transactions allowed is " + ToString(MAX_PACKAGE_COUNT) + ".\n"
282
0
        "\nThis checks if transactions violate the consensus or policy rules.\n"
283
0
        "\nSee sendrawtransaction call.\n",
284
0
        {
285
0
            {"rawtxs", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of hex strings of raw transactions.",
286
0
                {
287
0
                    {"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
288
0
                },
289
0
            },
290
0
            {"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},
291
0
             "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
292
0
                 "/kvB.\nFee rates larger than 1BTC/kvB are rejected.\nSet to 0 to accept any fee rate."},
293
0
        },
294
0
        RPCResult{
295
0
            RPCResult::Type::ARR, "", "The result of the mempool acceptance test for each raw transaction in the input array.\n"
296
0
                                      "Returns results for each transaction in the same order they were passed in.\n"
297
0
                                      "Transactions that cannot be fully validated due to failures in other transactions will not contain an 'allowed' result.\n",
298
0
            {
299
0
                {RPCResult::Type::OBJ, "", "",
300
0
                {
301
0
                    {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
302
0
                    {RPCResult::Type::STR_HEX, "wtxid", "The transaction witness hash in hex"},
303
0
                    {RPCResult::Type::STR, "package-error", /*optional=*/true, "Package validation error, if any (only possible if rawtxs had more than 1 transaction)."},
304
0
                    {RPCResult::Type::BOOL, "allowed", /*optional=*/true, "Whether this tx would be accepted to the mempool and pass client-specified maxfeerate. "
305
0
                                                       "If not present, the tx was not fully validated due to a failure in another tx in the list."},
306
0
                    {RPCResult::Type::NUM, "vsize", /*optional=*/true, "Virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted (only present when 'allowed' is true)"},
307
0
                    {RPCResult::Type::OBJ, "fees", /*optional=*/true, "Transaction fees (only present if 'allowed' is true)",
308
0
                    {
309
0
                        {RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT},
310
0
                        {RPCResult::Type::STR_AMOUNT, "effective-feerate", /*optional=*/false, "the effective feerate in " + CURRENCY_UNIT + " per KvB. May differ from the base feerate if, for example, there are modified fees from prioritisetransaction or a package feerate was used."},
311
0
                        {RPCResult::Type::ARR, "effective-includes", /*optional=*/false, "transactions whose fees and vsizes are included in effective-feerate.",
312
0
                            {RPCResult{RPCResult::Type::STR_HEX, "", "transaction wtxid in hex"},
313
0
                        }},
314
0
                    }},
315
0
                    {RPCResult::Type::STR, "reject-reason", /*optional=*/true, "Rejection reason (only present when 'allowed' is false)"},
316
0
                    {RPCResult::Type::STR, "reject-details", /*optional=*/true, "Rejection details (only present when 'allowed' is false and rejection details exist)"},
317
0
                }},
318
0
            }
319
0
        },
320
0
        RPCExamples{
321
0
            "\nCreate a transaction\n"
322
0
            + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") +
323
0
            "Sign the transaction, and get back the hex\n"
324
0
            + HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") +
325
0
            "\nTest acceptance of the transaction (signed hex)\n"
326
0
            + HelpExampleCli("testmempoolaccept", R"('["signedhex"]')") +
327
0
            "\nAs a JSON-RPC call\n"
328
0
            + HelpExampleRpc("testmempoolaccept", "[\"signedhex\"]")
329
0
                },
330
0
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
331
0
        {
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
0
    };
423
0
}
424
425
static std::vector<RPCResult> ClusterDescription()
426
0
{
427
0
    return {
428
0
        RPCResult{RPCResult::Type::NUM, "clusterweight", "total sigops-adjusted weight (as defined in BIP 141 and modified by '-bytespersigop')"},
429
0
        RPCResult{RPCResult::Type::NUM, "txcount", "number of transactions"},
430
0
        RPCResult{RPCResult::Type::ARR, "chunks", "chunks in this cluster (in mining order)",
431
0
            {RPCResult{RPCResult::Type::OBJ, "chunk", "",
432
0
                {
433
0
                    RPCResult{RPCResult::Type::NUM, "chunkfee", "fees of the transactions in this chunk"},
434
0
                    RPCResult{RPCResult::Type::NUM, "chunkweight", "sigops-adjusted weight of all transactions in this chunk"},
435
0
                    RPCResult{RPCResult::Type::ARR, "txs", "transactions in this chunk in mining order",
436
0
                        {RPCResult{RPCResult::Type::STR_HEX, "txid", "transaction id"}}},
437
0
                }
438
0
            }}
439
0
        }
440
0
    };
441
0
}
442
443
static std::vector<RPCResult> MempoolEntryDescription()
444
0
{
445
0
    std::vector<RPCResult> list = {
446
0
        RPCResult{RPCResult::Type::NUM, "vsize", "virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted."},
447
0
        RPCResult{RPCResult::Type::NUM, "weight", "transaction weight as defined in BIP 141."},
448
0
        RPCResult{RPCResult::Type::NUM_TIME, "time", "local time transaction entered pool in seconds since 1 Jan 1970 GMT"},
449
0
        RPCResult{RPCResult::Type::NUM, "height", "block height when transaction entered pool"},
450
0
        RPCResult{RPCResult::Type::NUM, "descendantcount", "number of in-mempool descendant transactions (including this one)"},
451
0
        RPCResult{RPCResult::Type::NUM, "descendantsize", "virtual transaction size of in-mempool descendants (including this one)"},
452
0
        RPCResult{RPCResult::Type::NUM, "ancestorcount", "number of in-mempool ancestor transactions (including this one)"},
453
0
        RPCResult{RPCResult::Type::NUM, "ancestorsize", "virtual transaction size of in-mempool ancestors (including this one)"},
454
0
        RPCResult{RPCResult::Type::NUM, "chunkweight", "sigops-adjusted weight (as defined in BIP 141 and modified by '-bytespersigop') of this transaction's chunk"},
455
0
        RPCResult{RPCResult::Type::STR_HEX, "wtxid", "hash of serialized transaction, including witness data"},
456
0
        RPCResult{RPCResult::Type::OBJ, "fees", "",
457
0
            {
458
0
                RPCResult{RPCResult::Type::STR_AMOUNT, "base", "transaction fee, denominated in " + CURRENCY_UNIT},
459
0
                RPCResult{RPCResult::Type::STR_AMOUNT, "modified", "transaction fee with fee deltas used for mining priority, denominated in " + CURRENCY_UNIT},
460
0
                RPCResult{RPCResult::Type::STR_AMOUNT, "ancestor", "transaction fees of in-mempool ancestors (including this one) with fee deltas used for mining priority, denominated in " + CURRENCY_UNIT},
461
0
                RPCResult{RPCResult::Type::STR_AMOUNT, "descendant", "transaction fees of in-mempool descendants (including this one) with fee deltas used for mining priority, denominated in " + CURRENCY_UNIT},
462
0
                RPCResult{RPCResult::Type::STR_AMOUNT, "chunk", "transaction fees of chunk, denominated in " + CURRENCY_UNIT},
463
0
            }},
464
0
        RPCResult{RPCResult::Type::ARR, "depends", "unconfirmed transactions used as inputs for this transaction",
465
0
            {RPCResult{RPCResult::Type::STR_HEX, "transactionid", "parent transaction id"}}},
466
0
        RPCResult{RPCResult::Type::ARR, "spentby", "unconfirmed transactions spending outputs from this transaction",
467
0
            {RPCResult{RPCResult::Type::STR_HEX, "transactionid", "child transaction id"}}},
468
0
        RPCResult{RPCResult::Type::BOOL, "unbroadcast", "Whether this transaction is currently unbroadcast (initial broadcast not yet acknowledged by any peers)"},
469
0
    };
470
0
    if (IsDeprecatedRPCEnabled("bip125")) {
  Branch (470:9): [True: 0, False: 0]
471
0
        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
0
    }
473
0
    return list;
474
0
}
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
0
{
627
0
    return RPCMethod{"getmempoolfeeratediagram",
628
0
        "Returns the feerate diagram for the whole mempool.",
629
0
        {},
630
0
        {
631
0
            RPCResult{"mempool chunks",
632
0
                RPCResult::Type::ARR, "", "",
633
0
                {
634
0
                    {
635
0
                        RPCResult::Type::OBJ, "", "",
636
0
                        {
637
0
                            {RPCResult::Type::NUM, "weight", "cumulative sigops-adjusted weight"},
638
0
                            {RPCResult::Type::NUM, "fee", "cumulative fee"}
639
0
                        }
640
0
                    }
641
0
                }
642
0
            }
643
0
        },
644
0
        RPCExamples{
645
0
            HelpExampleCli("getmempoolfeeratediagram", "")
646
0
            + HelpExampleRpc("getmempoolfeeratediagram", "")
647
0
        },
648
0
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
649
0
        {
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
0
    };
666
0
}
667
668
static RPCMethod getrawmempool()
669
0
{
670
0
    return RPCMethod{
671
0
        "getrawmempool",
672
0
        "Returns all transaction ids in memory pool as a json array of string transaction ids.\n"
673
0
        "\nHint: use getmempoolentry to fetch a specific transaction from the mempool.\n",
674
0
        {
675
0
            {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"},
676
0
            {"mempool_sequence", RPCArg::Type::BOOL, RPCArg::Default{false}, "If verbose=false, returns a json object with transaction list and mempool sequence number attached."},
677
0
        },
678
0
        {
679
0
            RPCResult{"for verbose = false",
680
0
                RPCResult::Type::ARR, "", "",
681
0
                {
682
0
                    {RPCResult::Type::STR_HEX, "", "The transaction id"},
683
0
                }},
684
0
            RPCResult{"for verbose = true",
685
0
                RPCResult::Type::OBJ_DYN, "", "",
686
0
                {
687
0
                    {RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()},
688
0
                }},
689
0
            RPCResult{"for verbose = false and mempool_sequence = true",
690
0
                RPCResult::Type::OBJ, "", "",
691
0
                {
692
0
                    {RPCResult::Type::ARR, "txids", "",
693
0
                    {
694
0
                        {RPCResult::Type::STR_HEX, "", "The transaction id"},
695
0
                    }},
696
0
                    {RPCResult::Type::NUM, "mempool_sequence", "The mempool sequence value."},
697
0
                }},
698
0
        },
699
0
        RPCExamples{
700
0
            HelpExampleCli("getrawmempool", "true")
701
0
            + HelpExampleRpc("getrawmempool", "true")
702
0
        },
703
0
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
704
0
{
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
0
    };
717
0
}
718
719
static RPCMethod getmempoolancestors()
720
0
{
721
0
    return RPCMethod{
722
0
        "getmempoolancestors",
723
0
        "If txid is in the mempool, returns all in-mempool ancestors.\n",
724
0
        {
725
0
            {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
726
0
            {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"},
727
0
        },
728
0
        {
729
0
            RPCResult{"for verbose = false",
730
0
                RPCResult::Type::ARR, "", "",
731
0
                {{RPCResult::Type::STR_HEX, "", "The transaction id of an in-mempool ancestor transaction"}}},
732
0
            RPCResult{"for verbose = true",
733
0
                RPCResult::Type::OBJ_DYN, "", "",
734
0
                {
735
0
                    {RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()},
736
0
                }},
737
0
        },
738
0
        RPCExamples{
739
0
            HelpExampleCli("getmempoolancestors", "\"mytxid\"")
740
0
            + HelpExampleRpc("getmempoolancestors", "\"mytxid\"")
741
0
        },
742
0
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
743
0
{
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
0
    };
778
0
}
779
780
static RPCMethod getmempooldescendants()
781
0
{
782
0
    return RPCMethod{
783
0
        "getmempooldescendants",
784
0
        "If txid is in the mempool, returns all in-mempool descendants.\n",
785
0
        {
786
0
            {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
787
0
            {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"},
788
0
        },
789
0
        {
790
0
            RPCResult{"for verbose = false",
791
0
                RPCResult::Type::ARR, "", "",
792
0
                {{RPCResult::Type::STR_HEX, "", "The transaction id of an in-mempool descendant transaction"}}},
793
0
            RPCResult{"for verbose = true",
794
0
                RPCResult::Type::OBJ_DYN, "", "",
795
0
                {
796
0
                    {RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()},
797
0
                }},
798
0
        },
799
0
        RPCExamples{
800
0
            HelpExampleCli("getmempooldescendants", "\"mytxid\"")
801
0
            + HelpExampleRpc("getmempooldescendants", "\"mytxid\"")
802
0
        },
803
0
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
804
0
{
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
0
    };
843
0
}
844
845
static RPCMethod getmempoolcluster()
846
0
{
847
0
    return RPCMethod{"getmempoolcluster",
848
0
        "Returns mempool data for given cluster\n",
849
0
        {
850
0
            {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The txid of a transaction in the cluster"},
851
0
        },
852
0
        RPCResult{
853
0
            RPCResult::Type::OBJ, "", "", ClusterDescription()},
854
0
        RPCExamples{
855
0
            HelpExampleCli("getmempoolcluster", "txid")
856
0
            + HelpExampleRpc("getmempoolcluster", "txid")
857
0
        },
858
0
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
859
0
{
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
0
    };
878
0
}
879
880
static RPCMethod getmempoolentry()
881
0
{
882
0
    return RPCMethod{
883
0
        "getmempoolentry",
884
0
        "Returns mempool data for given transaction\n",
885
0
        {
886
0
            {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
887
0
        },
888
0
        RPCResult{
889
0
            RPCResult::Type::OBJ, "", "", MempoolEntryDescription()},
890
0
        RPCExamples{
891
0
            HelpExampleCli("getmempoolentry", "\"mytxid\"")
892
0
            + HelpExampleRpc("getmempoolentry", "\"mytxid\"")
893
0
        },
894
0
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
895
0
{
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
0
    };
911
0
}
912
913
static RPCMethod gettxspendingprevout()
914
0
{
915
0
    return RPCMethod{"gettxspendingprevout",
916
0
        "Scans the mempool (and the txospenderindex, if available) to find transactions spending any of the given outputs",
917
0
        {
918
0
            {"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The transaction outputs that we want to check, and within each, the txid (string) vout (numeric).",
919
0
                {
920
0
                    {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
921
0
                        {
922
0
                            {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
923
0
                            {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
924
0
                        },
925
0
                    },
926
0
                },
927
0
            },
928
0
            {"options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "",
929
0
                {
930
0
                    {"mempool_only", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true if txospenderindex unavailable, otherwise false"}, "If false and mempool lacks a relevant spend, use txospenderindex (throws an exception if not available)."},
931
0
                    {"return_spending_tx", RPCArg::Type::BOOL, RPCArg::DefaultHint{"false"}, "If true, return the full spending tx."},
932
0
                },
933
0
            },
934
0
        },
935
0
        RPCResult{
936
0
            RPCResult::Type::ARR, "", "",
937
0
            {
938
0
                {RPCResult::Type::OBJ, "", "",
939
0
                {
940
0
                    {RPCResult::Type::STR_HEX, "txid", "the transaction id of the checked output"},
941
0
                    {RPCResult::Type::NUM, "vout", "the vout value of the checked output"},
942
0
                    {RPCResult::Type::STR_HEX, "spendingtxid", /*optional=*/true, "the transaction id of the mempool transaction spending this output (omitted if unspent)"},
943
0
                    {RPCResult::Type::STR_HEX, "spendingtx", /*optional=*/true, "the transaction spending this output (only if return_spending_tx is set, omitted if unspent)"},
944
0
                    {RPCResult::Type::STR_HEX, "blockhash", /*optional=*/true, "the hash of the spending block (omitted if unspent or the spending tx is not confirmed)"},
945
0
                }},
946
0
            }
947
0
        },
948
0
        RPCExamples{
949
0
            HelpExampleCli("gettxspendingprevout", "\"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":3}]\"")
950
0
            + HelpExampleRpc("gettxspendingprevout", "\"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":3}]\"")
951
0
            + HelpExampleCliNamed("gettxspendingprevout", {{"outputs", "[{\"txid\":\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\",\"vout\":3}]"}, {"return_spending_tx", true}})
952
0
        },
953
0
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
954
0
        {
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
0
    };
1057
0
}
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
0
{
1087
0
    return RPCMethod{"getmempoolinfo",
1088
0
        "Returns details on the active state of the TX memory pool.",
1089
0
        {},
1090
0
        RPCResult{
1091
0
            RPCResult::Type::OBJ, "", "",
1092
0
            [](){
1093
0
                std::vector<RPCResult> list = {
1094
0
                    {RPCResult::Type::BOOL, "loaded", "True if the initial load attempt of the persisted mempool finished"},
1095
0
                    {RPCResult::Type::NUM, "size", "Current tx count"},
1096
0
                    {RPCResult::Type::NUM, "bytes", "Sum of all virtual transaction sizes as defined in BIP 141. Differs from actual serialized size because witness data is discounted"},
1097
0
                    {RPCResult::Type::NUM, "usage", "Total memory usage for the mempool"},
1098
0
                    {RPCResult::Type::STR_AMOUNT, "total_fee", "Total fees for the mempool in " + CURRENCY_UNIT + ", ignoring modified fees through prioritisetransaction"},
1099
0
                    {RPCResult::Type::NUM, "maxmempool", "Maximum memory usage for the mempool"},
1100
0
                    {RPCResult::Type::STR_AMOUNT, "mempoolminfee", "Minimum fee rate in " + CURRENCY_UNIT + "/kvB for tx to be accepted. Is the maximum of minrelaytxfee and minimum mempool fee"},
1101
0
                    {RPCResult::Type::STR_AMOUNT, "minrelaytxfee", "Current minimum relay fee for transactions"},
1102
0
                    {RPCResult::Type::NUM, "incrementalrelayfee", "minimum fee rate increment for mempool limiting or replacement in " + CURRENCY_UNIT + "/kvB"},
1103
0
                    {RPCResult::Type::NUM, "unbroadcastcount", "Current number of transactions that haven't passed initial broadcast yet"},
1104
0
                    {RPCResult::Type::BOOL, "permitbaremultisig", "True if the mempool accepts transactions with bare multisig outputs"},
1105
0
                    {RPCResult::Type::NUM, "maxdatacarriersize", "Maximum number of bytes that can be used by OP_RETURN outputs in the mempool"},
1106
0
                    {RPCResult::Type::NUM, "limitclustercount", "Maximum number of transactions that can be in a cluster (configured by -limitclustercount)"},
1107
0
                    {RPCResult::Type::NUM, "limitclustersize", "Maximum size of a cluster in virtual bytes (configured by -limitclustersize)"},
1108
0
                    {RPCResult::Type::BOOL, "optimal", "If the mempool is in a known-optimal transaction ordering"},
1109
0
                };
1110
0
                if (IsDeprecatedRPCEnabled("fullrbf")) {
  Branch (1110:21): [True: 0, False: 0]
1111
0
                    list.emplace_back(RPCResult::Type::BOOL, "fullrbf", "True if the mempool accepts RBF without replaceability signaling inspection (DEPRECATED)");
1112
0
                }
1113
0
                return list;
1114
0
            }()
1115
0
            },
1116
0
        RPCExamples{
1117
0
            HelpExampleCli("getmempoolinfo", "")
1118
0
            + HelpExampleRpc("getmempoolinfo", "")
1119
0
        },
1120
0
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
1121
0
{
1122
0
    return MempoolInfoToJSON(EnsureAnyMemPool(request.context));
1123
0
},
1124
0
    };
1125
0
}
1126
1127
static RPCMethod importmempool()
1128
0
{
1129
0
    return RPCMethod{
1130
0
        "importmempool",
1131
0
        "Import a mempool.dat file and attempt to add its contents to the mempool.\n"
1132
0
        "Warning: Importing untrusted files is dangerous, especially if metadata from the file is taken over.",
1133
0
        {
1134
0
            {"filepath", RPCArg::Type::STR, RPCArg::Optional::NO, "The mempool file"},
1135
0
            {"options",
1136
0
             RPCArg::Type::OBJ_NAMED_PARAMS,
1137
0
             RPCArg::Optional::OMITTED,
1138
0
             "",
1139
0
             {
1140
0
                 {"use_current_time", RPCArg::Type::BOOL, RPCArg::Default{true},
1141
0
                  "Whether to use the current system time or use the entry time metadata from the mempool file.\n"
1142
0
                  "Warning: Importing untrusted metadata may lead to unexpected issues and undesirable behavior."},
1143
0
                 {"apply_fee_delta_priority", RPCArg::Type::BOOL, RPCArg::Default{false},
1144
0
                  "Whether to apply the fee delta metadata from the mempool file.\n"
1145
0
                  "It will be added to any existing fee deltas.\n"
1146
0
                  "The fee delta can be set by the prioritisetransaction RPC.\n"
1147
0
                  "Warning: Importing untrusted metadata may lead to unexpected issues and undesirable behavior.\n"
1148
0
                  "Only set this bool if you understand what it does."},
1149
0
                 {"apply_unbroadcast_set", RPCArg::Type::BOOL, RPCArg::Default{false},
1150
0
                  "Whether to apply the unbroadcast set metadata from the mempool file.\n"
1151
0
                  "Warning: Importing untrusted metadata may lead to unexpected issues and undesirable behavior."},
1152
0
             },
1153
0
             RPCArgOptions{.oneline_description = "options"}},
1154
0
        },
1155
0
        RPCResult{RPCResult::Type::OBJ, "", "", std::vector<RPCResult>{}},
1156
0
        RPCExamples{HelpExampleCli("importmempool", "/path/to/mempool.dat") + HelpExampleRpc("importmempool", "/path/to/mempool.dat")},
1157
0
        [](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
0
    };
1186
0
}
1187
1188
static RPCMethod savemempool()
1189
0
{
1190
0
    return RPCMethod{
1191
0
        "savemempool",
1192
0
        "Dumps the mempool to disk. It will fail until the previous dump is fully loaded.\n",
1193
0
        {},
1194
0
        RPCResult{
1195
0
            RPCResult::Type::OBJ, "", "",
1196
0
            {
1197
0
                {RPCResult::Type::STR, "filename", "the directory and file where the mempool was saved"},
1198
0
            }},
1199
0
        RPCExamples{
1200
0
            HelpExampleCli("savemempool", "")
1201
0
            + HelpExampleRpc("savemempool", "")
1202
0
        },
1203
0
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
1204
0
{
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
0
    };
1224
0
}
1225
1226
static std::vector<RPCResult> OrphanDescription()
1227
0
{
1228
0
    return {
1229
0
        RPCResult{RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
1230
0
        RPCResult{RPCResult::Type::STR_HEX, "wtxid", "The transaction witness hash in hex"},
1231
0
        RPCResult{RPCResult::Type::NUM, "bytes", "The serialized transaction size in bytes"},
1232
0
        RPCResult{RPCResult::Type::NUM, "vsize", "The virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted."},
1233
0
        RPCResult{RPCResult::Type::NUM, "weight", "The transaction weight as defined in BIP 141."},
1234
0
        RPCResult{RPCResult::Type::ARR, "from", "",
1235
0
        {
1236
0
            RPCResult{RPCResult::Type::NUM, "peer_id", "Peer ID"},
1237
0
        }},
1238
0
    };
1239
0
}
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
0
{
1259
0
    return RPCMethod{
1260
0
        "getorphantxs",
1261
0
        "Shows transactions in the tx orphanage.\n"
1262
0
        "\nEXPERIMENTAL warning: this call may be changed in future releases.\n",
1263
0
        {
1264
0
            {"verbosity", RPCArg::Type::NUM, RPCArg::Default{0}, "0 for an array of txids (may contain duplicates), 1 for an array of objects with tx details, and 2 for details from (1) and tx hex",
1265
0
             RPCArgOptions{.skip_type_check = true}},
1266
0
        },
1267
0
        {
1268
0
            RPCResult{"for verbose = 0",
1269
0
                RPCResult::Type::ARR, "", "",
1270
0
                {
1271
0
                    {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
1272
0
                }},
1273
0
            RPCResult{"for verbose = 1",
1274
0
                RPCResult::Type::ARR, "", "",
1275
0
                {
1276
0
                    {RPCResult::Type::OBJ, "", "", OrphanDescription()},
1277
0
                }},
1278
0
            RPCResult{"for verbose = 2",
1279
0
                RPCResult::Type::ARR, "", "",
1280
0
                {
1281
0
                    {RPCResult::Type::OBJ, "", "",
1282
0
                        Cat<std::vector<RPCResult>>(
1283
0
                            OrphanDescription(),
1284
0
                            {{RPCResult::Type::STR_HEX, "hex", "The serialized, hex-encoded transaction data"}}
1285
0
                        )
1286
0
                    },
1287
0
                }},
1288
0
        },
1289
0
        RPCExamples{
1290
0
            HelpExampleCli("getorphantxs", "2")
1291
0
            + HelpExampleRpc("getorphantxs", "2")
1292
0
        },
1293
0
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
1294
0
        {
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
0
    };
1324
0
}
1325
1326
static RPCMethod submitpackage()
1327
0
{
1328
0
    return RPCMethod{"submitpackage",
1329
0
        "Submit a package of raw transactions (serialized, hex-encoded) to local node.\n"
1330
0
        "The package will be validated according to consensus and mempool policy rules. If any transaction passes, it will be accepted to mempool.\n"
1331
0
        "This RPC is experimental and the interface may be unstable. Refer to doc/policy/packages.md for documentation on package policies.\n"
1332
0
        "Warning: successful submission does not mean the transactions will propagate throughout the network.\n"
1333
0
        ,
1334
0
        {
1335
0
            {"package", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of raw transactions.\n"
1336
0
                "The package must consist of a transaction with (some, all, or none of) its unconfirmed parents. A single transaction is permitted.\n"
1337
0
                "None of the parents may depend on each other. Parents that are already in mempool do not need to be present in the package.\n"
1338
0
                "The package must be topologically sorted, with the child being the last element in the array if there are multiple elements.",
1339
0
                {
1340
0
                    {"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
1341
0
                },
1342
0
            },
1343
0
            {"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},
1344
0
             "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
1345
0
                 "/kvB.\nFee rates larger than 1BTC/kvB are rejected.\nSet to 0 to accept any fee rate."},
1346
0
            {"maxburnamount", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_BURN_AMOUNT)},
1347
0
             "Reject transactions with provably unspendable outputs (e.g. 'datacarrier' outputs that use the OP_RETURN opcode) greater than the specified value, expressed in " + CURRENCY_UNIT + ".\n"
1348
0
             "If burning funds through unspendable outputs is desired, increase this value.\n"
1349
0
             "This check is based on heuristics and does not guarantee spendability of outputs.\n"
1350
0
            },
1351
0
        },
1352
0
        RPCResult{
1353
0
            RPCResult::Type::OBJ, "", "",
1354
0
            {
1355
0
                {RPCResult::Type::STR, "package_msg", "The transaction package result message. \"success\" indicates all transactions were accepted into or are already in the mempool."},
1356
0
                {RPCResult::Type::OBJ_DYN, "tx-results", "The transaction results keyed by wtxid. An entry is returned for every submitted wtxid.",
1357
0
                {
1358
0
                    {RPCResult::Type::OBJ, "wtxid", "transaction wtxid", {
1359
0
                        {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
1360
0
                        {RPCResult::Type::STR_HEX, "other-wtxid", /*optional=*/true, "The wtxid of a different transaction with the same txid but different witness found in the mempool. This means the submitted transaction was ignored."},
1361
0
                        {RPCResult::Type::NUM, "vsize", /*optional=*/true, "Sigops-adjusted virtual transaction size."},
1362
0
                        {RPCResult::Type::OBJ, "fees", /*optional=*/true, "Transaction fees", {
1363
0
                            {RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT},
1364
0
                            {RPCResult::Type::STR_AMOUNT, "effective-feerate", /*optional=*/true, "if the transaction was not already in the mempool, the effective feerate in " + CURRENCY_UNIT + " per KvB. For example, the package feerate and/or feerate with modified fees from prioritisetransaction."},
1365
0
                            {RPCResult::Type::ARR, "effective-includes", /*optional=*/true, "if effective-feerate is provided, the wtxids of the transactions whose fees and vsizes are included in effective-feerate.",
1366
0
                                {{RPCResult::Type::STR_HEX, "", "transaction wtxid in hex"},
1367
0
                            }},
1368
0
                        }},
1369
0
                        {RPCResult::Type::STR, "error", /*optional=*/true, "Error string if rejected from mempool, or \"package-not-validated\" when the package aborts before any per-tx processing."},
1370
0
                    }}
1371
0
                }},
1372
0
                {RPCResult::Type::ARR, "replaced-transactions", /*optional=*/true, "List of txids of replaced transactions",
1373
0
                {
1374
0
                    {RPCResult::Type::STR_HEX, "", "The transaction id"},
1375
0
                }},
1376
0
            },
1377
0
        },
1378
0
        RPCExamples{
1379
0
            HelpExampleRpc("submitpackage", R"(["raw-parent-tx-1", "raw-parent-tx-2", "raw-child-tx"])") +
1380
0
            HelpExampleCli("submitpackage", R"('["raw-tx-without-unconfirmed-parents"]')")
1381
0
        },
1382
0
        [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
1383
0
        {
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
0
    };
1538
0
}
1539
1540
void RegisterMempoolRPCCommands(CRPCTable& t)
1541
0
{
1542
0
    static const CRPCCommand commands[]{
1543
0
        {"rawtransactions", &sendrawtransaction},
1544
0
        {"rawtransactions", &getprivatebroadcastinfo},
1545
0
        {"rawtransactions", &abortprivatebroadcast},
1546
0
        {"rawtransactions", &testmempoolaccept},
1547
0
        {"blockchain", &getmempoolancestors},
1548
0
        {"blockchain", &getmempooldescendants},
1549
0
        {"blockchain", &getmempoolentry},
1550
0
        {"blockchain", &getmempoolcluster},
1551
0
        {"blockchain", &gettxspendingprevout},
1552
0
        {"blockchain", &getmempoolinfo},
1553
0
        {"hidden", &getmempoolfeeratediagram},
1554
0
        {"blockchain", &getrawmempool},
1555
0
        {"blockchain", &importmempool},
1556
0
        {"blockchain", &savemempool},
1557
0
        {"hidden", &getorphantxs},
1558
0
        {"rawtransactions", &submitpackage},
1559
0
    };
1560
0
    for (const auto& c : commands) {
  Branch (1560:24): [True: 0, False: 0]
1561
0
        t.appendCommand(c.name, &c);
1562
0
    }
1563
0
}