Coverage Report

Created: 2025-06-10 13:21

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