Coverage Report

Created: 2025-04-09 20:14

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