mempool.cpp raw
1 // Copyright (c) 2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2022 The Limenka developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6 #include <rpc/blockchain.h>
7
8 #include <node/mempool_persist.h>
9
10 #include <chainparams.h>
11 #include <consensus/validation.h>
12 #include <core_io.h>
13 #include <core_memusage.h>
14 #include <kernel/mempool_entry.h>
15 #include <net_processing.h>
16 #include <node/context.h>
17 #include <node/mempool_persist_args.h>
18 #include <node/types.h>
19 #include <policy/rbf.h>
20 #include <policy/settings.h>
21 #include <primitives/transaction.h>
22 #include <rpc/mempool.h>
23 #include <rpc/rawtransaction.h>
24 #include <rpc/mempool.h>
25 #include <rpc/server.h>
26 #include <rpc/server_util.h>
27 #include <rpc/util.h>
28 #include <txmempool.h>
29 #include <univalue.h>
30 #include <util/any.h>
31 #include <util/fs.h>
32 #include <util/moneystr.h>
33 #include <util/strencodings.h>
34 #include <util/time.h>
35 #include <util/vector.h>
36 #include <validation.h>
37
38 #include <optional>
39 #include <utility>
40
41 using node::DumpMempool;
42
43 using node::DEFAULT_MAX_BURN_AMOUNT;
44 using node::DEFAULT_MAX_RAW_TX_FEE_RATE;
45 using node::MempoolPath;
46 using node::NodeContext;
47 using node::TransactionError;
48 using util::ToString;
49
50 static RPCHelpMan sendrawtransaction()
51 {
52 return RPCHelpMan{"sendrawtransaction",
53 "\nSubmit a raw transaction (serialized, hex-encoded) to local node and network.\n"
54 "\nThe transaction will be sent unconditionally to all peers, so using sendrawtransaction\n"
55 "for manual rebroadcast may degrade privacy by leaking the transaction's origin, as\n"
56 "nodes will normally not rebroadcast non-wallet transactions already in their mempool.\n"
57 "\nA specific exception, RPC_TRANSACTION_ALREADY_IN_UTXO_SET, may throw if the transaction cannot be added to the mempool.\n"
58 "\nRelated RPCs: createrawtransaction, signrawtransactionwithkey\n",
59 {
60 {"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
61 {"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},
62 "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
63 "/kvB.\nFee rates larger than 1BTC/kvB are rejected.\nSet to 0 to accept any fee rate.",
64 RPCArgOptions{.skip_type_check = true} // for ignore_rejects compatibility
65 },
66 {"maxburnamount", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_BURN_AMOUNT)},
67 "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"
68 "If burning funds through unspendable outputs is desired, increase this value.\n"
69 "This check is based on heuristics and does not guarantee spendability of outputs.\n",
70 RPCArgOptions{.skip_type_check = true} // for ignore_rejects compatibility
71 },
72 {"ignore_rejects", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Rejection conditions to ignore, eg 'txn-mempool-conflict'",
73 {
74 {"reject_reason", RPCArg::Type::STR, RPCArg::Optional::OMITTED, ""},
75 },
76 },
77 },
78 RPCResult{
79 RPCResult::Type::STR_HEX, "", "The transaction hash in hex"
80 },
81 RPCExamples{
82 "\nCreate a transaction\n"
83 + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") +
84 "Sign the transaction, and get back the hex\n"
85 + HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") +
86 "\nSend the transaction (signed hex)\n"
87 + HelpExampleCli("sendrawtransaction", "\"signedhex\"") +
88 "\nAs a JSON-RPC call\n"
89 + HelpExampleRpc("sendrawtransaction", "\"signedhex\"")
90 },
91 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
92 {
93 CFeeRate max_raw_tx_fee_rate{DEFAULT_MAX_RAW_TX_FEE_RATE};
94 CAmount max_burn_amount{0};
95 const UniValue* json_ign_rejs = &request.params[3];
96
97 if (request.params[1].isArray() && request.params[2].isNull() && request.params[3].isNull()) {
98 // ignore_rejects used to occupy this position (v0.12.0.knots20160226.rc1-v0.17.1.knots20181229)
99 json_ign_rejs = &request.params[1];
100 } else {
101 if (!request.params[1].isNull()) {
102 max_raw_tx_fee_rate = ParseFeeRate(self.Arg<UniValue>("maxfeerate"));
103 }
104 if (request.params[2].isArray() && request.params[3].isNull()) {
105 // ignore_rejects used to occupy this position (v0.18.0.knots20190502-v23.0.knots20220529)
106 json_ign_rejs = &request.params[2];
107 } else if (!request.params[2].isNull()) {
108 max_burn_amount = AmountFromValue(request.params[2]);
109 }
110 }
111
112 CMutableTransaction mtx;
113 if (!DecodeHexTx(mtx, request.params[0].get_str())) {
114 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed. Make sure the tx has at least one input.");
115 }
116
117 for (const auto& out : mtx.vout) {
118 if((out.scriptPubKey.IsUnspendable() || !out.scriptPubKey.HasValidOps()) && out.nValue > max_burn_amount) {
119 throw JSONRPCTransactionError(TransactionError::MAX_BURN_EXCEEDED);
120 }
121 }
122
123 CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
124
125 ignore_rejects_type ignore_rejects;
126 if (!json_ign_rejs->isNull()) {
127 for (size_t i = 0; i < json_ign_rejs->size(); ++i) {
128 const UniValue& json_ign_rej = (*json_ign_rejs)[i];
129 ignore_rejects.insert(json_ign_rej.get_str());
130 }
131 }
132
133 std::string err_string;
134 AssertLockNotHeld(cs_main);
135 NodeContext& node = EnsureAnyNodeContext(request.context);
136 const TransactionError err = BroadcastTransaction(node, tx, err_string, max_raw_tx_fee_rate, /*relay=*/true, /*wait_callback=*/true, ignore_rejects);
137 if (TransactionError::OK != err) {
138 throw JSONRPCTransactionError(err, err_string);
139 }
140
141 return tx->GetHash().GetHex();
142 },
143 };
144 }
145
146 static RPCHelpMan testmempoolaccept()
147 {
148 return RPCHelpMan{"testmempoolaccept",
149 "\nReturns result of mempool acceptance tests indicating if raw transaction(s) (serialized, hex-encoded) would be accepted by mempool.\n"
150 "\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"
151 "\nIf one transaction fails, other transactions may not be fully validated (the 'allowed' key will be blank).\n"
152 "\nThe maximum number of transactions allowed is " + ToString(MAX_PACKAGE_COUNT) + ".\n"
153 "\nThis checks if transactions violate the consensus or policy rules.\n"
154 "\nSee sendrawtransaction call.\n",
155 {
156 {"rawtxs", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of hex strings of raw transactions.",
157 {
158 {"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
159 },
160 },
161 {"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},
162 "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
163 "/kvB.\nFee rates larger than 1BTC/kvB are rejected.\nSet to 0 to accept any fee rate."},
164 {"ignore_rejects", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Rejection conditions to ignore, eg 'txn-mempool-conflict'",
165 {
166 {"reject_reason", RPCArg::Type::STR, RPCArg::Optional::OMITTED, ""},
167 },
168 },
169 },
170 RPCResult{
171 RPCResult::Type::ARR, "", "The result of the mempool acceptance test for each raw transaction in the input array.\n"
172 "Returns results for each transaction in the same order they were passed in.\n"
173 "Transactions that cannot be fully validated due to failures in other transactions will not contain an 'allowed' result.\n",
174 {
175 {RPCResult::Type::OBJ, "", "",
176 {
177 {RPCResult::Type::STR_HEX, "txid", "The transaction id"},
178 {RPCResult::Type::STR_HEX, "wtxid", "The transaction witness hash in hex"},
179 {RPCResult::Type::STR, "package-error", /*optional=*/true, "Package validation error, if any (only possible if rawtxs had more than 1 transaction)."},
180 {RPCResult::Type::BOOL, "allowed", /*optional=*/true, "Whether this tx would be accepted to the mempool and pass client-specified maxfeerate. "
181 "If not present, the tx was not fully validated due to a failure in another tx in the list."},
182 {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)"},
183 {RPCResult::Type::NUM, "usage", "Memory usage of transaction for this node"},
184 {RPCResult::Type::OBJ, "fees", /*optional=*/true, "Transaction fees (only present if 'allowed' is true)",
185 {
186 {RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT},
187 {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."},
188 {RPCResult::Type::ARR, "effective-includes", /*optional=*/false, "transactions whose fees and vsizes are included in effective-feerate.",
189 {RPCResult{RPCResult::Type::STR_HEX, "", "transaction wtxid in hex"},
190 }},
191 }},
192 {RPCResult::Type::STR, "reject-reason", /*optional=*/true, "Rejection reason (only present when 'allowed' is false)"},
193 {RPCResult::Type::STR, "reject-details", /*optional=*/true, "Rejection details (only present when 'allowed' is false and rejection details exist)"},
194 }},
195 }
196 },
197 RPCExamples{
198 "\nCreate a transaction\n"
199 + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") +
200 "Sign the transaction, and get back the hex\n"
201 + HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") +
202 "\nTest acceptance of the transaction (signed hex)\n"
203 + HelpExampleCli("testmempoolaccept", R"('["signedhex"]')") +
204 "\nAs a JSON-RPC call\n"
205 + HelpExampleRpc("testmempoolaccept", "[\"signedhex\"]")
206 },
207 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
208 {
209 const UniValue raw_transactions = request.params[0].get_array();
210 if (raw_transactions.size() < 1 || raw_transactions.size() > MAX_PACKAGE_COUNT) {
211 throw JSONRPCError(RPC_INVALID_PARAMETER,
212 "Array must contain between 1 and " + ToString(MAX_PACKAGE_COUNT) + " transactions.");
213 }
214
215 const CFeeRate max_raw_tx_fee_rate{ParseFeeRate(self.Arg<UniValue>("maxfeerate"))};
216
217 const UniValue* json_ign_rejs = &request.params[2];
218 ignore_rejects_type ignore_rejects;
219 if (!json_ign_rejs->isNull()) {
220 for (size_t i = 0; i < json_ign_rejs->size(); ++i) {
221 const UniValue& json_ign_rej = (*json_ign_rejs)[i];
222 const std::string& ign_rej = json_ign_rej.get_str();
223 ignore_rejects.insert(ign_rej);
224 }
225 }
226
227 std::vector<CTransactionRef> txns;
228 txns.reserve(raw_transactions.size());
229 for (const auto& rawtx : raw_transactions.getValues()) {
230 CMutableTransaction mtx;
231 if (!DecodeHexTx(mtx, rawtx.get_str())) {
232 throw JSONRPCError(RPC_DESERIALIZATION_ERROR,
233 "TX decode failed: " + rawtx.get_str() + " Make sure the tx has at least one input.");
234 }
235 txns.emplace_back(MakeTransactionRef(std::move(mtx)));
236 }
237
238 NodeContext& node = EnsureAnyNodeContext(request.context);
239 CTxMemPool& mempool = EnsureMemPool(node);
240 ChainstateManager& chainman = EnsureChainman(node);
241 Chainstate& chainstate = chainman.ActiveChainstate();
242 const PackageMempoolAcceptResult package_result = [&] {
243 LOCK(::cs_main);
244 if (txns.size() > 1) return ProcessNewPackage(chainstate, mempool, txns, /*test_accept=*/true, /*client_maxfeerate=*/{}, ignore_rejects);
245 return PackageMempoolAcceptResult(txns[0]->GetWitnessHash(),
246 chainman.ProcessTransaction(txns[0], /*test_accept=*/true, ignore_rejects));
247 }();
248
249 UniValue rpc_result(UniValue::VARR);
250 // We will check transaction fees while we iterate through txns in order. If any transaction fee
251 // exceeds maxfeerate, we will leave the rest of the validation results blank, because it
252 // doesn't make sense to return a validation result for a transaction if its ancestor(s) would
253 // not be submitted.
254 bool exit_early{false};
255 for (const auto& tx : txns) {
256 UniValue result_inner(UniValue::VOBJ);
257 result_inner.pushKV("txid", tx->GetHash().GetHex());
258 result_inner.pushKV("wtxid", tx->GetWitnessHash().GetHex());
259 result_inner.pushKV("usage", RecursiveDynamicUsage(tx));
260 if (package_result.m_state.GetResult() == PackageValidationResult::PCKG_POLICY) {
261 result_inner.pushKV("package-error", package_result.m_state.ToString());
262 }
263 auto it = package_result.m_tx_results.find(tx->GetWitnessHash());
264 if (exit_early || it == package_result.m_tx_results.end()) {
265 // Validation unfinished. Just return the txid and wtxid.
266 rpc_result.push_back(std::move(result_inner));
267 continue;
268 }
269 const auto& tx_result = it->second;
270 // Package testmempoolaccept doesn't allow transactions to already be in the mempool.
271 CHECK_NONFATAL(tx_result.m_result_type != MempoolAcceptResult::ResultType::MEMPOOL_ENTRY);
272 if (tx_result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
273 const CAmount fee = tx_result.m_base_fees.value();
274 // Check that fee does not exceed maximum fee
275 const int64_t virtual_size = tx_result.m_vsize.value();
276 const CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size);
277 if (max_raw_tx_fee && fee > max_raw_tx_fee &&
278 0 == (ignore_rejects.count("absurdly-high-fee") + ignore_rejects.count("max-fee-exceeded"))) {
279 result_inner.pushKV("allowed", false);
280 result_inner.pushKV("reject-reason", "max-fee-exceeded");
281 exit_early = true;
282 } else {
283 // Only return the fee and vsize if the transaction would pass ATMP.
284 // These can be used to calculate the feerate.
285 result_inner.pushKV("allowed", true);
286 result_inner.pushKV("vsize", virtual_size);
287 UniValue fees(UniValue::VOBJ);
288 fees.pushKV("base", ValueFromAmount(fee));
289 fees.pushKV("effective-feerate", ValueFromAmount(tx_result.m_effective_feerate.value().GetFeePerK()));
290 UniValue effective_includes_res(UniValue::VARR);
291 for (const auto& wtxid : tx_result.m_wtxids_fee_calculations.value()) {
292 effective_includes_res.push_back(wtxid.ToString());
293 }
294 fees.pushKV("effective-includes", std::move(effective_includes_res));
295 result_inner.pushKV("fees", std::move(fees));
296 }
297 } else {
298 result_inner.pushKV("allowed", false);
299 const TxValidationState state = tx_result.m_state;
300 if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
301 result_inner.pushKV("reject-reason", "missing-inputs");
302 } else {
303 result_inner.pushKV("reject-reason", state.GetRejectReason());
304 result_inner.pushKV("reject-details", state.ToString());
305 }
306 }
307 rpc_result.push_back(std::move(result_inner));
308 }
309 return rpc_result;
310 },
311 };
312 }
313
314 static std::vector<RPCResult> MempoolEntryDescription()
315 {
316 return {
317 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."},
318 RPCResult{RPCResult::Type::NUM, "weight", "transaction weight as defined in BIP 141."},
319 RPCResult{RPCResult::Type::NUM_TIME, "time", "local time transaction entered pool in seconds since 1 Jan 1970 GMT"},
320 RPCResult{RPCResult::Type::NUM, "height", "block height when transaction entered pool"},
321 RPCResult{RPCResult::Type::NUM, "startingpriority", "Priority when transaction entered pool"},
322 RPCResult{RPCResult::Type::NUM, "currentpriority", "Transaction priority now"},
323 RPCResult{RPCResult::Type::NUM, "descendantcount", "number of in-mempool descendant transactions (including this one)"},
324 RPCResult{RPCResult::Type::NUM, "descendantsize", "virtual transaction size of in-mempool descendants (including this one)"},
325 RPCResult{RPCResult::Type::NUM, "ancestorcount", "number of in-mempool ancestor transactions (including this one)"},
326 RPCResult{RPCResult::Type::NUM, "ancestorsize", "virtual transaction size of in-mempool ancestors (including this one)"},
327 RPCResult{RPCResult::Type::STR_HEX, "hash", "hash of entire serialized transaction"},
328 RPCResult{RPCResult::Type::STR_HEX, "wtxid", "hash of serialized transaction, including witness data"},
329 RPCResult{RPCResult::Type::OBJ, "fees", "",
330 {
331 RPCResult{RPCResult::Type::STR_AMOUNT, "base", "transaction fee, denominated in " + CURRENCY_UNIT},
332 RPCResult{RPCResult::Type::STR_AMOUNT, "modified", "transaction fee with fee deltas used for mining priority, denominated in " + CURRENCY_UNIT},
333 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},
334 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},
335 }},
336 RPCResult{RPCResult::Type::ARR, "depends", "unconfirmed transactions used as inputs for this transaction",
337 {RPCResult{RPCResult::Type::STR_HEX, "transactionid", "parent transaction id"}}},
338 RPCResult{RPCResult::Type::ARR, "spentby", "unconfirmed transactions spending outputs from this transaction",
339 {RPCResult{RPCResult::Type::STR_HEX, "transactionid", "child transaction id"}}},
340 RPCResult{RPCResult::Type::BOOL, "bip125-replaceable", "Whether this transaction signals BIP125 replaceability or has an unconfirmed ancestor signaling BIP125 replaceability.\n"},
341 RPCResult{RPCResult::Type::BOOL, "unbroadcast", "Whether this transaction is currently unbroadcast (initial broadcast not yet acknowledged by any peers)"},
342 };
343 }
344
345 static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPoolEntry& e, const int next_block_height) EXCLUSIVE_LOCKS_REQUIRED(pool.cs)
346 {
347 AssertLockHeld(pool.cs);
348
349 info.pushKV("vsize", (int)e.GetTxSize());
350 info.pushKV("weight", (int)e.GetTxWeight());
351 info.pushKV("time", count_seconds(e.GetTime()));
352 info.pushKV("height", (int)e.GetHeight());
353 info.pushKV("startingpriority", e.GetStartingPriority());
354 info.pushKV("currentpriority", e.GetPriority(next_block_height));
355 info.pushKV("descendantcount", e.GetCountWithDescendants());
356 info.pushKV("descendantsize", e.GetSizeWithDescendants());
357 info.pushKV("ancestorcount", e.GetCountWithAncestors());
358 info.pushKV("ancestorsize", e.GetSizeWithAncestors());
359 info.pushKV("wtxid", e.GetTx().GetWitnessHash().ToString());
360 info.pushKV("hash", info["wtxid"]);
361
362 UniValue fees(UniValue::VOBJ);
363 fees.pushKV("base", ValueFromAmount(e.GetFee()));
364 fees.pushKV("modified", ValueFromAmount(e.GetModifiedFee()));
365 fees.pushKV("ancestor", ValueFromAmount(e.GetModFeesWithAncestors()));
366 fees.pushKV("descendant", ValueFromAmount(e.GetModFeesWithDescendants()));
367 info.pushKV("fees", std::move(fees));
368
369 const CTransaction& tx = e.GetTx();
370 std::set<std::string> setDepends;
371 for (const CTxIn& txin : tx.vin)
372 {
373 if (pool.exists(GenTxid::Txid(txin.prevout.hash)))
374 setDepends.insert(txin.prevout.hash.ToString());
375 }
376
377 UniValue depends(UniValue::VARR);
378 for (const std::string& dep : setDepends)
379 {
380 depends.push_back(dep);
381 }
382
383 info.pushKV("depends", std::move(depends));
384
385 UniValue spent(UniValue::VARR);
386 for (const CTxMemPoolEntry& child : e.GetMemPoolChildrenConst()) {
387 spent.push_back(child.GetTx().GetHash().ToString());
388 }
389
390 info.pushKV("spentby", std::move(spent));
391
392 // Add opt-in RBF status
393 bool rbfStatus = false;
394 RBFTransactionState rbfState = IsRBFOptIn(tx, pool);
395 if (rbfState == RBFTransactionState::UNKNOWN) {
396 throw JSONRPCError(RPC_MISC_ERROR, "Transaction is not in mempool");
397 } else if (rbfState == RBFTransactionState::REPLACEABLE_BIP125) {
398 rbfStatus = true;
399 }
400
401 info.pushKV("bip125-replaceable", rbfStatus);
402 info.pushKV("unbroadcast", pool.IsUnbroadcastTx(tx.GetHash()));
403 }
404
405 UniValue MempoolToJSON(ChainstateManager &chainman, const CTxMemPool& pool, bool verbose, bool include_mempool_sequence)
406 {
407 if (verbose) {
408 if (include_mempool_sequence) {
409 throw JSONRPCError(RPC_INVALID_PARAMETER, "Verbose results cannot contain mempool sequence values.");
410 }
411 LOCK(::cs_main);
412 const CChain& active_chain = chainman.ActiveChain();
413 const int next_block_height = active_chain.Height() + 1;
414 LOCK(pool.cs);
415 // TODO: Release cs_main after mempool.cs acquired
416
417 UniValue o(UniValue::VOBJ);
418 for (const CTxMemPoolEntry& e : pool.entryAll()) {
419 UniValue info(UniValue::VOBJ);
420 entryToJSON(pool, info, e, next_block_height);
421 // Mempool has unique entries so there is no advantage in using
422 // UniValue::pushKV, which checks if the key already exists in O(N).
423 // UniValue::pushKVEnd is used instead which currently is O(1).
424 o.pushKVEnd(e.GetTx().GetHash().ToString(), std::move(info));
425 }
426 return o;
427 } else {
428 UniValue a(UniValue::VARR);
429 uint64_t mempool_sequence;
430 {
431 LOCK(pool.cs);
432 for (const CTxMemPoolEntry& e : pool.entryAll()) {
433 a.push_back(e.GetTx().GetHash().ToString());
434 }
435 mempool_sequence = pool.GetSequence();
436 }
437 if (!include_mempool_sequence) {
438 return a;
439 } else {
440 UniValue o(UniValue::VOBJ);
441 o.pushKV("txids", std::move(a));
442 o.pushKV("mempool_sequence", mempool_sequence);
443 return o;
444 }
445 }
446 }
447
448 static RPCHelpMan maxmempool()
449 {
450 return RPCHelpMan{"maxmempool",
451 "\nSets the allocated memory for the memory pool.\n",
452 {
453 {"megabytes", RPCArg::Type::NUM, RPCArg::Optional::NO, "The memory allocated in MB"},
454 },
455 RPCResult{
456 RPCResult::Type::NONE, "", ""},
457 RPCExamples{
458 HelpExampleCli("maxmempool", "150") + HelpExampleRpc("maxmempool", "150")
459 },
460 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
461 {
462 int64_t nSize = request.params[0].getInt<int32_t>();
463 int64_t nMempoolSizeMax = nSize * 1000000;
464
465 CTxMemPool& mempool = EnsureAnyMemPool(request.context);
466 LOCK2(cs_main, mempool.cs);
467
468 int64_t nMempoolSizeMin = maxmempoolMinimumBytes(mempool.m_opts.limits.descendant_size_vbytes);
469 if (nMempoolSizeMax < 0 || nMempoolSizeMax < nMempoolSizeMin)
470 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("MaxMempool size %d is too small", nSize));
471 mempool.m_opts.max_size_bytes = nMempoolSizeMax;
472
473 auto node_context = util::AnyPtr<NodeContext>(request.context);
474 if (node_context && node_context->chainman) {
475 Chainstate& active_chainstate = node_context->chainman->ActiveChainstate();
476 LimitMempoolSize(mempool, active_chainstate.CoinsTip());
477 }
478
479 return NullUniValue;
480 }
481 };
482 }
483
484 static RPCHelpMan listmempooltransactions()
485 {
486 return RPCHelpMan{"listmempooltransactions",
487 "\nReturns all transactions in the mempool. Can be filtered by mempool_sequence\n"
488 "\nAllows for syncing with current mempool entries via polling (not zmq).",
489 {
490 {"start_sequence", RPCArg::Type::NUM, RPCArg::Default{0}, "The mempool_sequence to start the results to. Defaults to 0 (zero, all transactions)."},
491 {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"},
492 },
493 {
494 RPCResult{"for verbose = false",
495 RPCResult::Type::OBJ, "", "",
496 {
497 {RPCResult::Type::NUM, "mempool_sequence", "The current max mempool sequence value."},
498 {RPCResult::Type::ARR, "txs", "",
499 {
500 {RPCResult::Type::OBJ, "", "",
501 {
502 {RPCResult::Type::NUM, "entry_sequence", "The mempool sequence value for this transaction entry."},
503 {RPCResult::Type::STR_HEX, "txid", "The transaction id"},
504 }},
505 }},
506 }},
507 RPCResult{"for verbose = true",
508 RPCResult::Type::OBJ, "", "",
509 {
510 {RPCResult::Type::NUM, "mempool_sequence", "The current max mempool sequence value."},
511 {RPCResult::Type::ARR, "txs", "",
512 {
513 {RPCResult::Type::OBJ, "", "",
514 {
515 Cat<std::vector<RPCResult>>(
516 {
517 {RPCResult::Type::NUM, "entry_sequence", "The mempool sequence value for this transaction entry."},
518 },
519 DecodeTxDoc(/*txid_field_doc=*/"The transaction id of the mempool transaction")),
520 }},
521 }},
522 }},
523 },
524 RPCExamples{
525 HelpExampleCli("listmempooltransactions", "true")
526 + HelpExampleRpc("listmempooltransactions", "true")
527 },
528 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
529 {
530 uint64_t start_mempool_sequence = 0;
531 if (!request.params[0].isNull()) {
532 start_mempool_sequence = request.params[0].getInt<uint64_t>();
533 }
534
535 bool fVerbose = false;
536 if (!request.params[1].isNull())
537 fVerbose = request.params[1].get_bool();
538
539 return MempoolTxsToJSON(EnsureAnyMemPool(request.context), fVerbose, start_mempool_sequence);
540 },
541 };
542 }
543
544 UniValue MempoolTxsToJSON(const CTxMemPool& pool, bool verbose, uint64_t sequence_start)
545 {
546 uint64_t mempool_sequence;
547
548 LOCK(pool.cs);
549 mempool_sequence = pool.GetSequence();
550
551 UniValue o(UniValue::VOBJ);
552 o.pushKV("mempool_sequence", mempool_sequence);
553
554 UniValue a(UniValue::VARR);
555 for (const CTxMemPoolEntry& e : pool.mapTx) {
556 UniValue txentry(UniValue::VOBJ);
557
558 // We skip anything not requested.
559 if (e.GetSequence() < sequence_start)
560 continue;
561
562 txentry.pushKV("entry_sequence", e.GetSequence());
563
564 if (verbose) {
565 // We could also calculate fees etc for this transaction, but yolo.
566 TxToUniv(e.GetTx(), /*block_hash=*/uint256::ZERO, /*entry=*/txentry, /*include_hex=*/false);
567 } else {
568 txentry.pushKV("txid", e.GetTx().GetHash().ToString());
569 }
570
571 a.push_back(txentry);
572 }
573
574 o.pushKV("txs", a);
575 return o;
576 }
577
578 static RPCHelpMan getrawmempool()
579 {
580 return RPCHelpMan{"getrawmempool",
581 "\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n"
582 "\nHint: use getmempoolentry to fetch a specific transaction from the mempool.\n",
583 {
584 {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"},
585 {"mempool_sequence", RPCArg::Type::BOOL, RPCArg::Default{false}, "If verbose=false, returns a json object with transaction list and mempool sequence number attached."},
586 },
587 {
588 RPCResult{"for verbose = false",
589 RPCResult::Type::ARR, "", "",
590 {
591 {RPCResult::Type::STR_HEX, "", "The transaction id"},
592 }},
593 RPCResult{"for verbose = true",
594 RPCResult::Type::OBJ_DYN, "", "",
595 {
596 {RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()},
597 }},
598 RPCResult{"for verbose = false and mempool_sequence = true",
599 RPCResult::Type::OBJ, "", "",
600 {
601 {RPCResult::Type::ARR, "txids", "",
602 {
603 {RPCResult::Type::STR_HEX, "", "The transaction id"},
604 }},
605 {RPCResult::Type::NUM, "mempool_sequence", "The mempool sequence value."},
606 }},
607 },
608 RPCExamples{
609 HelpExampleCli("getrawmempool", "true")
610 + HelpExampleRpc("getrawmempool", "true")
611 },
612 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
613 {
614 bool fVerbose = false;
615 if (!request.params[0].isNull())
616 fVerbose = request.params[0].get_bool();
617
618 bool include_mempool_sequence = false;
619 if (!request.params[1].isNull()) {
620 include_mempool_sequence = request.params[1].get_bool();
621 }
622
623 NodeContext& node = EnsureAnyNodeContext(request.context);
624 ChainstateManager& chainman = EnsureChainman(node);
625 return MempoolToJSON(chainman, EnsureAnyMemPool(request.context), fVerbose, include_mempool_sequence);
626 },
627 };
628 }
629
630 static RPCHelpMan getmempoolancestors()
631 {
632 return RPCHelpMan{"getmempoolancestors",
633 "\nIf txid is in the mempool, returns all in-mempool ancestors.\n",
634 {
635 {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
636 {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"},
637 },
638 {
639 RPCResult{"for verbose = false",
640 RPCResult::Type::ARR, "", "",
641 {{RPCResult::Type::STR_HEX, "", "The transaction id of an in-mempool ancestor transaction"}}},
642 RPCResult{"for verbose = true",
643 RPCResult::Type::OBJ_DYN, "", "",
644 {
645 {RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()},
646 }},
647 },
648 RPCExamples{
649 HelpExampleCli("getmempoolancestors", "\"mytxid\"")
650 + HelpExampleRpc("getmempoolancestors", "\"mytxid\"")
651 },
652 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
653 {
654 bool fVerbose = false;
655 if (!request.params[1].isNull())
656 fVerbose = request.params[1].get_bool();
657
658 uint256 hash = ParseHashV(request.params[0], "parameter 1");
659
660 const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
661 ChainstateManager& chainman = EnsureAnyChainman(request.context);
662 LOCK(::cs_main);
663 const CChain& active_chain = chainman.ActiveChain();
664 const int next_block_height = active_chain.Height() + 1;
665 LOCK(mempool.cs);
666 // TODO: Release cs_main after mempool.cs acquired
667
668 const auto entry{mempool.GetEntry(Txid::FromUint256(hash))};
669 if (entry == nullptr) {
670 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
671 }
672
673 auto ancestors{mempool.AssumeCalculateMemPoolAncestors(self.m_name, *entry, CTxMemPool::Limits::NoLimits(), /*fSearchForParents=*/false)};
674
675 if (!fVerbose) {
676 UniValue o(UniValue::VARR);
677 for (CTxMemPool::txiter ancestorIt : ancestors) {
678 o.push_back(ancestorIt->GetTx().GetHash().ToString());
679 }
680 return o;
681 } else {
682 UniValue o(UniValue::VOBJ);
683 for (CTxMemPool::txiter ancestorIt : ancestors) {
684 const CTxMemPoolEntry &e = *ancestorIt;
685 const uint256& _hash = e.GetTx().GetHash();
686 UniValue info(UniValue::VOBJ);
687 entryToJSON(mempool, info, e, next_block_height);
688 o.pushKV(_hash.ToString(), std::move(info));
689 }
690 return o;
691 }
692 },
693 };
694 }
695
696 static RPCHelpMan getmempooldescendants()
697 {
698 return RPCHelpMan{"getmempooldescendants",
699 "\nIf txid is in the mempool, returns all in-mempool descendants.\n",
700 {
701 {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
702 {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"},
703 },
704 {
705 RPCResult{"for verbose = false",
706 RPCResult::Type::ARR, "", "",
707 {{RPCResult::Type::STR_HEX, "", "The transaction id of an in-mempool descendant transaction"}}},
708 RPCResult{"for verbose = true",
709 RPCResult::Type::OBJ_DYN, "", "",
710 {
711 {RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()},
712 }},
713 },
714 RPCExamples{
715 HelpExampleCli("getmempooldescendants", "\"mytxid\"")
716 + HelpExampleRpc("getmempooldescendants", "\"mytxid\"")
717 },
718 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
719 {
720 bool fVerbose = false;
721 if (!request.params[1].isNull())
722 fVerbose = request.params[1].get_bool();
723
724 uint256 hash = ParseHashV(request.params[0], "parameter 1");
725
726 const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
727 ChainstateManager& chainman = EnsureAnyChainman(request.context);
728 LOCK(::cs_main);
729 const CChain& active_chain = chainman.ActiveChain();
730 const int next_block_height = active_chain.Height() + 1;
731 LOCK(mempool.cs);
732 // TODO: Release cs_main after mempool.cs acquired
733
734 const auto it{mempool.GetIter(hash)};
735 if (!it) {
736 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
737 }
738
739 CTxMemPool::setEntries setDescendants;
740 mempool.CalculateDescendants(*it, setDescendants);
741 // CTxMemPool::CalculateDescendants will include the given tx
742 setDescendants.erase(*it);
743
744 if (!fVerbose) {
745 UniValue o(UniValue::VARR);
746 for (CTxMemPool::txiter descendantIt : setDescendants) {
747 o.push_back(descendantIt->GetTx().GetHash().ToString());
748 }
749
750 return o;
751 } else {
752 UniValue o(UniValue::VOBJ);
753 for (CTxMemPool::txiter descendantIt : setDescendants) {
754 const CTxMemPoolEntry &e = *descendantIt;
755 const uint256& _hash = e.GetTx().GetHash();
756 UniValue info(UniValue::VOBJ);
757 entryToJSON(mempool, info, e, next_block_height);
758 o.pushKV(_hash.ToString(), std::move(info));
759 }
760 return o;
761 }
762 },
763 };
764 }
765
766 static RPCHelpMan getmempoolentry()
767 {
768 return RPCHelpMan{"getmempoolentry",
769 "\nReturns mempool data for given transaction\n",
770 {
771 {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
772 },
773 RPCResult{
774 RPCResult::Type::OBJ, "", "", MempoolEntryDescription()},
775 RPCExamples{
776 HelpExampleCli("getmempoolentry", "\"mytxid\"")
777 + HelpExampleRpc("getmempoolentry", "\"mytxid\"")
778 },
779 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
780 {
781 uint256 hash = ParseHashV(request.params[0], "parameter 1");
782
783 const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
784 ChainstateManager& chainman = EnsureAnyChainman(request.context);
785 LOCK(::cs_main);
786 const CChain& active_chain = chainman.ActiveChain();
787 const int next_block_height = active_chain.Height() + 1;
788 LOCK(mempool.cs);
789 // TODO: Release cs_main after mempool.cs acquired
790
791 const auto entry{mempool.GetEntry(Txid::FromUint256(hash))};
792 if (entry == nullptr) {
793 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
794 }
795
796 UniValue info(UniValue::VOBJ);
797 entryToJSON(mempool, info, *entry, next_block_height);
798 return info;
799 },
800 };
801 }
802
803 static RPCHelpMan gettxspendingprevout()
804 {
805 return RPCHelpMan{"gettxspendingprevout",
806 "Scans the mempool to find transactions spending any of the given outputs",
807 {
808 {"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The transaction outputs that we want to check, and within each, the txid (string) vout (numeric).",
809 {
810 {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
811 {
812 {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
813 {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
814 },
815 },
816 },
817 },
818 },
819 RPCResult{
820 RPCResult::Type::ARR, "", "",
821 {
822 {RPCResult::Type::OBJ, "", "",
823 {
824 {RPCResult::Type::STR_HEX, "txid", "the transaction id of the checked output"},
825 {RPCResult::Type::NUM, "vout", "the vout value of the checked output"},
826 {RPCResult::Type::STR_HEX, "spendingtxid", /*optional=*/true, "the transaction id of the mempool transaction spending this output (omitted if unspent)"},
827 }},
828 }
829 },
830 RPCExamples{
831 HelpExampleCli("gettxspendingprevout", "\"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":3}]\"")
832 + HelpExampleRpc("gettxspendingprevout", "[{\"txid\":\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\",\"vout\":3}]")
833 },
834 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
835 {
836 const UniValue& output_params = request.params[0].get_array();
837 if (output_params.empty()) {
838 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, outputs are missing");
839 }
840
841 std::vector<COutPoint> prevouts;
842 prevouts.reserve(output_params.size());
843
844 for (unsigned int idx = 0; idx < output_params.size(); idx++) {
845 const UniValue& o = output_params[idx].get_obj();
846
847 RPCTypeCheckObj(o,
848 {
849 {"txid", UniValueType(UniValue::VSTR)},
850 {"vout", UniValueType(UniValue::VNUM)},
851 }, /*fAllowNull=*/false, /*fStrict=*/true);
852
853 const Txid txid = Txid::FromUint256(ParseHashO(o, "txid"));
854 const int nOutput{o.find_value("vout").getInt<int>()};
855 if (nOutput < 0) {
856 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
857 }
858
859 prevouts.emplace_back(txid, nOutput);
860 }
861
862 const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
863 LOCK(mempool.cs);
864
865 UniValue result{UniValue::VARR};
866
867 for (const COutPoint& prevout : prevouts) {
868 UniValue o(UniValue::VOBJ);
869 o.pushKV("txid", prevout.hash.ToString());
870 o.pushKV("vout", (uint64_t)prevout.n);
871
872 const CTransaction* spendingTx = mempool.GetConflictTx(prevout);
873 if (spendingTx != nullptr) {
874 o.pushKV("spendingtxid", spendingTx->GetHash().ToString());
875 }
876
877 result.push_back(std::move(o));
878 }
879
880 return result;
881 },
882 };
883 }
884
885 UniValue MempoolInfoToJSON(const CTxMemPool& pool, const std::optional<MempoolHistogramFeeRates>& histogram_floors)
886 {
887 // Make sure this call is atomic in the pool.
888 LOCK(pool.cs);
889 UniValue ret(UniValue::VOBJ);
890 ret.pushKV("loaded", pool.GetLoadTried());
891 ret.pushKV("size", (int64_t)pool.size());
892 ret.pushKV("bytes", (int64_t)pool.GetTotalTxSize());
893 ret.pushKV("usage", (int64_t)pool.DynamicMemoryUsage());
894 ret.pushKV("total_fee", ValueFromAmount(pool.GetTotalFee()));
895 ret.pushKV("maxmempool", pool.m_opts.max_size_bytes);
896 ret.pushKV("mempoolminfee", ValueFromAmount(std::max(pool.GetMinFee(), pool.m_opts.min_relay_feerate).GetFeePerK()));
897 ret.pushKV("minrelaytxfee", ValueFromAmount(pool.m_opts.min_relay_feerate.GetFeePerK()));
898 ret.pushKV("incrementalrelayfee", ValueFromAmount(pool.m_opts.incremental_relay_feerate.GetFeePerK()));
899 ret.pushKV("dustrelayfee", ValueFromAmount(pool.m_opts.dust_relay_feerate.GetFeePerK()));
900 ret.pushKV("dustrelayfeefloor", ValueFromAmount(pool.m_opts.dust_relay_feerate_floor.GetFeePerK()));
901 if (pool.m_opts.dust_relay_target == 0) {
902 ret.pushKV("dustdynamic", "off");
903 } else {
904 std::string multiplier_str = strprintf("%u", pool.m_opts.dust_relay_multiplier / 1000);
905 if (pool.m_opts.dust_relay_multiplier % 1000) {
906 multiplier_str += strprintf(".%03u", pool.m_opts.dust_relay_multiplier % 1000);
907 while (multiplier_str.back() == '0') multiplier_str.pop_back();
908 }
909 if (pool.m_opts.dust_relay_target < 0) {
910 ret.pushKV("dustdynamic", strprintf("%s*target:%u", multiplier_str, -pool.m_opts.dust_relay_target));
911 } else { // pool.m_opts.dust_relay_target > 0
912 ret.pushKV("dustdynamic", strprintf("%s*mempool:%u", multiplier_str, pool.m_opts.dust_relay_target));
913 }
914 }
915 ret.pushKV("unbroadcastcount", uint64_t{pool.GetUnbroadcastTxs().size()});
916 ret.pushKV("fullrbf", (pool.m_opts.rbf_policy == RBFPolicy::Always));
917 switch (pool.m_opts.rbf_policy) {
918 case RBFPolicy::Never : ret.pushKV("rbf_policy", "never"); break;
919 case RBFPolicy::OptIn : ret.pushKV("rbf_policy", "optin"); break;
920 case RBFPolicy::Always: ret.pushKV("rbf_policy", "always"); break;
921 }
922 switch (pool.m_opts.truc_policy) {
923 case TRUCPolicy::Reject : ret.pushKV("truc_policy", "reject"); break;
924 case TRUCPolicy::Accept : ret.pushKV("truc_policy", "accept"); break;
925 case TRUCPolicy::Enforce: ret.pushKV("truc_policy", "enforce"); break;
926 }
927
928 if (histogram_floors) {
929 const MempoolHistogramFeeRates& floors{histogram_floors.value()};
930
931 std::vector<uint64_t> sizes(floors.size(), 0);
932 std::vector<uint64_t> count(floors.size(), 0);
933 std::vector<CAmount> fees(floors.size(), 0);
934
935 for (const CTxMemPoolEntry& e : pool.mapTx) {
936 const CAmount fee{e.GetFee()};
937 const uint32_t size{uint32_t(e.GetTxSize())};
938
939 const CAmount afees{e.GetModFeesWithAncestors()}, dfees{e.GetModFeesWithDescendants()};
940 const uint32_t asize{uint32_t(e.GetSizeWithAncestors())}, dsize{uint32_t(e.GetSizeWithDescendants())};
941
942 // Do not use CFeeRate here, since it rounds up, and this should be rounding down
943 const CAmount fpb{fee / size}; // Fee rate per byte
944 const CAmount afpb{afees / asize}; // Fee rate per byte including ancestors
945 const CAmount dfpb{dfees / dsize}; // Fee rate per byte including descendants
946
947 // Fee rate per byte including ancestors & descendants
948 // (fee/size are included in both, so subtracted to avoid double-counting)
949 const CAmount tfpb{(afees + dfees - fee) / (asize + dsize - size)};
950
951 const CAmount fee_rate{std::max(std::min(dfpb, tfpb), std::min(fpb, afpb))};
952
953 // Distribute fee rates
954 for (size_t i = floors.size(); i > 0;) {
955 --i;
956 if (fee_rate >= floors[i]) {
957 sizes[i] += size;
958 ++count[i];
959 fees[i] += fee;
960 break;
961 }
962 }
963 }
964
965 // Track total amount of available fees in fee rate groups
966 CAmount total_fees = 0;
967 UniValue info(UniValue::VOBJ);
968 for (size_t i = 0; i < floors.size(); ++i) {
969 UniValue info_sub(UniValue::VOBJ);
970 info_sub.pushKV("sizes", sizes[i]);
971 info_sub.pushKV("count", count.at(i));
972 info_sub.pushKV("fees", fees.at(i));
973 info_sub.pushKV("from_feerate", floors[i]);
974 info_sub.pushKV("to_feerate", i == floors.size() - 1 ? std::numeric_limits<int64_t>::max() : floors[i + 1]);
975 total_fees += fees.at(i);
976 info.pushKV(ToString(floors[i]), info_sub);
977 }
978 info.pushKV("total_fees", total_fees);
979 ret.pushKV("fee_histogram", info);
980 }
981
982 return ret;
983 }
984
985 static RPCHelpMan getmempoolinfo()
986 {
987 return RPCHelpMan{"getmempoolinfo",
988 "Returns details on the active state of the TX memory pool.\n",
989 {
990 {"fee_histogram|with_fee_histogram", {RPCArg::Type::ARR, RPCArg::Type::BOOL}, RPCArg::Optional::OMITTED, "Fee statistics grouped by fee rate ranges",
991 {
992 {"fee_rate", RPCArg::Type::NUM, RPCArg::Optional::NO, "Fee rate (in " + CURRENCY_ATOM + "/vB) to group the fees by"},
993 },
994 },
995 },
996 RPCResult{
997 RPCResult::Type::OBJ, "", "",
998 {
999 {RPCResult::Type::BOOL, "loaded", "True if the initial load attempt of the persisted mempool finished"},
1000 {RPCResult::Type::NUM, "size", "Current tx count"},
1001 {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"},
1002 {RPCResult::Type::NUM, "usage", "Total memory usage for the mempool"},
1003 {RPCResult::Type::STR_AMOUNT, "total_fee", "Total fees for the mempool in " + CURRENCY_UNIT + ", ignoring modified fees through prioritisetransaction"},
1004 {RPCResult::Type::NUM, "maxmempool", "Maximum memory usage for the mempool"},
1005 {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"},
1006 {RPCResult::Type::STR_AMOUNT, "minrelaytxfee", "Current minimum relay fee for transactions"},
1007 {RPCResult::Type::NUM, "incrementalrelayfee", "minimum fee rate increment for mempool limiting or replacement in " + CURRENCY_UNIT + "/kvB"},
1008 {RPCResult::Type::NUM, "dustrelayfee", "Current fee rate used to define dust, the value of an output so small it will cost more to spend than its value, in " + CURRENCY_UNIT + "/kvB"},
1009 {RPCResult::Type::NUM, "dustrelayfeefloor", "Minimum fee rate used to define dust in " + CURRENCY_UNIT + "/kvB"},
1010 {RPCResult::Type::STR, "dustdynamic", "Method for automatic adjustments to dustrelayfee (one of: off, target:<blocks>, or mempool:<kB>)"},
1011 {RPCResult::Type::NUM, "unbroadcastcount", "Current number of transactions that haven't passed initial broadcast yet"},
1012 {RPCResult::Type::BOOL, "fullrbf", "True if the mempool accepts RBF without replaceability signaling inspection"},
1013 {RPCResult::Type::STR, "rbf_policy", "Policy used for replacing conflicting transactions by fee (one of: never, optin, always)"},
1014 {RPCResult::Type::STR, "truc_policy", "Behaviour for transactions requesting limits (one of: reject, accept, enforce)"},
1015 {RPCResult::Type::OBJ_DYN, "fee_histogram", /*optional=*/true, "",
1016 {
1017 {RPCResult::Type::OBJ, "<fee_rate_group>", "Fee rate group named by its lower bound (in " + CURRENCY_ATOM + "/vB), identical to the \"from_feerate\" field below",
1018 {
1019 {RPCResult::Type::NUM, "sizes", "Cumulative size of all transactions in the fee rate group (in vBytes)"},
1020 {RPCResult::Type::NUM, "count", "Number of transactions in the fee rate group"},
1021 {RPCResult::Type::NUM, "fees", "Cumulative fees of all transactions in the fee rate group (in " + CURRENCY_ATOM + ")"},
1022 {RPCResult::Type::NUM, "from_feerate", "Group contains transactions with fee rates equal or greater than this value (in " + CURRENCY_ATOM + "/vB)"},
1023 {RPCResult::Type::NUM, "to_feerate", /*optional=*/true, "Group contains transactions with fee rates equal or less than this value (in " + CURRENCY_ATOM + "/vB)"},
1024 }},
1025 {RPCResult::Type::ELISION, "", ""},
1026 {RPCResult::Type::NUM, "total_fees", "Total available fees in mempool (in " + CURRENCY_ATOM + ")"},
1027 }, /*skip_type_check=*/ true},
1028 }},
1029 RPCExamples{
1030 HelpExampleCli("getmempoolinfo", "") +
1031 HelpExampleCli("getmempoolinfo", R"("[0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 17, 20, 25, 30, 40, 50, 60, 70, 80, 100, 120, 140, 170, 200]")") +
1032 HelpExampleRpc("getmempoolinfo", "") +
1033 HelpExampleRpc("getmempoolinfo", R"([0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 17, 20, 25, 30, 40, 50, 60, 70, 80, 100, 120, 140, 170, 200])")
1034 },
1035 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1036 {
1037 MempoolHistogramFeeRates histogram_floors;
1038 std::optional<MempoolHistogramFeeRates> histogram_floors_opt = std::nullopt;
1039
1040 if (request.params[0].isBool()) {
1041 if (request.params[0].isTrue()) {
1042 histogram_floors_opt = MempoolInfoToJSON_const_histogram_floors;
1043 }
1044 } else if (!request.params[0].isNull()) {
1045 const UniValue histogram_floors_univalue = request.params[0].get_array();
1046
1047 if (histogram_floors_univalue.empty()) {
1048 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid number of parameters");
1049 }
1050
1051 for (size_t i = 0; i < histogram_floors_univalue.size(); ++i) {
1052 int64_t value = histogram_floors_univalue[i].getInt<int64_t>();
1053
1054 if (value < 0) {
1055 throw JSONRPCError(RPC_INVALID_PARAMETER, "Non-negative values are expected");
1056 } else if (i > 0 && histogram_floors.back() >= value) {
1057 throw JSONRPCError(RPC_INVALID_PARAMETER, "Strictly increasing values are expected");
1058 }
1059
1060 histogram_floors.push_back(value);
1061 }
1062 histogram_floors_opt = std::optional<MempoolHistogramFeeRates>(std::move(histogram_floors));
1063 }
1064
1065 return MempoolInfoToJSON(EnsureAnyMemPool(request.context), histogram_floors_opt);
1066 },
1067 };
1068 }
1069
1070 static RPCHelpMan importmempool()
1071 {
1072 return RPCHelpMan{
1073 "importmempool",
1074 "Import a mempool.dat file and attempt to add its contents to the mempool.\n"
1075 "Warning: Importing untrusted files is dangerous, especially if metadata from the file is taken over.",
1076 {
1077 {"filepath", RPCArg::Type::STR, RPCArg::Optional::NO, "The mempool file"},
1078 {"options",
1079 RPCArg::Type::OBJ_NAMED_PARAMS,
1080 RPCArg::Optional::OMITTED,
1081 "",
1082 {
1083 {"use_current_time", RPCArg::Type::BOOL, RPCArg::Default{true},
1084 "Whether to use the current system time or use the entry time metadata from the mempool file.\n"
1085 "Warning: Importing untrusted metadata may lead to unexpected issues and undesirable behavior."},
1086 {"apply_fee_delta_priority", RPCArg::Type::BOOL, RPCArg::Default{false},
1087 "Whether to apply the fee delta metadata from the mempool file.\n"
1088 "It will be added to any existing fee deltas.\n"
1089 "The fee delta can be set by the prioritisetransaction RPC.\n"
1090 "Warning: Importing untrusted metadata may lead to unexpected issues and undesirable behavior.\n"
1091 "Only set this bool if you understand what it does."},
1092 {"apply_unbroadcast_set", RPCArg::Type::BOOL, RPCArg::Default{false},
1093 "Whether to apply the unbroadcast set metadata from the mempool file.\n"
1094 "Warning: Importing untrusted metadata may lead to unexpected issues and undesirable behavior."},
1095 },
1096 RPCArgOptions{.oneline_description = "options"}},
1097 },
1098 RPCResult{RPCResult::Type::OBJ, "", "", std::vector<RPCResult>{}},
1099 RPCExamples{HelpExampleCli("importmempool", "/path/to/mempool.dat") + HelpExampleRpc("importmempool", "/path/to/mempool.dat")},
1100 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
1101 EnsureNotWalletRestricted(request);
1102
1103 const NodeContext& node{EnsureAnyNodeContext(request.context)};
1104
1105 CTxMemPool& mempool{EnsureMemPool(node)};
1106 ChainstateManager& chainman = EnsureChainman(node);
1107 Chainstate& chainstate = chainman.ActiveChainstate();
1108
1109 if (chainman.IsInitialBlockDownload()) {
1110 throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Can only import the mempool after the block download and sync is done.");
1111 }
1112
1113 const fs::path load_path{fs::u8path(request.params[0].get_str())};
1114 const UniValue& use_current_time{request.params[1]["use_current_time"]};
1115 const UniValue& apply_fee_delta{request.params[1]["apply_fee_delta_priority"]};
1116 const UniValue& apply_unbroadcast{request.params[1]["apply_unbroadcast_set"]};
1117 node::ImportMempoolOptions opts{
1118 .use_current_time = use_current_time.isNull() ? true : use_current_time.get_bool(),
1119 .apply_fee_delta_priority = apply_fee_delta.isNull() ? false : apply_fee_delta.get_bool(),
1120 .apply_unbroadcast_set = apply_unbroadcast.isNull() ? false : apply_unbroadcast.get_bool(),
1121 };
1122
1123 if (!node::LoadMempool(mempool, load_path, chainstate, std::move(opts))) {
1124 throw JSONRPCError(RPC_MISC_ERROR, "Unable to import mempool file, see debug log for details.");
1125 }
1126
1127 UniValue ret{UniValue::VOBJ};
1128 return ret;
1129 },
1130 };
1131 }
1132
1133 static RPCHelpMan savemempool()
1134 {
1135 return RPCHelpMan{"savemempool",
1136 "\nDumps the mempool to disk. It will fail until the previous dump is fully loaded.\n",
1137 {},
1138 RPCResult{
1139 RPCResult::Type::OBJ, "", "",
1140 {
1141 {RPCResult::Type::STR, "filename", "the directory and file where the mempool was saved"},
1142 }},
1143 RPCExamples{
1144 HelpExampleCli("savemempool", "")
1145 + HelpExampleRpc("savemempool", "")
1146 },
1147 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1148 {
1149 const ArgsManager& args{EnsureAnyArgsman(request.context)};
1150 const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
1151
1152 if (!mempool.GetLoadTried()) {
1153 throw JSONRPCError(RPC_MISC_ERROR, "The mempool was not loaded yet");
1154 }
1155
1156 const fs::path& dump_path = MempoolPath(args);
1157
1158 if (!DumpMempool(mempool, dump_path)) {
1159 throw JSONRPCError(RPC_MISC_ERROR, "Unable to dump mempool to disk");
1160 }
1161
1162 UniValue ret(UniValue::VOBJ);
1163 ret.pushKV("filename", dump_path.utf8string());
1164
1165 return ret;
1166 },
1167 };
1168 }
1169
1170 static std::vector<RPCResult> OrphanDescription()
1171 {
1172 return {
1173 RPCResult{RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
1174 RPCResult{RPCResult::Type::STR_HEX, "wtxid", "The transaction witness hash in hex"},
1175 RPCResult{RPCResult::Type::NUM, "bytes", "The serialized transaction size in bytes"},
1176 RPCResult{RPCResult::Type::NUM, "vsize", "The virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted. CAUTION: Since orphan transactions are missing input data, this can be incorrect!"},
1177 RPCResult{RPCResult::Type::NUM, "weight", "The transaction weight as defined in BIP 141."},
1178 RPCResult{RPCResult::Type::NUM_TIME, "entry", "The entry time into the orphanage expressed in " + UNIX_EPOCH_TIME},
1179 RPCResult{RPCResult::Type::NUM_TIME, "expiration", "The orphan expiration time expressed in " + UNIX_EPOCH_TIME},
1180 RPCResult{RPCResult::Type::ARR, "from", "",
1181 {
1182 RPCResult{RPCResult::Type::NUM, "peer_id", "Peer ID"},
1183 }},
1184 };
1185 }
1186
1187 static UniValue OrphanToJSON(const TxOrphanage::OrphanTxBase& orphan)
1188 {
1189 UniValue o(UniValue::VOBJ);
1190 o.pushKV("txid", orphan.tx->GetHash().ToString());
1191 o.pushKV("wtxid", orphan.tx->GetWitnessHash().ToString());
1192 o.pushKV("bytes", orphan.tx->GetTotalSize());
1193 o.pushKV("vsize", GetVirtualTransactionSize(*orphan.tx));
1194 o.pushKV("weight", GetTransactionWeight(*orphan.tx));
1195 o.pushKV("entry", int64_t{TicksSinceEpoch<std::chrono::seconds>(orphan.nTimeExpire - ORPHAN_TX_EXPIRE_TIME)});
1196 o.pushKV("expiration", int64_t{TicksSinceEpoch<std::chrono::seconds>(orphan.nTimeExpire)});
1197 UniValue from(UniValue::VARR);
1198 for (const auto fromPeer: orphan.announcers) {
1199 from.push_back(fromPeer);
1200 }
1201 o.pushKV("from", from);
1202 return o;
1203 }
1204
1205 static RPCHelpMan getorphantxs()
1206 {
1207 return RPCHelpMan{"getorphantxs",
1208 "\nShows transactions in the tx orphanage.\n"
1209 "\nEXPERIMENTAL warning: this call may be changed in future releases.\n",
1210 {
1211 {"verbosity", RPCArg::Type::NUM, RPCArg::Default{0}, "0 for an array of txids (may contain duplicates), 1 for an array of objects with tx details, and 2 for details from (1) and tx hex",
1212 RPCArgOptions{.skip_type_check = true}},
1213 },
1214 {
1215 RPCResult{"for verbose = 0",
1216 RPCResult::Type::ARR, "", "",
1217 {
1218 {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
1219 }},
1220 RPCResult{"for verbose = 1",
1221 RPCResult::Type::ARR, "", "",
1222 {
1223 {RPCResult::Type::OBJ, "", "", OrphanDescription()},
1224 }},
1225 RPCResult{"for verbose = 2",
1226 RPCResult::Type::ARR, "", "",
1227 {
1228 {RPCResult::Type::OBJ, "", "",
1229 Cat<std::vector<RPCResult>>(
1230 OrphanDescription(),
1231 {{RPCResult::Type::STR_HEX, "hex", "The serialized, hex-encoded transaction data"}}
1232 )
1233 },
1234 }},
1235 },
1236 RPCExamples{
1237 HelpExampleCli("getorphantxs", "2")
1238 + HelpExampleRpc("getorphantxs", "2")
1239 },
1240 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1241 {
1242 const NodeContext& node = EnsureAnyNodeContext(request.context);
1243 PeerManager& peerman = EnsurePeerman(node);
1244 std::vector<TxOrphanage::OrphanTxBase> orphanage = peerman.GetOrphanTransactions();
1245
1246 int verbosity{ParseVerbosity(request.params[0], /*default_verbosity=*/0, /*allow_bool*/false)};
1247
1248 UniValue ret(UniValue::VARR);
1249
1250 if (verbosity == 0) {
1251 for (auto const& orphan : orphanage) {
1252 ret.push_back(orphan.tx->GetHash().ToString());
1253 }
1254 } else if (verbosity == 1) {
1255 for (auto const& orphan : orphanage) {
1256 ret.push_back(OrphanToJSON(orphan));
1257 }
1258 } else if (verbosity == 2) {
1259 for (auto const& orphan : orphanage) {
1260 UniValue o{OrphanToJSON(orphan)};
1261 o.pushKV("hex", EncodeHexTx(*orphan.tx));
1262 ret.push_back(o);
1263 }
1264 } else {
1265 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid verbosity value " + ToString(verbosity));
1266 }
1267
1268 return ret;
1269 },
1270 };
1271 }
1272
1273 static RPCHelpMan submitpackage()
1274 {
1275 return RPCHelpMan{"submitpackage",
1276 "Submit a package of raw transactions (serialized, hex-encoded) to local node.\n"
1277 "The package will be validated according to consensus and mempool policy rules. If any transaction passes, it will be accepted to mempool.\n"
1278 "This RPC is experimental and the interface may be unstable. Refer to doc/policy/packages.md for documentation on package policies.\n"
1279 "Warning: successful submission does not mean the transactions will propagate throughout the network.\n"
1280 ,
1281 {
1282 {"package", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of raw transactions.\n"
1283 "The package must solely consist of a child transaction and all of its unconfirmed parents, if any. None of the parents may depend on each other.\n"
1284 "The package must be topologically sorted, with the child being the last element in the array.",
1285 {
1286 {"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
1287 },
1288 },
1289 {"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},
1290 "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
1291 "/kvB.\nFee rates larger than 1BTC/kvB are rejected.\nSet to 0 to accept any fee rate."},
1292 {"maxburnamount", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_BURN_AMOUNT)},
1293 "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"
1294 "If burning funds through unspendable outputs is desired, increase this value.\n"
1295 "This check is based on heuristics and does not guarantee spendability of outputs.\n"
1296 },
1297 },
1298 RPCResult{
1299 RPCResult::Type::OBJ, "", "",
1300 {
1301 {RPCResult::Type::STR, "package_msg", "The transaction package result message. \"success\" indicates all transactions were accepted into or are already in the mempool."},
1302 {RPCResult::Type::OBJ_DYN, "tx-results", "transaction results keyed by wtxid",
1303 {
1304 {RPCResult::Type::OBJ, "wtxid", "transaction wtxid", {
1305 {RPCResult::Type::STR_HEX, "txid", "The transaction id"},
1306 {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."},
1307 {RPCResult::Type::NUM, "vsize", /*optional=*/true, "Sigops-adjusted virtual transaction size."},
1308 {RPCResult::Type::OBJ, "fees", /*optional=*/true, "Transaction fees", {
1309 {RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT},
1310 {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."},
1311 {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.",
1312 {{RPCResult::Type::STR_HEX, "", "transaction wtxid in hex"},
1313 }},
1314 }},
1315 {RPCResult::Type::STR, "error", /*optional=*/true, "The transaction error string, if it was rejected by the mempool"},
1316 }}
1317 }},
1318 {RPCResult::Type::ARR, "replaced-transactions", /*optional=*/true, "List of txids of replaced transactions",
1319 {
1320 {RPCResult::Type::STR_HEX, "", "The transaction id"},
1321 }},
1322 },
1323 },
1324 RPCExamples{
1325 HelpExampleRpc("submitpackage", R"(["raw-parent-tx-1", "raw-parent-tx-2", "raw-child-tx"])") +
1326 HelpExampleCli("submitpackage", R"('["raw-tx-without-unconfirmed-parents"]')")
1327 },
1328 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1329 {
1330 const UniValue raw_transactions = request.params[0].get_array();
1331 if (raw_transactions.empty() || raw_transactions.size() > MAX_PACKAGE_COUNT) {
1332 throw JSONRPCError(RPC_INVALID_PARAMETER,
1333 "Array must contain between 1 and " + ToString(MAX_PACKAGE_COUNT) + " transactions.");
1334 }
1335
1336 // Fee check needs to be run with chainstate and package context
1337 const CFeeRate max_raw_tx_fee_rate{ParseFeeRate(self.Arg<UniValue>("maxfeerate"))};
1338 std::optional<CFeeRate> client_maxfeerate{max_raw_tx_fee_rate};
1339 // 0-value is special; it's mapped to no sanity check
1340 if (max_raw_tx_fee_rate == CFeeRate(0)) {
1341 client_maxfeerate = std::nullopt;
1342 }
1343
1344 // Burn sanity check is run with no context
1345 const CAmount max_burn_amount = request.params[2].isNull() ? 0 : AmountFromValue(request.params[2]);
1346
1347 std::vector<CTransactionRef> txns;
1348 txns.reserve(raw_transactions.size());
1349 for (const auto& rawtx : raw_transactions.getValues()) {
1350 CMutableTransaction mtx;
1351 if (!DecodeHexTx(mtx, rawtx.get_str())) {
1352 throw JSONRPCError(RPC_DESERIALIZATION_ERROR,
1353 "TX decode failed: " + rawtx.get_str() + " Make sure the tx has at least one input.");
1354 }
1355
1356 for (const auto& out : mtx.vout) {
1357 if((out.scriptPubKey.IsUnspendable() || !out.scriptPubKey.HasValidOps()) && out.nValue > max_burn_amount) {
1358 throw JSONRPCTransactionError(TransactionError::MAX_BURN_EXCEEDED);
1359 }
1360 }
1361
1362 txns.emplace_back(MakeTransactionRef(std::move(mtx)));
1363 }
1364 CHECK_NONFATAL(!txns.empty());
1365 if (txns.size() > 1 && !IsChildWithParentsTree(txns)) {
1366 throw JSONRPCTransactionError(TransactionError::INVALID_PACKAGE, "package topology disallowed. not child-with-parents or parents depend on each other.");
1367 }
1368
1369 NodeContext& node = EnsureAnyNodeContext(request.context);
1370 CTxMemPool& mempool = EnsureMemPool(node);
1371 Chainstate& chainstate = EnsureChainman(node).ActiveChainstate();
1372 const auto package_result = WITH_LOCK(::cs_main, return ProcessNewPackage(chainstate, mempool, txns, /*test_accept=*/ false, client_maxfeerate));
1373
1374 std::string package_msg = "success";
1375
1376 // First catch package-wide errors, continue if we can
1377 switch(package_result.m_state.GetResult()) {
1378 case PackageValidationResult::PCKG_RESULT_UNSET:
1379 {
1380 // Belt-and-suspenders check; everything should be successful here
1381 CHECK_NONFATAL(package_result.m_tx_results.size() == txns.size());
1382 for (const auto& tx : txns) {
1383 CHECK_NONFATAL(mempool.exists(GenTxid::Txid(tx->GetHash())));
1384 }
1385 break;
1386 }
1387 case PackageValidationResult::PCKG_MEMPOOL_ERROR:
1388 {
1389 // This only happens with internal bug; user should stop and report
1390 throw JSONRPCTransactionError(TransactionError::MEMPOOL_ERROR,
1391 package_result.m_state.GetRejectReason());
1392 }
1393 case PackageValidationResult::PCKG_POLICY:
1394 case PackageValidationResult::PCKG_TX:
1395 {
1396 // Package-wide error we want to return, but we also want to return individual responses
1397 package_msg = package_result.m_state.ToString();
1398 CHECK_NONFATAL(package_result.m_tx_results.size() == txns.size() ||
1399 package_result.m_tx_results.empty());
1400 break;
1401 }
1402 }
1403
1404 size_t num_broadcast{0};
1405 for (const auto& tx : txns) {
1406 // We don't want to re-submit the txn for validation in BroadcastTransaction
1407 if (!mempool.exists(GenTxid::Txid(tx->GetHash()))) {
1408 continue;
1409 }
1410
1411 // We do not expect an error here; we are only broadcasting things already/still in mempool
1412 std::string err_string;
1413 const auto err = BroadcastTransaction(node, tx, err_string, /*max_tx_fee=*/0, /*relay=*/true, /*wait_callback=*/true);
1414 if (err != TransactionError::OK) {
1415 throw JSONRPCTransactionError(err,
1416 strprintf("transaction broadcast failed: %s (%d transactions were broadcast successfully)",
1417 err_string, num_broadcast));
1418 }
1419 num_broadcast++;
1420 }
1421
1422 UniValue rpc_result{UniValue::VOBJ};
1423 rpc_result.pushKV("package_msg", package_msg);
1424 UniValue tx_result_map{UniValue::VOBJ};
1425 std::set<uint256> replaced_txids;
1426 for (const auto& tx : txns) {
1427 UniValue result_inner{UniValue::VOBJ};
1428 result_inner.pushKV("txid", tx->GetHash().GetHex());
1429 auto it = package_result.m_tx_results.find(tx->GetWitnessHash());
1430 if (it == package_result.m_tx_results.end()) {
1431 // No results, report error and continue
1432 result_inner.pushKV("error", "unevaluated");
1433 continue;
1434 }
1435 const auto& tx_result = it->second;
1436 switch(it->second.m_result_type) {
1437 case MempoolAcceptResult::ResultType::DIFFERENT_WITNESS:
1438 result_inner.pushKV("other-wtxid", it->second.m_other_wtxid.value().GetHex());
1439 break;
1440 case MempoolAcceptResult::ResultType::INVALID:
1441 result_inner.pushKV("error", it->second.m_state.ToString());
1442 break;
1443 case MempoolAcceptResult::ResultType::VALID:
1444 case MempoolAcceptResult::ResultType::MEMPOOL_ENTRY:
1445 result_inner.pushKV("vsize", int64_t{it->second.m_vsize.value()});
1446 UniValue fees(UniValue::VOBJ);
1447 fees.pushKV("base", ValueFromAmount(it->second.m_base_fees.value()));
1448 if (tx_result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
1449 // Effective feerate is not provided for MEMPOOL_ENTRY transactions even
1450 // though modified fees is known, because it is unknown whether package
1451 // feerate was used when it was originally submitted.
1452 fees.pushKV("effective-feerate", ValueFromAmount(tx_result.m_effective_feerate.value().GetFeePerK()));
1453 UniValue effective_includes_res(UniValue::VARR);
1454 for (const auto& wtxid : tx_result.m_wtxids_fee_calculations.value()) {
1455 effective_includes_res.push_back(wtxid.ToString());
1456 }
1457 fees.pushKV("effective-includes", std::move(effective_includes_res));
1458 }
1459 result_inner.pushKV("fees", std::move(fees));
1460 for (const auto& ptx : it->second.m_replaced_transactions) {
1461 replaced_txids.insert(ptx->GetHash());
1462 }
1463 break;
1464 }
1465 tx_result_map.pushKV(tx->GetWitnessHash().GetHex(), std::move(result_inner));
1466 }
1467 rpc_result.pushKV("tx-results", std::move(tx_result_map));
1468 UniValue replaced_list(UniValue::VARR);
1469 for (const uint256& hash : replaced_txids) replaced_list.push_back(hash.ToString());
1470 rpc_result.pushKV("replaced-transactions", std::move(replaced_list));
1471 return rpc_result;
1472 },
1473 };
1474 }
1475
1476 void RegisterMempoolRPCCommands(CRPCTable& t)
1477 {
1478 static const CRPCCommand commands[]{
1479 {"rawtransactions", &sendrawtransaction},
1480 {"rawtransactions", &testmempoolaccept},
1481 {"blockchain", &getmempoolancestors},
1482 {"blockchain", &getmempooldescendants},
1483 {"blockchain", &getmempoolentry},
1484 {"blockchain", &gettxspendingprevout},
1485 {"blockchain", &getmempoolinfo},
1486 {"blockchain", &getrawmempool},
1487 {"blockchain", &importmempool},
1488 {"blockchain", &savemempool},
1489 {"blockchain", &maxmempool},
1490 {"hidden", &getorphantxs},
1491 {"rawtransactions", &submitpackage},
1492 {"rawtransactions", &listmempooltransactions},
1493 };
1494 for (const auto& c : commands) {
1495 t.appendCommand(c.name, &c);
1496 }
1497 }
1498