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 <instantsend/instantsend.h>
9 : #include <llmq/context.h>
10 : #include <util/helpers.h>
11 :
12 : #include <chainparams.h>
13 : #include <core_io.h>
14 : #include <fs.h>
15 : #include <policy/settings.h>
16 : #include <primitives/transaction.h>
17 : #include <rpc/server.h>
18 : #include <rpc/server_util.h>
19 : #include <rpc/util.h>
20 : #include <txmempool.h>
21 : #include <univalue.h>
22 : #include <util/moneystr.h>
23 : #include <validation.h>
24 : #include <util/system.h>
25 : #include <util/strencodings.h>
26 : #include <util/time.h>
27 :
28 : using node::DEFAULT_MAX_RAW_TX_FEE_RATE;
29 : using node::NodeContext;
30 :
31 18317 : RPCHelpMan sendrawtransaction()
32 : {
33 36634 : return RPCHelpMan{"sendrawtransaction",
34 18317 : "\nSubmit a raw transaction (serialized, hex-encoded) to local node and network.\n"
35 : "\nThe transaction will be sent unconditionally to all peers, so using sendrawtransaction\n"
36 : "for manual rebroadcast may degrade privacy by leaking the transaction's origin, as\n"
37 : "nodes will normally not rebroadcast non-wallet transactions already in their mempool.\n"
38 : "\nA specific exception, RPC_TRANSACTION_ALREADY_IN_CHAIN, may throw if the transaction cannot be added to the mempool.\n"
39 : "\nRelated RPCs: createrawtransaction, signrawtransactionwithkey\n",
40 91585 : {
41 18317 : {"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
42 36634 : {"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},
43 18317 : "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
44 : "/kB.\nSet to 0 to accept any fee rate.\n"},
45 18317 : {"instantsend", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "Deprecated and ignored"},
46 18317 : {"bypasslimits", RPCArg::Type::BOOL, RPCArg::Default{false}, "Bypass transaction policy limits"},
47 : },
48 18317 : RPCResult{
49 18317 : RPCResult::Type::STR_HEX, "", "The transaction hash in hex"
50 : },
51 18317 : RPCExamples{
52 : "\nCreate a transaction\n"
53 18317 : + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") +
54 : "Sign the transaction, and get back the hex\n"
55 18317 : + HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") +
56 : "\nSend the transaction (signed hex)\n"
57 18317 : + HelpExampleCli("sendrawtransaction", "\"signedhex\"") +
58 : "\nAs a JSON-RPC call\n"
59 18317 : + HelpExampleRpc("sendrawtransaction", "\"signedhex\"")
60 : },
61 30477 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
62 : {
63 61935 : RPCTypeCheck(request.params, {
64 12160 : UniValue::VSTR,
65 12160 : UniValueType(), // VNUM or VSTR, checked inside AmountFromValue()
66 12160 : UniValue::VBOOL,
67 12160 : UniValue::VBOOL,
68 : });
69 :
70 12160 : CMutableTransaction mtx;
71 12160 : if (!DecodeHexTx(mtx, request.params[0].get_str())) {
72 2 : throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed. Make sure the tx has at least one input.");
73 : }
74 12158 : CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
75 :
76 22170 : const CFeeRate max_raw_tx_fee_rate = request.params[1].isNull() ?
77 2146 : DEFAULT_MAX_RAW_TX_FEE_RATE :
78 10012 : CFeeRate(AmountFromValue(request.params[1]));
79 :
80 12158 : int64_t virtual_size = GetVirtualTransactionSize(*tx);
81 12158 : CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size);
82 :
83 12158 : bool bypass_limits = false;
84 12158 : if (!request.params[3].isNull()) bypass_limits = request.params[3].get_bool();
85 12158 : bilingual_str err_string;
86 12158 : AssertLockNotHeld(cs_main);
87 12158 : NodeContext& node = EnsureAnyNodeContext(request.context);
88 12158 : const TransactionError err = BroadcastTransaction(node, tx, err_string, max_raw_tx_fee, /*relay=*/true, /*wait_callback=*/true, bypass_limits);
89 12158 : if (TransactionError::OK != err) {
90 1133 : throw JSONRPCTransactionError(err, err_string.original);
91 : }
92 :
93 11025 : return tx->GetHash().GetHex();
94 12162 : },
95 : };
96 0 : }
97 :
98 7101 : static RPCHelpMan testmempoolaccept()
99 : {
100 14202 : return RPCHelpMan{"testmempoolaccept",
101 : "\nReturns result of mempool acceptance tests indicating if raw transaction (serialized, hex-encoded) would be accepted by mempool.\n"
102 : "\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"
103 : "\nIf one transaction fails, other transactions may not be fully validated (the 'allowed' key will be blank).\n"
104 7101 : "\nThe maximum number of transactions allowed is " + ToString(MAX_PACKAGE_COUNT) + ".\n"
105 : "\nThis checks if transactions violate the consensus or policy rules.\n"
106 : "\nSee sendrawtransaction call.\n",
107 21303 : {
108 14202 : {"rawtxs", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of hex strings of raw transactions.",
109 14202 : {
110 7101 : {"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
111 : },
112 : },
113 14202 : {"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},
114 7101 : "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + "/kB\n"},
115 : },
116 7101 : RPCResult{
117 7101 : RPCResult::Type::ARR, "", "The result of the mempool acceptance test for each raw transaction in the input array.\n"
118 : "Returns results for each transaction in the same order they were passed in.\n"
119 : "Transactions that cannot be fully validated due to failures in other transactions will not contain an 'allowed' result.\n",
120 14202 : {
121 14202 : {RPCResult::Type::OBJ, "", "",
122 49707 : {
123 7101 : {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
124 7101 : {RPCResult::Type::STR, "package-error", /*optional=*/true, "Package validation error, if any (only possible if rawtxs had more than 1 transaction)."},
125 7101 : {RPCResult::Type::BOOL, "allowed", /*optional=*/true, "Whether this tx would be accepted to the mempool and pass client-specified maxfeerate. "
126 : "If not present, the tx was not fully validated due to a failure in another tx in the list."},
127 7101 : {RPCResult::Type::NUM, "vsize", /*optional=*/true, "Transaction size."},
128 14202 : {RPCResult::Type::OBJ, "fees", /*optional=*/true, "Transaction fees (only present if 'allowed' is true)",
129 14202 : {
130 7101 : {RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT},
131 : }},
132 7101 : {RPCResult::Type::STR, "reject-reason", /*optional=*/true, "Rejection string (only present when 'allowed' is false)"},
133 : }},
134 : }
135 : },
136 7101 : RPCExamples{
137 : "\nCreate a transaction\n"
138 7101 : + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") +
139 : "Sign the transaction, and get back the hex\n"
140 7101 : + HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") +
141 : "\nTest acceptance of the transaction (signed hex)\n"
142 7101 : + HelpExampleCli("testmempoolaccept", R"('["signedhex"]')") +
143 : "\nAs a JSON-RPC call\n"
144 7101 : + HelpExampleRpc("testmempoolaccept", "[\"signedhex\"]")
145 : },
146 8050 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
147 : {
148 2853 : RPCTypeCheck(request.params, {
149 949 : UniValue::VARR,
150 949 : UniValueType(), // VNUM or VSTR, checked inside AmountFromValue()
151 : });
152 947 : const UniValue raw_transactions = request.params[0].get_array();
153 947 : if (raw_transactions.size() < 1 || raw_transactions.size() > MAX_PACKAGE_COUNT) {
154 8 : throw JSONRPCError(RPC_INVALID_PARAMETER,
155 4 : "Array must contain between 1 and " + ToString(MAX_PACKAGE_COUNT) + " transactions.");
156 : }
157 :
158 951 : const CFeeRate max_raw_tx_fee_rate = request.params[1].isNull() ?
159 927 : DEFAULT_MAX_RAW_TX_FEE_RATE :
160 12 : CFeeRate(AmountFromValue(request.params[1]));
161 :
162 939 : std::vector<CTransactionRef> txns;
163 939 : txns.reserve(raw_transactions.size());
164 3026 : for (const auto& rawtx : raw_transactions.getValues()) {
165 2089 : CMutableTransaction mtx;
166 2089 : if (!DecodeHexTx(mtx, rawtx.get_str())) {
167 4 : throw JSONRPCError(RPC_DESERIALIZATION_ERROR,
168 2 : "TX decode failed: " + rawtx.get_str() + " Make sure the tx has at least one input.");
169 : }
170 2087 : txns.emplace_back(MakeTransactionRef(std::move(mtx)));
171 2089 : }
172 :
173 937 : NodeContext& node = EnsureAnyNodeContext(request.context);
174 937 : CTxMemPool& mempool = EnsureMemPool(node);
175 937 : ChainstateManager& chainman = EnsureChainman(node);
176 937 : CChainState& chainstate = chainman.ActiveChainstate();
177 1874 : const PackageMempoolAcceptResult package_result = [&] {
178 937 : LOCK(::cs_main);
179 937 : if (txns.size() > 1) return ProcessNewPackage(chainstate, mempool, txns, /*test_accept=*/true);
180 805 : return PackageMempoolAcceptResult(txns[0]->GetHash(),
181 805 : chainman.ProcessTransaction(txns[0], /*test_accept=*/true));
182 937 : }();
183 :
184 937 : UniValue rpc_result(UniValue::VARR);
185 : // We will check transaction fees while we iterate through txns in order. If any transaction fee
186 : // exceeds maxfeerate, we will leave the rest of the validation results blank, because it
187 : // doesn't make sense to return a validation result for a transaction if its ancestor(s) would
188 : // not be submitted.
189 937 : bool exit_early{false};
190 3024 : for (const auto& tx : txns) {
191 2087 : UniValue result_inner(UniValue::VOBJ);
192 2087 : result_inner.pushKV("txid", tx->GetHash().GetHex());
193 2087 : if (package_result.m_state.GetResult() == PackageValidationResult::PCKG_POLICY) {
194 174 : result_inner.pushKV("package-error", package_result.m_state.GetRejectReason());
195 174 : }
196 2087 : auto it = package_result.m_tx_results.find(tx->GetHash());
197 2087 : if (exit_early || it == package_result.m_tx_results.end()) {
198 : // Validation unfinished. Just return the txid.
199 237 : rpc_result.push_back(result_inner);
200 237 : continue;
201 : }
202 1850 : const auto& tx_result = it->second;
203 : // Package testmempoolaccept doesn't allow transactions to already be in the mempool.
204 1850 : CHECK_NONFATAL(tx_result.m_result_type != MempoolAcceptResult::ResultType::MEMPOOL_ENTRY);
205 1850 : if (tx_result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
206 1758 : const CAmount fee = tx_result.m_base_fees.value();
207 : // Check that fee does not exceed maximum fee
208 1758 : const int64_t virtual_size = tx_result.m_vsize.value();
209 1758 : const CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size);
210 1758 : if (max_raw_tx_fee && fee > max_raw_tx_fee) {
211 11 : result_inner.pushKV("allowed", false);
212 11 : result_inner.pushKV("reject-reason", "max-fee-exceeded");
213 11 : exit_early = true;
214 11 : } else {
215 : // Only return the fee and vsize if the transaction would pass ATMP.
216 : // These can be used to calculate the feerate.
217 1747 : result_inner.pushKV("allowed", true);
218 1747 : result_inner.pushKV("vsize", virtual_size);
219 1747 : UniValue fees(UniValue::VOBJ);
220 1747 : fees.pushKV("base", ValueFromAmount(fee));
221 1747 : result_inner.pushKV("fees", fees);
222 1747 : }
223 1758 : } else {
224 92 : result_inner.pushKV("allowed", false);
225 92 : const TxValidationState state = tx_result.m_state;
226 92 : if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
227 12 : result_inner.pushKV("reject-reason", "missing-inputs");
228 12 : } else {
229 80 : result_inner.pushKV("reject-reason", state.GetRejectReason());
230 : }
231 92 : }
232 1850 : rpc_result.push_back(result_inner);
233 2087 : }
234 937 : return rpc_result;
235 955 : },
236 : };
237 0 : }
238 :
239 49323 : static std::vector<RPCResult> MempoolEntryDescription()
240 : {
241 838491 : return {
242 49323 : RPCResult{RPCResult::Type::NUM, "vsize", "Transaction size."},
243 98646 : RPCResult{RPCResult::Type::STR_AMOUNT, "fee", /*optional=*/true,
244 49323 : "transaction fee, denominated in " + CURRENCY_UNIT + " (DEPRECATED, returned only if config option -deprecatedrpc=fees is passed)"},
245 98646 : RPCResult{RPCResult::Type::STR_AMOUNT, "modifiedfee", /*optional=*/true,
246 49323 : "transaction fee with fee deltas used for mining priority, denominated in " + CURRENCY_UNIT +
247 : " (DEPRECATED, returned only if config option -deprecatedrpc=fees is passed)"},
248 49323 : RPCResult{RPCResult::Type::NUM_TIME, "time", "local time transaction entered pool in " + UNIX_EPOCH_TIME},
249 49323 : RPCResult{RPCResult::Type::NUM, "height", "block height when transaction entered pool"},
250 49323 : RPCResult{RPCResult::Type::NUM, "descendantcount", "number of in-mempool descendant transactions (including this one)"},
251 49323 : RPCResult{RPCResult::Type::NUM, "descendantsize", "size of in-mempool descendants (including this one)"},
252 98646 : RPCResult{RPCResult::Type::STR_AMOUNT, "descendantfees", /*optional=*/true,
253 49323 : "transaction fees of in-mempool descendants (including this one) with fee deltas used for mining priority, denominated in " +
254 49323 : CURRENCY_ATOM + "s (DEPRECATED, returned only if config option -deprecatedrpc=fees is passed)"},
255 49323 : RPCResult{RPCResult::Type::NUM, "ancestorcount", "number of in-mempool ancestor transactions (including this one)"},
256 49323 : RPCResult{RPCResult::Type::NUM, "ancestorsize", "size of in-mempool ancestors (including this one)"},
257 98646 : RPCResult{RPCResult::Type::STR_AMOUNT, "ancestorfees", /*optional=*/true,
258 49323 : "transaction fees of in-mempool ancestors (including this one) with fee deltas used for mining priority, denominated in " +
259 49323 : CURRENCY_ATOM + "s (DEPRECATED, returned only if config option -deprecatedrpc=fees is passed)"},
260 98646 : RPCResult{RPCResult::Type::OBJ, "fees", "",
261 246615 : {
262 49323 : RPCResult{RPCResult::Type::STR_AMOUNT, "base", "transaction fee, denominated in " + CURRENCY_UNIT},
263 49323 : RPCResult{RPCResult::Type::STR_AMOUNT, "modified", "transaction fee with fee deltas used for mining priority, denominated in " + CURRENCY_UNIT},
264 49323 : 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},
265 49323 : 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},
266 : }},
267 98646 : RPCResult{RPCResult::Type::ARR, "depends", "unconfirmed transactions used as inputs for this transaction",
268 49323 : {RPCResult{RPCResult::Type::STR_HEX, "transactionid", "parent transaction id"}}},
269 98646 : RPCResult{RPCResult::Type::ARR, "spentby", "unconfirmed transactions spending outputs from this transaction",
270 49323 : {RPCResult{RPCResult::Type::STR_HEX, "transactionid", "child transaction id"}}},
271 49323 : RPCResult{RPCResult::Type::BOOL, "instantsend", "True if this transaction was locked via InstantSend"},
272 49323 : RPCResult{RPCResult::Type::BOOL, "unbroadcast", "Whether this transaction is currently unbroadcast (initial broadcast not yet acknowledged by any peers)"}
273 : };
274 0 : }
275 :
276 6690 : static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPoolEntry& e, const llmq::CInstantSendManager* isman) EXCLUSIVE_LOCKS_REQUIRED(pool.cs)
277 : {
278 6690 : AssertLockHeld(pool.cs);
279 :
280 6690 : info.pushKV("vsize", (int)e.GetTxSize());
281 : // TODO: top-level fee fields are deprecated. deprecated_fee_fields_enabled blocks should be removed in v24
282 6690 : const bool deprecated_fee_fields_enabled{IsDeprecatedRPCEnabled("fees")};
283 6690 : if (deprecated_fee_fields_enabled) {
284 6 : info.pushKV("fee", ValueFromAmount(e.GetFee()));
285 6 : info.pushKV("modifiedfee", ValueFromAmount(e.GetModifiedFee()));
286 6 : }
287 6690 : info.pushKV("time", count_seconds(e.GetTime()));
288 6690 : info.pushKV("height", (int)e.GetHeight());
289 6690 : info.pushKV("descendantcount", e.GetCountWithDescendants());
290 6690 : info.pushKV("descendantsize", e.GetSizeWithDescendants());
291 6690 : if (deprecated_fee_fields_enabled) {
292 6 : info.pushKV("descendantfees", e.GetModFeesWithDescendants());
293 6 : }
294 6690 : info.pushKV("ancestorcount", e.GetCountWithAncestors());
295 6690 : info.pushKV("ancestorsize", e.GetSizeWithAncestors());
296 6690 : if (deprecated_fee_fields_enabled) {
297 6 : info.pushKV("ancestorfees", e.GetModFeesWithAncestors());
298 6 : }
299 :
300 6690 : UniValue fees(UniValue::VOBJ);
301 6690 : fees.pushKV("base", ValueFromAmount(e.GetFee()));
302 6690 : fees.pushKV("modified", ValueFromAmount(e.GetModifiedFee()));
303 6690 : fees.pushKV("ancestor", ValueFromAmount(e.GetModFeesWithAncestors()));
304 6690 : fees.pushKV("descendant", ValueFromAmount(e.GetModFeesWithDescendants()));
305 6690 : info.pushKV("fees", fees);
306 :
307 6690 : const CTransaction& tx = e.GetTx();
308 6690 : std::set<std::string> setDepends;
309 48473 : for (const CTxIn& txin : tx.vin)
310 : {
311 41783 : if (pool.exists(txin.prevout.hash))
312 20372 : setDepends.insert(txin.prevout.hash.ToString());
313 : }
314 :
315 6690 : UniValue depends(UniValue::VARR);
316 27062 : for (const std::string& dep : setDepends)
317 : {
318 20372 : depends.push_back(dep);
319 : }
320 :
321 6690 : info.pushKV("depends", depends);
322 :
323 6690 : UniValue spent(UniValue::VARR);
324 6690 : const CTxMemPool::txiter& it = pool.mapTx.find(tx.GetHash());
325 6690 : const CTxMemPoolEntry::Children& children = it->GetMemPoolChildrenConst();
326 23451 : for (const CTxMemPoolEntry& child : children) {
327 16761 : spent.push_back(child.GetTx().GetHash().ToString());
328 : }
329 :
330 6690 : info.pushKV("spentby", spent);
331 6690 : info.pushKV("instantlock", isman ? util::to_string(isman->IsLocked(tx.GetHash())) : "unknown");
332 6690 : info.pushKV("unbroadcast", pool.IsUnbroadcastTx(tx.GetHash()));
333 6690 : }
334 :
335 23639 : UniValue MempoolToJSON(const CTxMemPool& pool, const llmq::CInstantSendManager* isman, bool verbose, bool include_mempool_sequence)
336 : {
337 23639 : if (verbose) {
338 1570 : if (include_mempool_sequence) {
339 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Verbose results cannot contain mempool sequence values.");
340 : }
341 1570 : LOCK(pool.cs);
342 1570 : UniValue o(UniValue::VOBJ);
343 6660 : for (const CTxMemPoolEntry& e : pool.mapTx) {
344 5090 : const uint256& hash = e.GetTx().GetHash();
345 5090 : UniValue info(UniValue::VOBJ);
346 5090 : entryToJSON(pool, info, e, isman);
347 : // Mempool has unique entries so there is no advantage in using
348 : // UniValue::pushKV, which checks if the key already exists in O(N).
349 : // UniValue::__pushKV is used instead which currently is O(1).
350 5090 : o.__pushKV(hash.ToString(), info);
351 5090 : }
352 1570 : return o;
353 1570 : } else {
354 : uint64_t mempool_sequence;
355 22069 : std::vector<uint256> vtxid;
356 : {
357 22069 : LOCK(pool.cs);
358 22069 : pool.queryHashes(vtxid);
359 22069 : mempool_sequence = pool.GetSequence();
360 22069 : }
361 22069 : UniValue a(UniValue::VARR);
362 304055 : for (const uint256& hash : vtxid)
363 281986 : a.push_back(hash.ToString());
364 :
365 22069 : if (!include_mempool_sequence) {
366 22069 : return a;
367 : } else {
368 0 : UniValue o(UniValue::VOBJ);
369 0 : o.pushKV("txids", a);
370 0 : o.pushKV("mempool_sequence", mempool_sequence);
371 0 : return o;
372 0 : }
373 22069 : }
374 23639 : }
375 :
376 29793 : static RPCHelpMan getrawmempool()
377 : {
378 59586 : return RPCHelpMan{"getrawmempool",
379 29793 : "\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n"
380 : "\nHint: use getmempoolentry to fetch a specific transaction from the mempool.\n",
381 89379 : {
382 29793 : {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"},
383 29793 : {"mempool_sequence", RPCArg::Type::BOOL, RPCArg::Default{false}, "If verbose=false, returns a json object with transaction list and mempool sequence number attached."},
384 : },
385 119172 : {
386 59586 : RPCResult{"for verbose = false",
387 29793 : RPCResult::Type::ARR, "", "",
388 59586 : {
389 29793 : {RPCResult::Type::STR_HEX, "", "The transaction id"},
390 : }},
391 59586 : RPCResult{"for verbose = true",
392 29793 : RPCResult::Type::OBJ_DYN, "", "",
393 59586 : {
394 29793 : {RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()},
395 : }},
396 59586 : RPCResult{"for verbose = false and mempool_sequence = true",
397 29793 : RPCResult::Type::OBJ, "", "",
398 89379 : {
399 59586 : {RPCResult::Type::ARR, "txids", "",
400 59586 : {
401 29793 : {RPCResult::Type::STR_HEX, "", "The transaction id"},
402 : }},
403 29793 : {RPCResult::Type::NUM, "mempool_sequence", "The mempool sequence value."},
404 : }},
405 : },
406 29793 : RPCExamples{
407 29793 : HelpExampleCli("getrawmempool", "true")
408 29793 : + HelpExampleRpc("getrawmempool", "true")
409 : },
410 53430 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
411 : {
412 23637 : bool fVerbose = false;
413 23637 : if (!request.params[0].isNull())
414 1572 : fVerbose = request.params[0].get_bool();
415 :
416 23637 : bool include_mempool_sequence = false;
417 23637 : if (!request.params[1].isNull()) {
418 0 : include_mempool_sequence = request.params[1].get_bool();
419 0 : }
420 :
421 23637 : const NodeContext& node = EnsureAnyNodeContext(request.context);
422 23637 : const CTxMemPool& mempool = EnsureMemPool(node);
423 23637 : const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
424 :
425 23637 : return MempoolToJSON(mempool, llmq_ctx.isman.get(), fVerbose, include_mempool_sequence);
426 : },
427 : };
428 0 : }
429 :
430 6207 : static RPCHelpMan getmempoolancestors()
431 : {
432 12414 : return RPCHelpMan{"getmempoolancestors",
433 6207 : "\nIf txid is in the mempool, returns all in-mempool ancestors.\n",
434 18621 : {
435 6207 : {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
436 6207 : {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"},
437 : },
438 18621 : {
439 12414 : RPCResult{"for verbose = false",
440 6207 : RPCResult::Type::ARR, "", "",
441 6207 : {{RPCResult::Type::STR_HEX, "", "The transaction id of an in-mempool ancestor transaction"}}},
442 12414 : RPCResult{"for verbose = true",
443 6207 : RPCResult::Type::OBJ_DYN, "", "",
444 12414 : {
445 6207 : {RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()},
446 : }},
447 : },
448 6207 : RPCExamples{
449 6207 : HelpExampleCli("getmempoolancestors", "\"mytxid\"")
450 6207 : + HelpExampleRpc("getmempoolancestors", "\"mytxid\"")
451 : },
452 6258 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
453 : {
454 51 : bool fVerbose = false;
455 51 : if (!request.params[1].isNull())
456 26 : fVerbose = request.params[1].get_bool();
457 :
458 51 : uint256 hash(ParseHashV(request.params[0], "parameter 1"));
459 :
460 51 : const NodeContext& node = EnsureAnyNodeContext(request.context);
461 :
462 51 : const CTxMemPool& mempool = EnsureMemPool(node);
463 51 : LOCK(mempool.cs);
464 :
465 51 : CTxMemPool::txiter it = mempool.mapTx.find(hash);
466 51 : if (it == mempool.mapTx.end()) {
467 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
468 : }
469 :
470 51 : CTxMemPool::setEntries setAncestors;
471 51 : uint64_t noLimit = std::numeric_limits<uint64_t>::max();
472 51 : std::string dummy;
473 51 : mempool.CalculateMemPoolAncestors(*it, setAncestors, noLimit, noLimit, noLimit, noLimit, dummy, false);
474 :
475 51 : if (!fVerbose) {
476 25 : UniValue o(UniValue::VARR);
477 325 : for (CTxMemPool::txiter ancestorIt : setAncestors) {
478 300 : o.push_back(ancestorIt->GetTx().GetHash().ToString());
479 : }
480 25 : return o;
481 25 : } else {
482 26 : UniValue o(UniValue::VOBJ);
483 26 : const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
484 350 : for (CTxMemPool::txiter ancestorIt : setAncestors) {
485 324 : const CTxMemPoolEntry &e = *ancestorIt;
486 324 : const uint256& _hash = e.GetTx().GetHash();
487 324 : UniValue info(UniValue::VOBJ);
488 324 : entryToJSON(mempool, info, e, llmq_ctx.isman.get());
489 324 : o.pushKV(_hash.ToString(), info);
490 324 : }
491 26 : return o;
492 26 : }
493 51 : },
494 : };
495 0 : }
496 :
497 6207 : static RPCHelpMan getmempooldescendants()
498 : {
499 12414 : return RPCHelpMan{"getmempooldescendants",
500 6207 : "\nIf txid is in the mempool, returns all in-mempool descendants.\n",
501 18621 : {
502 6207 : {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
503 6207 : {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"},
504 : },
505 18621 : {
506 12414 : RPCResult{"for verbose = false",
507 6207 : RPCResult::Type::ARR, "", "",
508 6207 : {{RPCResult::Type::STR_HEX, "", "The transaction id of an in-mempool descendant transaction"}}},
509 12414 : RPCResult{"for verbose = true",
510 6207 : RPCResult::Type::OBJ_DYN, "", "",
511 12414 : {
512 6207 : {RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()},
513 : }},
514 : },
515 6207 : RPCExamples{
516 6207 : HelpExampleCli("getmempooldescendants", "\"mytxid\"")
517 6207 : + HelpExampleRpc("getmempooldescendants", "\"mytxid\"")
518 : },
519 6258 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
520 : {
521 51 : bool fVerbose = false;
522 51 : if (!request.params[1].isNull())
523 26 : fVerbose = request.params[1].get_bool();
524 :
525 51 : uint256 hash(ParseHashV(request.params[0], "parameter 1"));
526 :
527 51 : const NodeContext& node = EnsureAnyNodeContext(request.context);
528 :
529 51 : const CTxMemPool& mempool = EnsureMemPool(node);
530 51 : LOCK(mempool.cs);
531 :
532 51 : CTxMemPool::txiter it = mempool.mapTx.find(hash);
533 51 : if (it == mempool.mapTx.end()) {
534 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
535 : }
536 :
537 51 : CTxMemPool::setEntries setDescendants;
538 51 : mempool.CalculateDescendants(it, setDescendants);
539 : // CTxMemPool::CalculateDescendants will include the given tx
540 51 : setDescendants.erase(it);
541 :
542 51 : if (!fVerbose) {
543 25 : UniValue o(UniValue::VARR);
544 325 : for (CTxMemPool::txiter descendantIt : setDescendants) {
545 300 : o.push_back(descendantIt->GetTx().GetHash().ToString());
546 : }
547 :
548 25 : return o;
549 25 : } else {
550 26 : UniValue o(UniValue::VOBJ);
551 26 : const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
552 350 : for (CTxMemPool::txiter descendantIt : setDescendants) {
553 324 : const CTxMemPoolEntry &e = *descendantIt;
554 324 : const uint256& _hash = e.GetTx().GetHash();
555 324 : UniValue info(UniValue::VOBJ);
556 324 : entryToJSON(mempool, info, e, llmq_ctx.isman.get());
557 324 : o.pushKV(_hash.ToString(), info);
558 324 : }
559 26 : return o;
560 26 : }
561 51 : },
562 : };
563 0 : }
564 :
565 7116 : static RPCHelpMan getmempoolentry()
566 : {
567 14232 : return RPCHelpMan{"getmempoolentry",
568 7116 : "\nReturns mempool data for given transaction\n",
569 14232 : {
570 7116 : {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
571 : },
572 7116 : RPCResult{
573 7116 : RPCResult::Type::OBJ, "", "", MempoolEntryDescription()},
574 7116 : RPCExamples{
575 7116 : HelpExampleCli("getmempoolentry", "\"mytxid\"")
576 7116 : + HelpExampleRpc("getmempoolentry", "\"mytxid\"")
577 : },
578 8076 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
579 : {
580 :
581 968 : uint256 hash(ParseHashV(request.params[0], "parameter 1"));
582 :
583 960 : const NodeContext& node = EnsureAnyNodeContext(request.context);
584 :
585 960 : const CTxMemPool& mempool = EnsureMemPool(node);
586 960 : LOCK(mempool.cs);
587 :
588 960 : CTxMemPool::txiter it = mempool.mapTx.find(hash);
589 960 : if (it == mempool.mapTx.end()) {
590 8 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
591 : }
592 :
593 952 : const CTxMemPoolEntry &e = *it;
594 952 : UniValue info(UniValue::VOBJ);
595 952 : const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
596 952 : entryToJSON(mempool, info, e, llmq_ctx.isman.get());
597 952 : return info;
598 968 : },
599 : };
600 0 : }
601 :
602 6203 : static RPCHelpMan gettxspendingprevout()
603 : {
604 12406 : return RPCHelpMan{"gettxspendingprevout",
605 6203 : "Scans the mempool to find transactions spending any of the given outputs",
606 12406 : {
607 12406 : {"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The transaction outputs that we want to check, and within each, the txid (string) vout (numeric).",
608 12406 : {
609 12406 : {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
610 18609 : {
611 6203 : {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
612 6203 : {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
613 : },
614 : },
615 : },
616 : },
617 : },
618 6203 : RPCResult{
619 6203 : RPCResult::Type::ARR, "", "",
620 12406 : {
621 12406 : {RPCResult::Type::OBJ, "", "",
622 24812 : {
623 6203 : {RPCResult::Type::STR_HEX, "txid", "the transaction id of the checked output"},
624 6203 : {RPCResult::Type::NUM, "vout", "the vout value of the checked output"},
625 6203 : {RPCResult::Type::STR_HEX, "spendingtxid", /*optional=*/true, "the transaction id of the mempool transaction spending this output (omitted if unspent)"},
626 : }},
627 : }
628 : },
629 6203 : RPCExamples{
630 6203 : HelpExampleCli("gettxspendingprevout", "\"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":3}]\"")
631 6203 : + HelpExampleRpc("gettxspendingprevout", "\"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":3}]\"")
632 : },
633 6250 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
634 : {
635 47 : const UniValue& output_params = request.params[0].get_array();
636 47 : if (output_params.empty()) {
637 12 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, outputs are missing");
638 : }
639 :
640 45 : std::vector<COutPoint> prevouts;
641 45 : prevouts.reserve(output_params.size());
642 :
643 86 : for (unsigned int idx = 0; idx < output_params.size(); idx++) {
644 51 : const UniValue& o = output_params[idx].get_obj();
645 :
646 102 : RPCTypeCheckObj(o,
647 153 : {
648 51 : {"txid", UniValueType(UniValue::VSTR)},
649 51 : {"vout", UniValueType(UniValue::VNUM)},
650 : }, /*fAllowNull=*/false, /*fStrict=*/true);
651 :
652 43 : const uint256 txid(ParseHashO(o, "txid"));
653 43 : const int nOutput{o.find_value("vout").getInt<int>()};
654 43 : if (nOutput < 0) {
655 2 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
656 : }
657 :
658 41 : prevouts.emplace_back(txid, nOutput);
659 41 : }
660 :
661 35 : const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
662 35 : LOCK(mempool.cs);
663 :
664 35 : UniValue result{UniValue::VARR};
665 :
666 76 : for (const COutPoint& prevout : prevouts) {
667 41 : UniValue o(UniValue::VOBJ);
668 41 : o.pushKV("txid", prevout.hash.ToString());
669 41 : o.pushKV("vout", (uint64_t)prevout.n);
670 :
671 41 : const CTransaction* spendingTx = mempool.GetConflictTx(prevout);
672 41 : if (spendingTx != nullptr) {
673 35 : o.pushKV("spendingtxid", spendingTx->GetHash().ToString());
674 35 : }
675 :
676 41 : result.push_back(o);
677 41 : }
678 :
679 35 : return result;
680 57 : },
681 : };
682 0 : }
683 :
684 4238 : UniValue MempoolInfoToJSON(const CTxMemPool& pool, const llmq::CInstantSendManager& isman)
685 : {
686 : // Make sure this call is atomic in the pool.
687 4238 : LOCK(pool.cs);
688 4238 : UniValue ret(UniValue::VOBJ);
689 4238 : ret.pushKV("loaded", pool.IsLoaded());
690 4238 : ret.pushKV("size", (int64_t)pool.size());
691 4238 : ret.pushKV("bytes", (int64_t)pool.GetTotalTxSize());
692 4238 : ret.pushKV("usage", (int64_t)pool.DynamicMemoryUsage());
693 4238 : ret.pushKV("total_fee", ValueFromAmount(pool.GetTotalFee()));
694 4238 : int64_t maxmempool{gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000};
695 4238 : ret.pushKV("maxmempool", maxmempool);
696 4238 : ret.pushKV("mempoolminfee", ValueFromAmount(std::max(pool.GetMinFee(maxmempool), ::minRelayTxFee).GetFeePerK()));
697 4238 : ret.pushKV("minrelaytxfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()));
698 4238 : ret.pushKV("instantsendlocks", isman.GetInstantSendLockCount());
699 4238 : ret.pushKV("unbroadcastcount", pool.GetUnbroadcastTxs().size());
700 4238 : return ret;
701 4238 : }
702 :
703 10392 : static RPCHelpMan getmempoolinfo()
704 : {
705 20784 : return RPCHelpMan{"getmempoolinfo",
706 10392 : "\nReturns details on the active state of the TX memory pool.\n",
707 10392 : {},
708 10392 : RPCResult{
709 10392 : RPCResult::Type::OBJ, "", "",
710 114312 : {
711 10392 : {RPCResult::Type::BOOL, "loaded", "True if the mempool is fully loaded"},
712 10392 : {RPCResult::Type::NUM, "size", "Current tx count"},
713 10392 : {RPCResult::Type::NUM, "bytes", "Sum of all transaction sizes"},
714 10392 : {RPCResult::Type::NUM, "usage", "Total memory usage for the mempool"},
715 10392 : {RPCResult::Type::STR_AMOUNT, "total_fee", "Total fees for the mempool in " + CURRENCY_UNIT + ", ignoring modified fees through prioritisetransaction"},
716 10392 : {RPCResult::Type::NUM, "maxmempool", "Maximum memory usage for the mempool"},
717 10392 : {RPCResult::Type::STR_AMOUNT, "mempoolminfee", "Minimum fee rate in " + CURRENCY_UNIT + "/kB for tx to be accepted. Is the maximum of minrelaytxfee and minimum mempool fee"},
718 10392 : {RPCResult::Type::STR_AMOUNT, "minrelaytxfee", "Current minimum relay fee for transactions"},
719 10392 : {RPCResult::Type::NUM, "instantsendlocks", "Number of unconfirmed InstantSend locks"},
720 10392 : {RPCResult::Type::NUM, "unbroadcastcount", "Current number of transactions that haven't passed initial broadcast yet"}
721 : }},
722 10392 : RPCExamples{
723 10392 : HelpExampleCli("getmempoolinfo", "")
724 10392 : + HelpExampleRpc("getmempoolinfo", "")
725 : },
726 14628 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
727 : {
728 4236 : const NodeContext& node = EnsureAnyNodeContext(request.context);
729 4236 : const CTxMemPool& mempool = EnsureMemPool(node);
730 4236 : const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
731 4236 : return MempoolInfoToJSON(mempool, *llmq_ctx.isman);
732 : },
733 : };
734 0 : }
735 :
736 6160 : static RPCHelpMan savemempool()
737 : {
738 12320 : return RPCHelpMan{"savemempool",
739 6160 : "\nDumps the mempool to disk. It will fail until the previous dump is fully loaded.\n",
740 6160 : {},
741 6160 : RPCResult{
742 6160 : RPCResult::Type::OBJ, "", "",
743 12320 : {
744 6160 : {RPCResult::Type::STR, "filename", "the directory and file where the mempool was saved"},
745 : }},
746 6160 : RPCExamples{
747 6160 : HelpExampleCli("savemempool", "")
748 6160 : + HelpExampleRpc("savemempool", "")
749 : },
750 6164 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
751 : {
752 4 : const ArgsManager& args{EnsureAnyArgsman(request.context)};
753 4 : const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
754 :
755 4 : if (!mempool.IsLoaded()) {
756 2 : throw JSONRPCError(RPC_MISC_ERROR, "The mempool was not loaded yet");
757 : }
758 :
759 4 : if (!DumpMempool(mempool)) {
760 2 : throw JSONRPCError(RPC_MISC_ERROR, "Unable to dump mempool to disk");
761 : }
762 :
763 2 : UniValue ret(UniValue::VOBJ);
764 2 : ret.pushKV("filename", fs::path((args.GetDataDirNet() / "mempool.dat")).utf8string());
765 :
766 2 : return ret;
767 4 : },
768 : };
769 0 : }
770 :
771 6160 : static RPCHelpMan submitpackage()
772 : {
773 12320 : return RPCHelpMan{"submitpackage",
774 6160 : "Submit a package of raw transactions (serialized, hex-encoded) to local node (-regtest only).\n"
775 : "The package will be validated according to consensus and mempool policy rules. If all transactions pass, they will be accepted to mempool.\n"
776 : "This RPC is experimental and the interface may be unstable. Refer to doc/policy/packages.md for documentation on package policies.\n"
777 : "Warning: until package relay is in use, successful submission does not mean the transaction will propagate to other nodes on the network.\n"
778 : "Currently, each transaction is broadcasted individually after submission, which means they must meet other nodes' feerate requirements alone.\n"
779 : ,
780 12320 : {
781 12320 : {"package", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of raw transactions.",
782 12320 : {
783 6160 : {"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
784 : },
785 : },
786 : },
787 6160 : RPCResult{
788 6160 : RPCResult::Type::OBJ, "", "",
789 18480 : {
790 12320 : {RPCResult::Type::OBJ_DYN, "tx-results", "transaction results keyed by txid",
791 12320 : {
792 24640 : {RPCResult::Type::OBJ, "txid", "transaction txid", {
793 6160 : {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
794 6160 : {RPCResult::Type::NUM, "size", "Size of transaction in bytes"},
795 12320 : {RPCResult::Type::OBJ, "fees", "Transaction fees", {
796 6160 : {RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT},
797 : }},
798 : }}
799 : }},
800 6160 : {RPCResult::Type::STR_AMOUNT, "package-feerate", /*optional=*/true, "package feerate used for feerate checks in " + CURRENCY_UNIT + " per KvB. Excludes transactions which were deduplicated or accepted individually."},
801 : },
802 : },
803 6160 : RPCExamples{
804 12320 : HelpExampleCli("testmempoolaccept", "[rawtx1, rawtx2]") +
805 6160 : HelpExampleCli("submitpackage", "[rawtx1, rawtx2]")
806 : },
807 6180 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
808 : {
809 20 : if (!Params().IsMockableChain()) {
810 4 : throw std::runtime_error("submitpackage is for regression testing (-regtest mode) only");
811 : }
812 40 : RPCTypeCheck(request.params, {
813 20 : UniValue::VARR,
814 : });
815 20 : const UniValue raw_transactions = request.params[0].get_array();
816 20 : if (raw_transactions.size() < 1 || raw_transactions.size() > MAX_PACKAGE_COUNT) {
817 0 : throw JSONRPCError(RPC_INVALID_PARAMETER,
818 0 : "Array must contain between 1 and " + ToString(MAX_PACKAGE_COUNT) + " transactions.");
819 : }
820 :
821 20 : std::vector<CTransactionRef> txns;
822 20 : txns.reserve(raw_transactions.size());
823 206 : for (const auto& rawtx : raw_transactions.getValues()) {
824 186 : CMutableTransaction mtx;
825 186 : if (!DecodeHexTx(mtx, rawtx.get_str())) {
826 0 : throw JSONRPCError(RPC_DESERIALIZATION_ERROR,
827 0 : "TX decode failed: " + rawtx.get_str() + " Make sure the tx has at least one input.");
828 : }
829 186 : txns.emplace_back(MakeTransactionRef(std::move(mtx)));
830 186 : }
831 :
832 20 : NodeContext& node = EnsureAnyNodeContext(request.context);
833 20 : CTxMemPool& mempool = EnsureMemPool(node);
834 20 : CChainState& chainstate = EnsureChainman(node).ActiveChainstate();
835 40 : const auto package_result = WITH_LOCK(::cs_main, return ProcessNewPackage(chainstate, mempool, txns, /*test_accept=*/ false));
836 :
837 : // First catch any errors.
838 20 : switch(package_result.m_state.GetResult()) {
839 16 : case PackageValidationResult::PCKG_RESULT_UNSET: break;
840 : case PackageValidationResult::PCKG_POLICY:
841 : {
842 2 : throw JSONRPCTransactionError(TransactionError::INVALID_PACKAGE,
843 2 : package_result.m_state.GetRejectReason());
844 : }
845 : case PackageValidationResult::PCKG_MEMPOOL_ERROR:
846 : {
847 0 : throw JSONRPCTransactionError(TransactionError::MEMPOOL_ERROR,
848 0 : package_result.m_state.GetRejectReason());
849 : }
850 : case PackageValidationResult::PCKG_TX:
851 : {
852 2 : for (const auto& tx : txns) {
853 2 : auto it = package_result.m_tx_results.find(tx->GetHash());
854 4 : if (it != package_result.m_tx_results.end() && it->second.m_state.IsInvalid()) {
855 4 : throw JSONRPCTransactionError(TransactionError::MEMPOOL_REJECTED,
856 2 : strprintf("%s failed: %s", tx->GetHash().ToString(), it->second.m_state.GetRejectReason()));
857 : }
858 : }
859 : // If a PCKG_TX error was returned, there must have been an invalid transaction.
860 0 : NONFATAL_UNREACHABLE();
861 : }
862 : }
863 148 : for (const auto& tx : txns) {
864 132 : size_t num_submitted{0};
865 132 : bilingual_str err_string;
866 132 : const auto err = BroadcastTransaction(node, tx, err_string, 0, true, true);
867 132 : if (err != TransactionError::OK) {
868 0 : throw JSONRPCTransactionError(err,
869 0 : strprintf("transaction broadcast failed: %s (all transactions were submitted, %d transactions were broadcast successfully)",
870 0 : err_string.original, num_submitted));
871 : }
872 132 : }
873 16 : UniValue rpc_result{UniValue::VOBJ};
874 16 : UniValue tx_result_map{UniValue::VOBJ};
875 148 : for (const auto& tx : txns) {
876 132 : auto it = package_result.m_tx_results.find(tx->GetHash());
877 132 : CHECK_NONFATAL(it != package_result.m_tx_results.end());
878 132 : UniValue result_inner{UniValue::VOBJ};
879 132 : result_inner.pushKV("txid", tx->GetHash().GetHex());
880 159 : if (it->second.m_result_type == MempoolAcceptResult::ResultType::VALID ||
881 27 : it->second.m_result_type == MempoolAcceptResult::ResultType::MEMPOOL_ENTRY) {
882 132 : result_inner.pushKV("size", int64_t{it->second.m_vsize.value()});
883 132 : UniValue fees(UniValue::VOBJ);
884 132 : fees.pushKV("base", ValueFromAmount(it->second.m_base_fees.value()));
885 132 : result_inner.pushKV("fees", fees);
886 132 : }
887 132 : tx_result_map.pushKV(tx->GetHash().GetHex(), result_inner);
888 132 : }
889 16 : rpc_result.pushKV("tx-results", tx_result_map);
890 16 : if (package_result.m_package_feerate.has_value()) {
891 2 : rpc_result.pushKV("package-feerate", ValueFromAmount(package_result.m_package_feerate.value().GetFeePerK()));
892 2 : }
893 16 : return rpc_result;
894 24 : },
895 : };
896 0 : }
897 :
898 3201 : void RegisterMempoolRPCCommands(CRPCTable& t)
899 : {
900 33891 : static const CRPCCommand commands[]{
901 3069 : {"rawtransactions", &sendrawtransaction},
902 3069 : {"rawtransactions", &testmempoolaccept},
903 3069 : {"blockchain", &getmempoolancestors},
904 3069 : {"blockchain", &getmempooldescendants},
905 3069 : {"blockchain", &getmempoolentry},
906 3069 : {"blockchain", &gettxspendingprevout},
907 3069 : {"blockchain", &getmempoolinfo},
908 3069 : {"blockchain", &getrawmempool},
909 3069 : {"blockchain", &savemempool},
910 3069 : {"hidden", &submitpackage},
911 : };
912 35211 : for (const auto& c : commands) {
913 32010 : t.appendCommand(c.name, &c);
914 : }
915 3201 : }
|