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