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 2 :
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 2 : "\nTest acceptance of the transaction (signed hex)\n"
152 2 : + 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(__func__, *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 (-regtest only).\n"
823 : "The package will be validated according to consensus and mempool policy rules. If all transactions pass, they will be accepted to mempool.\n"
824 : "This RPC is experimental and the interface may be unstable. Refer to doc/policy/packages.md for documentation on package policies.\n"
825 : "Warning: until package relay is in use, successful submission does not mean the transaction will propagate to other nodes on the network.\n"
826 : "Currently, each transaction is broadcasted individually after submission, which means they must meet other nodes' feerate requirements alone.\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 : if (!Params().IsMockableChain()) {
866 0 : throw std::runtime_error("submitpackage is for regression testing (-regtest mode) only");
867 : }
868 0 : const UniValue raw_transactions = request.params[0].get_array();
869 0 : if (raw_transactions.size() < 1 || raw_transactions.size() > MAX_PACKAGE_COUNT) {
870 0 : throw JSONRPCError(RPC_INVALID_PARAMETER,
871 0 : "Array must contain between 1 and " + ToString(MAX_PACKAGE_COUNT) + " transactions.");
872 : }
873 :
874 0 : std::vector<CTransactionRef> txns;
875 0 : txns.reserve(raw_transactions.size());
876 0 : for (const auto& rawtx : raw_transactions.getValues()) {
877 0 : CMutableTransaction mtx;
878 0 : if (!DecodeHexTx(mtx, rawtx.get_str())) {
879 0 : throw JSONRPCError(RPC_DESERIALIZATION_ERROR,
880 0 : "TX decode failed: " + rawtx.get_str() + " Make sure the tx has at least one input.");
881 : }
882 0 : txns.emplace_back(MakeTransactionRef(std::move(mtx)));
883 0 : }
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 : {"hidden", &submitpackage},
987 : };
988 12 : for (const auto& c : commands) {
989 11 : t.appendCommand(c.name, &c);
990 : }
991 1 : }
|