Branch data Line data Source code
1 : : // Copyright (c) 2010 Satoshi Nakamoto
2 : : // Copyright (c) 2009-present 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/mempool.h>
7 : : #include <rpc/register.h> // IWYU pragma: associated
8 : :
9 : : #include <common/args.h>
10 : : #include <consensus/amount.h>
11 : : #include <consensus/validation.h>
12 : : #include <core_io.h>
13 : : #include <index/txospenderindex.h>
14 : : #include <net.h>
15 : : #include <net_processing.h>
16 : : #include <netaddress.h>
17 : : #include <netbase.h>
18 : : #include <node/mempool_persist.h>
19 : : #include <node/mempool_persist_args.h>
20 : : #include <node/transaction.h>
21 : : #include <node/txorphanage.h>
22 : : #include <node/types.h>
23 : : #include <policy/feerate.h>
24 : : #include <policy/packages.h>
25 : : #include <policy/policy.h>
26 : : #include <policy/rbf.h>
27 : : #include <primitives/transaction.h>
28 : : #include <rpc/protocol.h>
29 : : #include <rpc/request.h>
30 : : #include <rpc/server.h>
31 : : #include <rpc/server_util.h>
32 : : #include <rpc/util.h>
33 : : #include <script/script.h>
34 : : #include <sync.h>
35 : : #include <tinyformat.h>
36 : : #include <txgraph.h>
37 : : #include <txmempool.h>
38 : : #include <uint256.h>
39 : : #include <univalue.h>
40 : : #include <util/check.h>
41 : : #include <util/expected.h>
42 : : #include <util/feefrac.h>
43 : : #include <util/fs.h>
44 : : #include <util/moneystr.h>
45 : : #include <util/string.h>
46 : : #include <util/time.h>
47 : : #include <util/vector.h>
48 : : #include <validation.h>
49 : :
50 : : #include <algorithm>
51 : : #include <cstddef>
52 : : #include <cstdint>
53 : : #include <functional>
54 : : #include <list>
55 : : #include <map>
56 : : #include <memory>
57 : : #include <optional>
58 : : #include <ranges>
59 : : #include <set>
60 : : #include <string>
61 : : #include <string_view>
62 : : #include <tuple>
63 : : #include <utility>
64 : : #include <vector>
65 : :
66 : : namespace node {
67 : : struct NodeContext;
68 : : } // namespace node
69 : :
70 : : using node::DumpMempool;
71 : :
72 : : using node::DEFAULT_MAX_BURN_AMOUNT;
73 : : using node::DEFAULT_MAX_RAW_TX_FEE_RATE;
74 : : using node::MempoolPath;
75 : : using node::NodeContext;
76 : : using node::TransactionError;
77 : : using util::ToString;
78 : :
79 : 237 : static RPCMethod sendrawtransaction()
80 : : {
81 : 237 : return RPCMethod{
82 : 237 : "sendrawtransaction",
83 [ + - ]: 474 : "Submit a raw transaction (serialized, hex-encoded) to the network.\n"
84 : :
85 : : "\nIf -privatebroadcast is disabled, then the transaction will be put into the\n"
86 : : "local mempool of the node and will be sent unconditionally to all currently\n"
87 : : "connected peers, so using sendrawtransaction for manual rebroadcast will degrade\n"
88 : : "privacy by leaking the transaction's origin, as nodes will normally not\n"
89 : : "rebroadcast non-wallet transactions already in their mempool.\n"
90 : :
91 : : "\nIf -privatebroadcast is enabled, then the transaction will be sent via\n"
92 : : "dedicated, short-lived connections to Tor or I2P peers, or to IPv4/IPv6 peers\n"
93 : : "via the Tor network. This provides best-effort concealment of the transaction's origin.\n"
94 : : "Private broadcast is experimental and may change in future releases.\n"
95 : : "Submission does not itself add the transaction to the local mempool; normal\n"
96 : : "mempool acceptance and relay apply when it is received back from the network.\n"
97 : : "The private broadcast queue is bounded: when it is full, this RPC fails and\n"
98 : : "the transaction is not scheduled until an existing one completes or is\n"
99 : : "aborted. Use getprivatebroadcastinfo to inspect the queue and abortprivatebroadcast to abort.\n"
100 : :
101 : : "\nA specific exception, RPC_TRANSACTION_ALREADY_IN_UTXO_SET, may throw if the transaction cannot be added to the mempool.\n"
102 : :
103 : : "\nRelated RPCs: createrawtransaction, signrawtransactionwithkey\n",
104 : : {
105 [ + - + - ]: 474 : {"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
106 [ + - + - : 711 : {"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},
+ - ]
107 [ + - ]: 474 : "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
108 : 237 : "/kvB.\nFee rates larger than 1BTC/kvB are rejected.\nSet to 0 to accept any fee rate."},
109 [ + - + - : 474 : {"maxburnamount", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_BURN_AMOUNT)},
+ - ]
110 [ + - ]: 474 : "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"
111 : : "If burning funds through unspendable outputs is desired, increase this value.\n"
112 : 237 : "This check is based on heuristics and does not guarantee spendability of outputs.\n"},
113 : : },
114 [ + - ]: 474 : RPCResult{
115 [ + - + - ]: 474 : RPCResult::Type::STR_HEX, "", "The transaction hash in hex"
116 [ + - ]: 474 : },
117 : 237 : RPCExamples{
118 : : "\nCreate a transaction\n"
119 [ + - + - : 474 : + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") +
+ - + - ]
120 : 237 : "Sign the transaction, and get back the hex\n"
121 [ + - + - : 948 : + HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") +
+ - + - ]
122 : 237 : "\nSend the transaction (signed hex)\n"
123 [ + - + - : 948 : + HelpExampleCli("sendrawtransaction", "\"signedhex\"") +
+ - + - ]
124 : 237 : "\nAs a JSON-RPC call\n"
125 [ + - + - : 948 : + HelpExampleRpc("sendrawtransaction", "\"signedhex\"")
+ - + - ]
126 [ + - ]: 237 : },
127 : 237 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
128 : : {
129 [ + + ]: 97 : const CAmount max_burn_amount = request.params[2].isNull() ? 0 : AmountFromValue(request.params[2]);
130 : :
131 : 94 : CMutableTransaction mtx;
132 [ + - + - : 94 : if (!DecodeHexTx(mtx, request.params[0].get_str())) {
+ - + + ]
133 [ + - + - ]: 80 : throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed. Make sure the tx has at least one input.");
134 : : }
135 : :
136 [ + + ]: 1478 : for (const auto& out : mtx.vout) {
137 [ + + + - : 1428 : if((out.scriptPubKey.IsUnspendable() || !out.scriptPubKey.HasValidOps()) && out.nValue > max_burn_amount) {
+ + + + ]
138 [ + - + - ]: 8 : throw JSONRPCTransactionError(TransactionError::MAX_BURN_EXCEEDED);
139 : : }
140 : : }
141 : :
142 [ + - ]: 50 : CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
143 : :
144 [ + - + + ]: 50 : const CFeeRate max_raw_tx_fee_rate{ParseFeeRate(self.Arg<UniValue>("maxfeerate"))};
145 : :
146 : :
147 [ + - ]: 49 : std::string err_string;
148 : 49 : AssertLockNotHeld(cs_main);
149 [ + - ]: 49 : NodeContext& node = EnsureAnyNodeContext(request.context);
150 [ + - + - ]: 49 : const bool private_broadcast_enabled{gArgs.GetBoolArg("-privatebroadcast", DEFAULT_PRIVATE_BROADCAST)};
151 [ # # ]: 0 : if (private_broadcast_enabled &&
152 [ - + - - : 49 : !g_reachable_nets.Contains(NET_ONION) &&
- - ]
153 [ # # ]: 0 : !g_reachable_nets.Contains(NET_I2P)) {
154 : 0 : throw JSONRPCError(RPC_MISC_ERROR,
155 [ # # ]: 0 : "-privatebroadcast is enabled, but none of the Tor or I2P networks is "
156 : : "reachable. Maybe the location of the Tor proxy couldn't be retrieved "
157 : : "from the Tor daemon at startup. Check whether the Tor daemon is running "
158 [ # # ]: 0 : "and that -torcontrol, -torpassword and -i2psam are configured properly.");
159 : : }
160 [ + - ]: 49 : const auto method = private_broadcast_enabled ? node::TxBroadcast::NO_MEMPOOL_PRIVATE_BROADCAST
161 : : : node::TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL;
162 [ + - + - ]: 49 : const TransactionError err = BroadcastTransaction(node,
163 : : tx,
164 : : err_string,
165 [ + - ]: 49 : /*max_tx_fee=*/0,
166 : : max_raw_tx_fee_rate,
167 : : method,
168 : : /*wait_callback=*/true);
169 [ + - ]: 49 : if (TransactionError::OK != err) {
170 [ + - ]: 49 : throw JSONRPCTransactionError(err, err_string);
171 : : }
172 : :
173 [ # # # # ]: 0 : return tx->GetHash().GetHex();
174 [ - - ]: 99 : },
175 [ + - + - : 1659 : };
+ + - - ]
176 [ + - + - : 1422 : }
+ - - - ]
177 : :
178 : 70 : static RPCMethod getprivatebroadcastinfo()
179 : : {
180 : 70 : return RPCMethod{
181 : 70 : "getprivatebroadcastinfo",
182 [ + - ]: 140 : "Returns information about transactions tracked for private broadcast.\n"
183 : : "Transactions that have reached the send-attempt limit remain in the result with attempts_remaining=0.\n"
184 : : "This method is only available when running with -privatebroadcast enabled.\n",
185 : : {},
186 [ + - ]: 140 : RPCResult{
187 [ + - ]: 140 : RPCResult::Type::OBJ, "", "",
188 : : {
189 [ + - + - ]: 70 : {RPCResult::Type::ARR, "transactions", "",
190 : : {
191 [ + - + - ]: 140 : {RPCResult::Type::OBJ, "", "",
192 : : {
193 [ + - + - ]: 140 : {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
194 [ + - + - ]: 140 : {RPCResult::Type::STR_HEX, "wtxid", "The transaction witness hash in hex"},
195 [ + - + - ]: 140 : {RPCResult::Type::STR_HEX, "hex", "The serialized, hex-encoded transaction data"},
196 [ + - + - ]: 140 : {RPCResult::Type::NUM_TIME, "time_added", "The time this transaction was added to the private broadcast queue (seconds since epoch)"},
197 [ + - + - ]: 140 : {RPCResult::Type::NUM, "attempts_remaining", "The number of additional private broadcast send attempts allowed for this transaction"},
198 [ + - + - ]: 140 : {RPCResult::Type::ARR, "peers", "Per-peer send and acknowledgment information for this transaction",
199 : : {
200 [ + - + - ]: 140 : {RPCResult::Type::OBJ, "", "",
201 : : {
202 [ + - + - ]: 140 : {RPCResult::Type::STR, "address", "The address of the peer to which the transaction was sent"},
203 [ + - + - ]: 140 : {RPCResult::Type::NUM_TIME, "sent", "The time this transaction was picked for sending to this peer via private broadcast (seconds since epoch)"},
204 [ + - + - ]: 140 : {RPCResult::Type::NUM_TIME, "received", /*optional=*/true, "The time this peer acknowledged reception of the transaction (seconds since epoch)"},
205 : : }},
206 : : }},
207 : : }},
208 : : }},
209 : 2100 : }},
[ + - + -
+ - + - +
- + - + +
+ + + + +
+ + + - -
- - - - -
- - - ]
210 : 70 : RPCExamples{
211 [ + - + - : 140 : HelpExampleCli("getprivatebroadcastinfo", "")
+ - ]
212 [ + - + - : 280 : + HelpExampleRpc("getprivatebroadcastinfo", "")
+ - + - ]
213 [ + - ]: 70 : },
214 : 70 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
215 : : {
216 : 2 : const NodeContext& node{EnsureAnyNodeContext(request.context)};
217 : 2 : const PeerManager& peerman{EnsurePeerman(node)};
218 [ + - ]: 2 : if (!peerman.GetInfo().private_broadcast) {
219 [ + - + - ]: 4 : throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Private broadcast is not enabled. Ensure you're running Bitcoin Core with -privatebroadcast=1.");
220 : : }
221 : :
222 : 0 : const auto txs{peerman.GetPrivateBroadcastInfo()};
223 : :
224 : 0 : UniValue transactions(UniValue::VARR);
225 [ # # ]: 0 : for (const auto& tx_info : txs) {
226 : 0 : UniValue o(UniValue::VOBJ);
227 [ # # # # : 0 : o.pushKV("txid", tx_info.tx->GetHash().ToString());
# # # # ]
228 [ # # # # : 0 : o.pushKV("wtxid", tx_info.tx->GetWitnessHash().ToString());
# # # # ]
229 [ # # # # : 0 : o.pushKV("hex", EncodeHexTx(*tx_info.tx));
# # # # ]
230 [ # # # # : 0 : o.pushKV("time_added", TicksSinceEpoch<std::chrono::seconds>(tx_info.time_added));
# # ]
231 [ # # # # : 0 : o.pushKV("attempts_remaining", tx_info.attempts_remaining);
# # ]
232 : 0 : UniValue peers(UniValue::VARR);
233 [ # # ]: 0 : for (const auto& peer : tx_info.peers) {
234 : 0 : UniValue p(UniValue::VOBJ);
235 [ # # # # : 0 : p.pushKV("address", peer.address.ToStringAddrPort());
# # # # ]
236 [ # # # # : 0 : p.pushKV("sent", TicksSinceEpoch<std::chrono::seconds>(peer.sent));
# # ]
237 [ # # ]: 0 : if (peer.received.has_value()) {
238 [ # # # # : 0 : p.pushKV("received", TicksSinceEpoch<std::chrono::seconds>(*peer.received));
# # ]
239 : : }
240 [ # # ]: 0 : peers.push_back(std::move(p));
241 : 0 : }
242 [ # # # # ]: 0 : o.pushKV("peers", std::move(peers));
243 [ # # ]: 0 : transactions.push_back(std::move(o));
244 : 0 : }
245 : :
246 : 0 : UniValue ret(UniValue::VOBJ);
247 [ # # # # ]: 0 : ret.pushKV("transactions", std::move(transactions));
248 : 0 : return ret;
249 : 0 : },
250 [ + - + - ]: 280 : };
251 : 1680 : }
[ + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
- - - - ]
252 : :
253 : 72 : static RPCMethod abortprivatebroadcast()
254 : : {
255 : 72 : return RPCMethod{
256 : 72 : "abortprivatebroadcast",
257 [ + - ]: 144 : "Abort private broadcast attempts for a transaction currently being privately broadcast.\n"
258 : : "The transaction will be removed from the private broadcast queue.\n"
259 : : "This method is only available when running with -privatebroadcast enabled.\n",
260 : : {
261 [ + - + - ]: 144 : {"id", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A transaction identifier to abort. It will be matched against both txid and wtxid for all transactions in the private broadcast queue.\n"
262 : : "If the provided id matches a txid that corresponds to multiple transactions with different wtxids, multiple transactions will be removed and returned."},
263 : : },
264 [ + - ]: 144 : RPCResult{
265 [ + - + - ]: 144 : RPCResult::Type::OBJ, "", "",
266 : : {
267 [ + - + - ]: 144 : {RPCResult::Type::ARR, "removed_transactions", "Transactions removed from the private broadcast queue",
268 : : {
269 [ + - + - ]: 144 : {RPCResult::Type::OBJ, "", "",
270 : : {
271 [ + - + - ]: 144 : {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
272 [ + - + - ]: 144 : {RPCResult::Type::STR_HEX, "wtxid", "The transaction witness hash in hex"},
273 [ + - + - ]: 144 : {RPCResult::Type::STR_HEX, "hex", "The serialized, hex-encoded transaction data"},
274 : : }},
275 : : }},
276 : : }
277 : 1008 : },
[ + - + -
+ - + - +
+ + + + +
- - - - -
- ]
278 : 72 : RPCExamples{
279 [ + - + - : 144 : HelpExampleCli("abortprivatebroadcast", "\"id\"")
+ - ]
280 [ + - + - : 288 : + HelpExampleRpc("abortprivatebroadcast", "\"id\"")
+ - + - ]
281 [ + - ]: 72 : },
282 : 72 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
283 : : {
284 : :
285 : 1 : const NodeContext& node{EnsureAnyNodeContext(request.context)};
286 : 1 : PeerManager& peerman{EnsurePeerman(node)};
287 [ + - ]: 1 : if (!peerman.GetInfo().private_broadcast) {
288 [ + - + - ]: 2 : throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Private broadcast is not enabled. Ensure you're running Bitcoin Core with -privatebroadcast=1.");
289 : : }
290 : :
291 [ # # ]: 0 : const uint256 id{ParseHashV(self.Arg<UniValue>("id"), "id")};
292 : :
293 : 0 : const auto removed_txs{peerman.AbortPrivateBroadcast(id)};
294 [ # # ]: 0 : if (removed_txs.empty()) {
295 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in private broadcast queue. Check getprivatebroadcastinfo.");
296 : : }
297 : :
298 : 0 : UniValue removed_transactions(UniValue::VARR);
299 [ # # ]: 0 : for (const auto& tx : removed_txs) {
300 : 0 : UniValue o(UniValue::VOBJ);
301 [ # # # # : 0 : o.pushKV("txid", tx->GetHash().ToString());
# # # # ]
302 [ # # # # : 0 : o.pushKV("wtxid", tx->GetWitnessHash().ToString());
# # # # ]
303 [ # # # # : 0 : o.pushKV("hex", EncodeHexTx(*tx));
# # # # ]
304 [ # # ]: 0 : removed_transactions.push_back(std::move(o));
305 : 0 : }
306 : 0 : UniValue ret(UniValue::VOBJ);
307 [ # # # # ]: 0 : ret.pushKV("removed_transactions", std::move(removed_transactions));
308 : 0 : return ret;
309 : 0 : },
310 [ + - + - : 360 : };
+ + - - ]
311 : 864 : }
[ + - + -
+ - + - +
- + - -
- ]
312 : :
313 : 339 : static RPCMethod testmempoolaccept()
314 : : {
315 : 339 : return RPCMethod{
316 : 339 : "testmempoolaccept",
317 : : "Returns result of mempool acceptance tests indicating if raw transaction(s) (serialized, hex-encoded) would be accepted by mempool.\n"
318 : : "\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"
319 : : "\nIf one transaction fails, other transactions may not be fully validated (the 'allowed' key will be blank).\n"
320 [ + - + - ]: 678 : "\nThe maximum number of transactions allowed is " + ToString(MAX_PACKAGE_COUNT) + ".\n"
321 : : "\nThis checks if transactions violate the consensus or policy rules.\n"
322 : 339 : "\nSee sendrawtransaction call.\n",
323 : : {
324 [ + - + - ]: 678 : {"rawtxs", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of hex strings of raw transactions.",
325 : : {
326 [ + - + - ]: 678 : {"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
327 : : },
328 : : },
329 [ + - + - : 1017 : {"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},
+ - ]
330 [ + - ]: 678 : "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
331 : 339 : "/kvB.\nFee rates larger than 1BTC/kvB are rejected.\nSet to 0 to accept any fee rate."},
332 : : },
333 [ + - ]: 678 : RPCResult{
334 [ + - + - ]: 678 : RPCResult::Type::ARR, "", "The result of the mempool acceptance test for each raw transaction in the input array.\n"
335 : : "Returns results for each transaction in the same order they were passed in.\n"
336 : : "Transactions that cannot be fully validated due to failures in other transactions will not contain an 'allowed' result.\n",
337 : : {
338 [ + - + - ]: 678 : {RPCResult::Type::OBJ, "", "",
339 : : {
340 [ + - + - ]: 678 : {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
341 [ + - + - ]: 678 : {RPCResult::Type::STR_HEX, "wtxid", "The transaction witness hash in hex"},
342 [ + - + - ]: 678 : {RPCResult::Type::STR, "package-error", /*optional=*/true, "Package validation error, if any (only possible if rawtxs had more than 1 transaction)."},
343 [ + - + - ]: 678 : {RPCResult::Type::BOOL, "allowed", /*optional=*/true, "Whether this tx would be accepted to the mempool and pass client-specified maxfeerate. "
344 : : "If not present, the tx was not fully validated due to a failure in another tx in the list."},
345 [ + - + - ]: 678 : {RPCResult::Type::NUM, "vsize_adjusted", /*optional=*/true, "Maximum of sigop-adjusted size (-bytespersigop) and virtual transaction size as defined in BIP 141 (only present when 'allowed' is true)."},
346 [ + - + - ]: 678 : {RPCResult::Type::NUM, "vsize", /*optional=*/true, "(DEPRECATED) Was previously erroneously described as the BIP 141 vsize, but is actually sigops-adjusted vsize.\n"
347 : : "Use vsize_bip141 to actually get that behavior or switch to the explicit vsize_adjusted for retained behavior."},
348 [ + - + - ]: 678 : {RPCResult::Type::NUM, "vsize_bip141", /*optional=*/true, "Virtual transaction size as defined in BIP 141.\n"
349 : : "This is different from actual serialized size for witness transactions as witness data is discounted (only present when 'allowed' is true)."},
350 [ + - + - ]: 678 : {RPCResult::Type::OBJ, "fees", /*optional=*/true, "Transaction fees (only present if 'allowed' is true)",
351 : : {
352 [ + - + - ]: 678 : {RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT},
353 [ + - + - ]: 678 : {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."},
354 [ + - + - ]: 678 : {RPCResult::Type::ARR, "effective-includes", /*optional=*/false, "transactions whose fees and vsizes are included in effective-feerate.",
355 [ + - + - ]: 678 : {RPCResult{RPCResult::Type::STR_HEX, "", "transaction wtxid in hex"},
356 : : }},
357 : : }},
358 [ + - + - ]: 678 : {RPCResult::Type::STR, "reject-reason", /*optional=*/true, "Rejection reason (only present when 'allowed' is false)"},
359 [ + - + - ]: 678 : {RPCResult::Type::STR, "reject-details", /*optional=*/true, "Rejection details (only present when 'allowed' is false and rejection details exist)"},
360 : : }},
361 : : }
362 : 11526 : },
[ + - + -
+ - + - +
- + + + +
+ + + + -
- - - - -
- - ]
363 : 339 : RPCExamples{
364 : : "\nCreate a transaction\n"
365 [ + - + - : 678 : + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") +
+ - + - ]
366 : 339 : "Sign the transaction, and get back the hex\n"
367 [ + - + - : 1356 : + HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") +
+ - + - ]
368 : 339 : "\nTest acceptance of the transaction (signed hex)\n"
369 [ + - + - : 1356 : + HelpExampleCli("testmempoolaccept", R"('["signedhex"]')") +
+ - + - ]
370 : 339 : "\nAs a JSON-RPC call\n"
371 [ + - + - : 1356 : + HelpExampleRpc("testmempoolaccept", "[\"signedhex\"]")
+ - + - ]
372 [ + - ]: 339 : },
373 : 339 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
374 : : {
375 : 189 : const UniValue raw_transactions = request.params[0].get_array();
376 [ - + + + : 189 : if (raw_transactions.size() < 1 || raw_transactions.size() > MAX_PACKAGE_COUNT) {
+ + ]
377 : 8 : throw JSONRPCError(RPC_INVALID_PARAMETER,
378 [ + - + - : 24 : "Array must contain between 1 and " + ToString(MAX_PACKAGE_COUNT) + " transactions.");
+ - ]
379 : : }
380 : :
381 [ + - + + ]: 181 : const CFeeRate max_raw_tx_fee_rate{ParseFeeRate(self.Arg<UniValue>("maxfeerate"))};
382 : :
383 : 173 : std::vector<CTransactionRef> txns;
384 [ - + + - ]: 173 : txns.reserve(raw_transactions.size());
385 [ + - + + ]: 578 : for (const auto& rawtx : raw_transactions.getValues()) {
386 [ + - ]: 489 : CMutableTransaction mtx;
387 [ + + + - : 489 : if (!DecodeHexTx(mtx, rawtx.get_str())) {
+ + ]
388 : 2 : throw JSONRPCError(RPC_DESERIALIZATION_ERROR,
389 [ + - + - : 6 : "TX decode failed: " + rawtx.get_str() + " Make sure the tx has at least one input.");
+ - ]
390 : : }
391 [ + - + - ]: 1215 : txns.emplace_back(MakeTransactionRef(std::move(mtx)));
392 : 489 : }
393 : :
394 [ + - ]: 89 : NodeContext& node = EnsureAnyNodeContext(request.context);
395 [ + - ]: 89 : CTxMemPool& mempool = EnsureMemPool(node);
396 [ + - ]: 89 : ChainstateManager& chainman = EnsureChainman(node);
397 [ + - ]: 89 : Chainstate& chainstate = chainman.ActiveChainstate();
398 : 178 : const PackageMempoolAcceptResult package_result = [&] {
399 : 89 : LOCK(::cs_main);
400 [ - + + + : 89 : if (txns.size() > 1) return ProcessNewPackage(chainstate, mempool, txns, /*test_accept=*/true, /*client_maxfeerate=*/{});
+ - ]
401 [ + - ]: 38 : return PackageMempoolAcceptResult(txns[0]->GetWitnessHash(),
402 [ + - + - ]: 38 : chainman.ProcessTransaction(txns[0], /*test_accept=*/true));
403 [ + - ]: 178 : }();
404 : :
405 : 89 : UniValue rpc_result(UniValue::VARR);
406 : : // We will check transaction fees while we iterate through txns in order. If any transaction fee
407 : : // exceeds maxfeerate, we will leave the rest of the validation results blank, because it
408 : : // doesn't make sense to return a validation result for a transaction if its ancestor(s) would
409 : : // not be submitted.
410 : 89 : bool exit_early{false};
411 [ + + ]: 439 : for (const auto& tx : txns) {
412 : 350 : UniValue result_inner(UniValue::VOBJ);
413 [ + - + - : 700 : result_inner.pushKV("txid", tx->GetHash().GetHex());
+ - + - ]
414 [ + - + - : 700 : result_inner.pushKV("wtxid", tx->GetWitnessHash().GetHex());
+ - + - ]
415 [ + + ]: 350 : if (package_result.m_state.GetResult() == PackageValidationResult::PCKG_POLICY) {
416 [ + - + - : 362 : result_inner.pushKV("package-error", package_result.m_state.ToString());
+ - + - ]
417 : : }
418 : 350 : auto it = package_result.m_tx_results.find(tx->GetWitnessHash());
419 [ + - + + ]: 350 : if (exit_early || it == package_result.m_tx_results.end()) {
420 : : // Validation unfinished. Just return the txid and wtxid.
421 [ + - ]: 287 : rpc_result.push_back(std::move(result_inner));
422 : 287 : continue;
423 : : }
424 [ + - ]: 63 : const auto& tx_result = it->second;
425 : : // Package testmempoolaccept doesn't allow transactions to already be in the mempool.
426 [ + - ]: 63 : CHECK_NONFATAL(tx_result.m_result_type != MempoolAcceptResult::ResultType::MEMPOOL_ENTRY);
427 [ - + ]: 63 : if (tx_result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
428 [ # # ]: 0 : const CAmount fee = tx_result.m_base_fees.value();
429 : : // Check that fee does not exceed maximum fee
430 [ # # ]: 0 : const int64_t virtual_size = tx_result.m_vsize.value();
431 [ # # ]: 0 : const CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size);
432 [ # # ]: 0 : if (max_raw_tx_fee && fee > max_raw_tx_fee) {
433 [ # # # # : 0 : result_inner.pushKV("allowed", false);
# # ]
434 [ # # # # : 0 : result_inner.pushKV("reject-reason", "max-fee-exceeded");
# # ]
435 : 0 : exit_early = true;
436 : : } else {
437 : : // Only return the fee and vsize if the transaction would pass ATMP.
438 : : // These can be used to calculate the feerate.
439 [ # # # # : 0 : result_inner.pushKV("allowed", true);
# # ]
440 [ # # # # : 0 : result_inner.pushKV("vsize_adjusted", virtual_size);
# # ]
441 [ # # # # : 0 : result_inner.pushKV("vsize", virtual_size);
# # ]
442 [ # # # # : 0 : result_inner.pushKV("vsize_bip141", GetVirtualTransactionSize(*tx));
# # # # ]
443 : 0 : UniValue fees(UniValue::VOBJ);
444 [ # # # # : 0 : fees.pushKV("base", ValueFromAmount(fee));
# # ]
445 [ # # # # : 0 : fees.pushKV("effective-feerate", ValueFromAmount(tx_result.m_effective_feerate.value().GetFeePerK()));
# # # # ]
446 : 0 : UniValue effective_includes_res(UniValue::VARR);
447 [ # # # # ]: 0 : for (const auto& wtxid : tx_result.m_wtxids_fee_calculations.value()) {
448 [ # # # # : 0 : effective_includes_res.push_back(wtxid.ToString());
# # ]
449 : : }
450 [ # # # # ]: 0 : fees.pushKV("effective-includes", std::move(effective_includes_res));
451 [ # # # # ]: 0 : result_inner.pushKV("fees", std::move(fees));
452 : 0 : }
453 : : } else {
454 [ + - + - : 126 : result_inner.pushKV("allowed", false);
+ - ]
455 [ + - ]: 63 : const TxValidationState state = tx_result.m_state;
456 [ + + ]: 63 : if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
457 [ + - + - : 10 : result_inner.pushKV("reject-reason", "missing-inputs");
+ - ]
458 : : } else {
459 [ - + + - : 174 : result_inner.pushKV("reject-reason", state.GetRejectReason());
+ - + - ]
460 [ + - + - : 116 : result_inner.pushKV("reject-details", state.ToString());
+ - + - ]
461 : : }
462 : 63 : }
463 [ + - ]: 63 : rpc_result.push_back(std::move(result_inner));
464 : 350 : }
465 : 89 : return rpc_result;
466 : 273 : },
467 : 3051 : };
[ + - + -
+ - + + +
+ - - -
- ]
468 : 12204 : }
[ + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - - - -
- - - ]
469 : :
470 : 86 : static std::vector<RPCResult> ClusterDescription()
471 : : {
472 : 86 : return {
473 [ + - + - ]: 172 : RPCResult{RPCResult::Type::NUM, "clusterweight", "total sigops-adjusted weight (as defined in BIP 141 and modified by '-bytespersigop')"},
474 [ + - + - ]: 172 : RPCResult{RPCResult::Type::NUM, "txcount", "number of transactions"},
475 [ + - + - ]: 172 : RPCResult{RPCResult::Type::ARR, "chunks", "chunks in this cluster (in mining order)",
476 [ + - + - ]: 172 : {RPCResult{RPCResult::Type::OBJ, "chunk", "",
477 : : {
478 [ + - + - ]: 172 : RPCResult{RPCResult::Type::STR_AMOUNT, "chunkfee", "fees of the transactions in this chunk"},
479 [ + - + - ]: 172 : RPCResult{RPCResult::Type::NUM, "chunkweight", "sigops-adjusted weight of all transactions in this chunk"},
480 [ + - + - ]: 172 : RPCResult{RPCResult::Type::ARR, "txs", "transactions in this chunk in mining order",
481 [ + - + - : 430 : {RPCResult{RPCResult::Type::STR_HEX, "txid", "transaction id"}}},
+ - + + -
- ]
482 : : }
483 [ + - + + : 430 : }}
- - ]
484 [ + - + + : 258 : }
- - ]
485 [ + - + + : 516 : };
- - ]
486 : 1376 : }
[ + - + -
+ - + - +
- + - + -
+ - - - -
- ]
487 : :
488 : 299 : static std::vector<RPCResult> MempoolEntryDescription()
489 : : {
490 : 299 : std::vector<RPCResult> list = {
491 [ + - + - ]: 598 : {RPCResult::Type::NUM, "vsize", "(DEPRECATED) Was previously erroneously described as the BIP 141 vsize, but is actually sigops-adjusted vsize.\n"
492 : : "Use vsize_bip141 to actually get that behavior or switch to the explicit vsize_adjusted for retained behavior."},
493 [ + - + - ]: 598 : {RPCResult::Type::NUM, "vsize_bip141", "Virtual transaction size as defined in BIP 141.\n"
494 : : "This is different from actual serialized size for witness transactions as witness data is discounted."},
495 [ + - + - ]: 598 : {RPCResult::Type::NUM, "vsize_adjusted", "Maximum of sigop-adjusted size (-bytespersigop) and virtual transaction size as defined in BIP 141."},
496 [ + - + - ]: 598 : RPCResult{RPCResult::Type::NUM, "weight", "transaction weight as defined in BIP 141."},
497 [ + - + - ]: 598 : RPCResult{RPCResult::Type::NUM_TIME, "time", "local time transaction entered pool in seconds since 1 Jan 1970 GMT"},
498 [ + - + - ]: 598 : RPCResult{RPCResult::Type::NUM, "height", "block height when transaction entered pool"},
499 [ + - + - ]: 598 : RPCResult{RPCResult::Type::NUM, "descendantcount", "number of in-mempool descendant transactions (including this one)"},
500 [ + - + - ]: 598 : RPCResult{RPCResult::Type::NUM, "descendantsize", "virtual transaction size of in-mempool descendants (including this one)"},
501 [ + - + - ]: 598 : RPCResult{RPCResult::Type::NUM, "ancestorcount", "number of in-mempool ancestor transactions (including this one)"},
502 [ + - + - ]: 598 : RPCResult{RPCResult::Type::NUM, "ancestorsize", "virtual transaction size of in-mempool ancestors (including this one)"},
503 [ + - + - ]: 598 : RPCResult{RPCResult::Type::NUM, "chunkweight", "sigops-adjusted weight (as defined in BIP 141 and modified by '-bytespersigop') of this transaction's chunk"},
504 [ + - + - ]: 598 : RPCResult{RPCResult::Type::STR_HEX, "wtxid", "hash of serialized transaction, including witness data"},
505 [ + - + - ]: 598 : RPCResult{RPCResult::Type::OBJ, "fees", "",
506 : : {
507 [ + - + - ]: 598 : RPCResult{RPCResult::Type::STR_AMOUNT, "base", "transaction fee, denominated in " + CURRENCY_UNIT},
508 [ + - + - ]: 598 : RPCResult{RPCResult::Type::STR_AMOUNT, "modified", "transaction fee with fee deltas used for mining priority, denominated in " + CURRENCY_UNIT},
509 [ + - + - ]: 598 : 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},
510 [ + - + - ]: 598 : 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},
511 [ + - + - ]: 598 : RPCResult{RPCResult::Type::STR_AMOUNT, "chunk", "transaction fees of chunk, denominated in " + CURRENCY_UNIT},
512 [ + - + + : 2093 : }},
- - ]
513 [ + - + - ]: 598 : RPCResult{RPCResult::Type::ARR, "depends", "unconfirmed transactions used as inputs for this transaction",
514 [ + - + - : 1495 : {RPCResult{RPCResult::Type::STR_HEX, "transactionid", "parent transaction id"}}},
+ - + + -
- ]
515 [ + - + - ]: 598 : RPCResult{RPCResult::Type::ARR, "spentby", "unconfirmed transactions spending outputs from this transaction",
516 [ + - + - : 1495 : {RPCResult{RPCResult::Type::STR_HEX, "transactionid", "child transaction id"}}},
+ - + + -
- ]
517 [ + - + - ]: 598 : RPCResult{RPCResult::Type::BOOL, "unbroadcast", "Whether this transaction is currently unbroadcast (initial broadcast not yet acknowledged by any peers)"},
518 [ + - + + : 6279 : };
- - ]
519 [ + - + - : 299 : if (IsDeprecatedRPCEnabled("bip125")) {
- + ]
520 [ # # ]: 0 : list.emplace_back(RPCResult::Type::BOOL, "bip125-replaceable", "Whether this transaction signals BIP125 replaceability or has an unconfirmed ancestor signaling BIP125 replaceability. (DEPRECATED)\n");
521 : : }
522 : 299 : return list;
523 : 13754 : }
[ + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - - - -
- ]
524 : :
525 : 0 : void AppendChunkInfo(UniValue& all_chunks, FeePerWeight chunk_feerate, std::vector<const CTxMemPoolEntry *> chunk_txs)
526 : : {
527 : 0 : UniValue chunk(UniValue::VOBJ);
528 [ # # # # : 0 : chunk.pushKV("chunkfee", ValueFromAmount(chunk_feerate.fee));
# # ]
529 [ # # # # : 0 : chunk.pushKV("chunkweight", chunk_feerate.size);
# # ]
530 : 0 : UniValue chunk_txids(UniValue::VARR);
531 [ # # ]: 0 : for (const auto& chunk_tx : chunk_txs) {
532 [ # # # # : 0 : chunk_txids.push_back(chunk_tx->GetTx().GetHash().ToString());
# # ]
533 : : }
534 [ # # # # ]: 0 : chunk.pushKV("txs", std::move(chunk_txids));
535 [ # # ]: 0 : all_chunks.push_back(std::move(chunk));
536 : 0 : }
537 : :
538 : 0 : static void clusterToJSON(const CTxMemPool& pool, UniValue& info, std::vector<const CTxMemPoolEntry *> cluster) EXCLUSIVE_LOCKS_REQUIRED(pool.cs)
539 : : {
540 : 0 : AssertLockHeld(pool.cs);
541 : 0 : int total_weight{0};
542 [ # # ]: 0 : for (const auto& tx : cluster) {
543 : 0 : total_weight += tx->GetAdjustedWeight();
544 : : }
545 [ # # # # ]: 0 : info.pushKV("clusterweight", total_weight);
546 [ # # # # : 0 : info.pushKV("txcount", cluster.size());
# # ]
547 : :
548 : : // Output the cluster by chunk. This isn't handed to us by the mempool, but
549 : : // we can calculate it by looking at the chunk feerates of each transaction
550 : : // in the cluster.
551 : 0 : FeePerWeight current_chunk_feerate = pool.GetMainChunkFeerate(*cluster[0]);
552 : 0 : std::vector<const CTxMemPoolEntry *> current_chunk;
553 [ # # # # ]: 0 : current_chunk.reserve(cluster.size());
554 : :
555 : 0 : UniValue all_chunks(UniValue::VARR);
556 [ # # ]: 0 : for (const auto& tx : cluster) {
557 [ # # ]: 0 : if (current_chunk_feerate.size == 0) {
558 : : // We've iterated all the transactions in the previous chunk; so
559 : : // append it to the output.
560 [ # # # # ]: 0 : AppendChunkInfo(all_chunks, pool.GetMainChunkFeerate(*current_chunk[0]), current_chunk);
561 [ # # ]: 0 : current_chunk.clear();
562 : 0 : current_chunk_feerate = pool.GetMainChunkFeerate(*tx);
563 : : }
564 [ # # ]: 0 : current_chunk.push_back(tx);
565 [ # # ]: 0 : current_chunk_feerate.size -= tx->GetAdjustedWeight();
566 : : }
567 [ # # # # ]: 0 : AppendChunkInfo(all_chunks, pool.GetMainChunkFeerate(*current_chunk[0]), current_chunk);
568 [ # # ]: 0 : current_chunk.clear();
569 [ # # # # ]: 0 : info.pushKV("chunks", std::move(all_chunks));
570 : 0 : }
571 : :
572 : 0 : static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPoolEntry& e) EXCLUSIVE_LOCKS_REQUIRED(pool.cs)
573 : : {
574 : 0 : AssertLockHeld(pool.cs);
575 : :
576 : 0 : auto [ancestor_count, ancestor_size, ancestor_fees] = pool.CalculateAncestorData(e);
577 : 0 : auto [descendant_count, descendant_size, descendant_fees] = pool.CalculateDescendantData(e);
578 : :
579 [ # # # # ]: 0 : info.pushKV("vsize_adjusted", e.GetTxSize());
580 [ # # # # ]: 0 : info.pushKV("vsize", e.GetTxSize());
581 [ # # # # ]: 0 : info.pushKV("vsize_bip141", GetVirtualTransactionSize(e.GetTx()));
582 [ # # # # ]: 0 : info.pushKV("weight", e.GetTxWeight());
583 [ # # # # ]: 0 : info.pushKV("time", count_seconds(e.GetTime()));
584 [ # # # # ]: 0 : info.pushKV("height", e.GetHeight());
585 [ # # # # ]: 0 : info.pushKV("descendantcount", descendant_count);
586 [ # # # # ]: 0 : info.pushKV("descendantsize", descendant_size);
587 [ # # # # ]: 0 : info.pushKV("ancestorcount", ancestor_count);
588 [ # # # # ]: 0 : info.pushKV("ancestorsize", ancestor_size);
589 [ # # # # : 0 : info.pushKV("wtxid", e.GetTx().GetWitnessHash().ToString());
# # ]
590 : 0 : auto feerate = pool.GetMainChunkFeerate(e);
591 [ # # # # ]: 0 : info.pushKV("chunkweight", feerate.size);
592 : :
593 : 0 : UniValue fees(UniValue::VOBJ);
594 [ # # # # : 0 : fees.pushKV("base", ValueFromAmount(e.GetFee()));
# # ]
595 [ # # # # : 0 : fees.pushKV("modified", ValueFromAmount(e.GetModifiedFee()));
# # ]
596 [ # # # # : 0 : fees.pushKV("ancestor", ValueFromAmount(ancestor_fees));
# # ]
597 [ # # # # : 0 : fees.pushKV("descendant", ValueFromAmount(descendant_fees));
# # ]
598 [ # # # # : 0 : fees.pushKV("chunk", ValueFromAmount(feerate.fee));
# # ]
599 [ # # # # ]: 0 : info.pushKV("fees", std::move(fees));
600 : :
601 : 0 : const CTransaction& tx = e.GetTx();
602 : 0 : std::set<std::string> setDepends;
603 [ # # ]: 0 : for (const CTxIn& txin : tx.vin)
604 : : {
605 [ # # # # ]: 0 : if (pool.exists(txin.prevout.hash))
606 [ # # # # ]: 0 : setDepends.insert(txin.prevout.hash.ToString());
607 : : }
608 : :
609 : 0 : UniValue depends(UniValue::VARR);
610 [ # # ]: 0 : for (const std::string& dep : setDepends)
611 : : {
612 [ # # # # ]: 0 : depends.push_back(dep);
613 : : }
614 : :
615 [ # # # # ]: 0 : info.pushKV("depends", std::move(depends));
616 : :
617 : 0 : UniValue spent(UniValue::VARR);
618 [ # # # # : 0 : for (const CTxMemPoolEntry& child : pool.GetChildren(e)) {
# # ]
619 [ # # # # : 0 : spent.push_back(child.GetTx().GetHash().ToString());
# # ]
620 : 0 : }
621 : :
622 [ # # # # ]: 0 : info.pushKV("spentby", std::move(spent));
623 [ # # # # : 0 : info.pushKV("unbroadcast", pool.IsUnbroadcastTx(tx.GetHash()));
# # ]
624 : :
625 : : // Add opt-in RBF status
626 [ # # # # : 0 : if (IsDeprecatedRPCEnabled("bip125")) {
# # ]
627 : 0 : bool rbfStatus = false;
628 [ # # ]: 0 : RBFTransactionState rbfState = IsRBFOptIn(tx, pool);
629 [ # # ]: 0 : if (rbfState == RBFTransactionState::UNKNOWN) {
630 [ # # # # ]: 0 : throw JSONRPCError(RPC_MISC_ERROR, "Transaction is not in mempool");
631 [ # # ]: 0 : } else if (rbfState == RBFTransactionState::REPLACEABLE_BIP125) {
632 : 0 : rbfStatus = true;
633 : : }
634 [ # # # # : 0 : info.pushKV("bip125-replaceable", rbfStatus);
# # ]
635 : : }
636 : 0 : }
637 : :
638 : 5 : UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose, bool include_mempool_sequence)
639 : : {
640 [ + + ]: 5 : if (verbose) {
641 [ + + ]: 2 : if (include_mempool_sequence) {
642 [ + - + - ]: 2 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Verbose results cannot contain mempool sequence values.");
643 : : }
644 : 1 : LOCK(pool.cs);
645 : 1 : UniValue o(UniValue::VOBJ);
646 [ + - - + ]: 1 : for (const CTxMemPoolEntry& e : pool.entryAll()) {
647 : 0 : UniValue info(UniValue::VOBJ);
648 [ # # ]: 0 : entryToJSON(pool, info, e);
649 : : // Mempool has unique entries so there is no advantage in using
650 : : // UniValue::pushKV, which checks if the key already exists in O(N).
651 : : // UniValue::pushKVEnd is used instead which currently is O(1).
652 [ # # # # ]: 0 : o.pushKVEnd(e.GetTx().GetHash().ToString(), std::move(info));
653 : 0 : }
654 [ + - ]: 1 : return o;
655 : 1 : } else {
656 : 3 : UniValue a(UniValue::VARR);
657 : 3 : uint64_t mempool_sequence;
658 : 3 : {
659 [ + - ]: 3 : LOCK(pool.cs);
660 [ + - - - : 3 : for (const CTxMemPoolEntry& e : pool.entryAll()) {
- + ]
661 [ # # # # : 0 : a.push_back(e.GetTx().GetHash().ToString());
# # ]
662 : : }
663 [ + - ]: 3 : mempool_sequence = pool.GetSequence();
664 : 0 : }
665 [ + + ]: 3 : if (!include_mempool_sequence) {
666 : 2 : return a;
667 : : } else {
668 : 1 : UniValue o(UniValue::VOBJ);
669 [ + - + - ]: 2 : o.pushKV("txids", std::move(a));
670 [ + - + - : 2 : o.pushKV("mempool_sequence", mempool_sequence);
+ - ]
671 : 1 : return o;
672 : 1 : }
673 : 3 : }
674 : : }
675 : :
676 : 65 : static RPCMethod getmempoolfeeratediagram()
677 : : {
678 : 65 : return RPCMethod{"getmempoolfeeratediagram",
679 [ + - ]: 130 : "Returns the feerate diagram for the whole mempool.",
680 : : {},
681 : : {
682 : 0 : RPCResult{"mempool chunks",
683 [ + - + - ]: 130 : RPCResult::Type::ARR, "", "",
684 : : {
685 : : {
686 [ + - + - ]: 130 : RPCResult::Type::OBJ, "", "",
687 : : {
688 [ + - + - ]: 130 : {RPCResult::Type::NUM, "weight", "cumulative sigops-adjusted weight"},
689 [ + - + - ]: 130 : {RPCResult::Type::STR_AMOUNT, "fee", "cumulative fee"}
690 : : }
691 : : }
692 : : }
693 : 585 : }
[ + - + -
+ + + + -
- - - ]
694 : : },
695 : 65 : RPCExamples{
696 [ + - + - : 130 : HelpExampleCli("getmempoolfeeratediagram", "")
+ - ]
697 [ + - + - : 260 : + HelpExampleRpc("getmempoolfeeratediagram", "")
+ - ]
698 [ + - ]: 65 : },
699 : 65 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
700 : : {
701 : 1 : const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
702 : 1 : LOCK(mempool.cs);
703 : :
704 : 1 : UniValue result(UniValue::VARR);
705 : :
706 [ + - ]: 1 : auto diagram = mempool.GetFeerateDiagram();
707 : :
708 [ + + ]: 2 : for (auto f : diagram) {
709 : 1 : UniValue o(UniValue::VOBJ);
710 [ + - + - : 2 : o.pushKV("weight", f.size);
+ - ]
711 [ + - + - : 2 : o.pushKV("fee", ValueFromAmount(f.fee));
+ - ]
712 [ + - + - ]: 1 : result.push_back(o);
713 : 1 : }
714 : 1 : return result;
715 [ + - ]: 2 : }
716 : 520 : };
[ + - + -
+ - + - +
+ - - ]
717 [ + - + - : 520 : }
+ - + - -
- ]
718 : :
719 : 77 : static RPCMethod getrawmempool()
720 : : {
721 : 77 : return RPCMethod{
722 : 77 : "getrawmempool",
723 [ + - ]: 154 : "Returns all transaction ids in memory pool as a json array of string transaction ids.\n"
724 : : "\nHint: use getmempoolentry to fetch a specific transaction from the mempool.\n",
725 : : {
726 [ + - + - : 231 : {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"},
+ - ]
727 [ + - + - : 231 : {"mempool_sequence", RPCArg::Type::BOOL, RPCArg::Default{false}, "If verbose=false, returns a json object with transaction list and mempool sequence number attached."},
+ - ]
728 : : },
729 : : {
730 [ + - ]: 77 : RPCResult{"for verbose = false",
731 [ + - + - ]: 154 : RPCResult::Type::ARR, "", "",
732 : : {
733 [ + - + - ]: 154 : {RPCResult::Type::STR_HEX, "", "The transaction id"},
734 [ + - + + : 308 : }},
- - ]
735 [ + - ]: 154 : RPCResult{"for verbose = true",
736 [ + - + - ]: 154 : RPCResult::Type::OBJ_DYN, "", "",
737 : : {
738 [ + - + - : 154 : {RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()},
+ - ]
739 [ + - + + : 231 : }},
- - ]
740 [ + - ]: 154 : RPCResult{"for verbose = false and mempool_sequence = true",
741 [ + - + - ]: 154 : RPCResult::Type::OBJ, "", "",
742 : : {
743 [ + - + - ]: 154 : {RPCResult::Type::ARR, "txids", "",
744 : : {
745 [ + - + - ]: 154 : {RPCResult::Type::STR_HEX, "", "The transaction id"},
746 : : }},
747 [ + - + - ]: 154 : {RPCResult::Type::NUM, "mempool_sequence", "The mempool sequence value."},
748 : 693 : }},
[ + - + -
+ + + + -
- - - ]
749 : : },
750 : 77 : RPCExamples{
751 [ + - + - : 154 : HelpExampleCli("getrawmempool", "true")
+ - ]
752 [ + - + - : 308 : + HelpExampleRpc("getrawmempool", "true")
+ - ]
753 [ + - ]: 77 : },
754 : 77 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
755 : : {
756 : 5 : bool fVerbose = false;
757 [ + + ]: 5 : if (!request.params[0].isNull())
758 : 3 : fVerbose = request.params[0].get_bool();
759 : :
760 : 5 : bool include_mempool_sequence = false;
761 [ + + ]: 5 : if (!request.params[1].isNull()) {
762 : 2 : include_mempool_sequence = request.params[1].get_bool();
763 : : }
764 : :
765 : 5 : return MempoolToJSON(EnsureAnyMemPool(request.context), fVerbose, include_mempool_sequence);
766 : : },
767 : 924 : };
[ + - + -
+ - + - +
+ + + - -
- - ]
768 : 1540 : }
[ + - + -
+ - + - +
- + - + -
+ - + - +
- - - - -
- - ]
769 : :
770 : 74 : static RPCMethod getmempoolancestors()
771 : : {
772 : 74 : return RPCMethod{
773 : 74 : "getmempoolancestors",
774 [ + - ]: 148 : "If txid is in the mempool, returns all in-mempool ancestors.\n",
775 : : {
776 [ + - + - ]: 148 : {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
777 [ + - + - : 222 : {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"},
+ - ]
778 : : },
779 : : {
780 [ + - ]: 74 : RPCResult{"for verbose = false",
781 [ + - + - ]: 148 : RPCResult::Type::ARR, "", "",
782 [ + - + - : 370 : {{RPCResult::Type::STR_HEX, "", "The transaction id of an in-mempool ancestor transaction"}}},
+ - + + -
- ]
783 [ + - ]: 148 : RPCResult{"for verbose = true",
784 [ + - + - ]: 148 : RPCResult::Type::OBJ_DYN, "", "",
785 : : {
786 [ + - + - : 148 : {RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()},
+ - ]
787 [ + - + + : 222 : }},
- - ]
788 : : },
789 : 74 : RPCExamples{
790 [ + - + - : 148 : HelpExampleCli("getmempoolancestors", "\"mytxid\"")
+ - ]
791 [ + - + - : 296 : + HelpExampleRpc("getmempoolancestors", "\"mytxid\"")
+ - ]
792 [ + - ]: 74 : },
793 : 74 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
794 : : {
795 : 3 : bool fVerbose = false;
796 [ + + ]: 3 : if (!request.params[1].isNull())
797 : 1 : fVerbose = request.params[1].get_bool();
798 : :
799 : 3 : auto txid{Txid::FromUint256(ParseHashV(request.params[0], "txid"))};
800 : :
801 : 1 : const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
802 : 1 : LOCK(mempool.cs);
803 : :
804 [ + - ]: 1 : const auto entry{mempool.GetEntry(txid)};
805 [ + - ]: 1 : if (entry == nullptr) {
806 [ + - + - ]: 2 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
807 : : }
808 : :
809 [ # # ]: 0 : auto ancestors{mempool.CalculateMemPoolAncestors(*entry)};
810 : :
811 [ # # ]: 0 : if (!fVerbose) {
812 : 0 : UniValue o(UniValue::VARR);
813 [ # # ]: 0 : for (CTxMemPool::txiter ancestorIt : ancestors) {
814 [ # # # # : 0 : o.push_back(ancestorIt->GetTx().GetHash().ToString());
# # ]
815 : : }
816 : : return o;
817 : 0 : } else {
818 : 0 : UniValue o(UniValue::VOBJ);
819 [ # # ]: 0 : for (CTxMemPool::txiter ancestorIt : ancestors) {
820 : 0 : const CTxMemPoolEntry &e = *ancestorIt;
821 : 0 : UniValue info(UniValue::VOBJ);
822 [ # # ]: 0 : entryToJSON(mempool, info, e);
823 [ # # # # ]: 0 : o.pushKVEnd(e.GetTx().GetHash().ToString(), std::move(info));
824 : 0 : }
825 : 0 : return o;
826 : 0 : }
827 [ # # ]: 0 : },
828 : 814 : };
[ + - + -
+ - + - +
+ + + - -
- - ]
829 : 888 : }
[ + - + -
+ - + - +
- + - - -
- - ]
830 : :
831 : 74 : static RPCMethod getmempooldescendants()
832 : : {
833 : 74 : return RPCMethod{
834 : 74 : "getmempooldescendants",
835 [ + - ]: 148 : "If txid is in the mempool, returns all in-mempool descendants.\n",
836 : : {
837 [ + - + - ]: 148 : {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
838 [ + - + - : 222 : {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"},
+ - ]
839 : : },
840 : : {
841 [ + - ]: 74 : RPCResult{"for verbose = false",
842 [ + - + - ]: 148 : RPCResult::Type::ARR, "", "",
843 [ + - + - : 370 : {{RPCResult::Type::STR_HEX, "", "The transaction id of an in-mempool descendant transaction"}}},
+ - + + -
- ]
844 [ + - ]: 148 : RPCResult{"for verbose = true",
845 [ + - + - ]: 148 : RPCResult::Type::OBJ_DYN, "", "",
846 : : {
847 [ + - + - : 148 : {RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()},
+ - ]
848 [ + - + + : 222 : }},
- - ]
849 : : },
850 : 74 : RPCExamples{
851 [ + - + - : 148 : HelpExampleCli("getmempooldescendants", "\"mytxid\"")
+ - ]
852 [ + - + - : 296 : + HelpExampleRpc("getmempooldescendants", "\"mytxid\"")
+ - ]
853 [ + - ]: 74 : },
854 : 74 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
855 : : {
856 : 3 : bool fVerbose = false;
857 [ + + ]: 3 : if (!request.params[1].isNull())
858 : 1 : fVerbose = request.params[1].get_bool();
859 : :
860 : 3 : auto txid{Txid::FromUint256(ParseHashV(request.params[0], "txid"))};
861 : :
862 : 1 : const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
863 : 1 : LOCK(mempool.cs);
864 : :
865 [ + - ]: 1 : const auto it{mempool.GetIter(txid)};
866 [ + - ]: 1 : if (!it) {
867 [ + - + - ]: 2 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
868 : : }
869 : :
870 [ # # ]: 0 : CTxMemPool::setEntries setDescendants;
871 [ # # ]: 0 : mempool.CalculateDescendants(*it, setDescendants);
872 : : // CTxMemPool::CalculateDescendants will include the given tx
873 : 0 : setDescendants.erase(*it);
874 : :
875 [ # # ]: 0 : if (!fVerbose) {
876 : 0 : UniValue o(UniValue::VARR);
877 [ # # ]: 0 : for (CTxMemPool::txiter descendantIt : setDescendants) {
878 [ # # # # : 0 : o.push_back(descendantIt->GetTx().GetHash().ToString());
# # ]
879 : : }
880 : :
881 : : return o;
882 : 0 : } else {
883 : 0 : UniValue o(UniValue::VOBJ);
884 [ # # ]: 0 : for (CTxMemPool::txiter descendantIt : setDescendants) {
885 : 0 : const CTxMemPoolEntry &e = *descendantIt;
886 : 0 : UniValue info(UniValue::VOBJ);
887 [ # # ]: 0 : entryToJSON(mempool, info, e);
888 [ # # # # ]: 0 : o.pushKVEnd(e.GetTx().GetHash().ToString(), std::move(info));
889 : 0 : }
890 : 0 : return o;
891 : 0 : }
892 [ # # ]: 0 : },
893 : 814 : };
[ + - + -
+ - + - +
+ + + - -
- - ]
894 : 888 : }
[ + - + -
+ - + - +
- + - - -
- - ]
895 : :
896 : 86 : static RPCMethod getmempoolcluster()
897 : : {
898 : 86 : return RPCMethod{"getmempoolcluster",
899 [ + - ]: 172 : "Returns mempool data for given cluster\n",
900 : : {
901 [ + - + - ]: 172 : {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The txid of a transaction in the cluster"},
902 : : },
903 [ + - ]: 172 : RPCResult{
904 [ + - + - : 172 : RPCResult::Type::OBJ, "", "", ClusterDescription()},
+ - + - ]
905 : 86 : RPCExamples{
906 [ + - + - : 172 : HelpExampleCli("getmempoolcluster", "txid")
+ - ]
907 [ + - + - : 344 : + HelpExampleRpc("getmempoolcluster", R"("txid")")
+ - + - ]
908 [ + - ]: 86 : },
909 : 86 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
910 : : {
911 : 1 : uint256 hash = ParseHashV(request.params[0], "txid");
912 : :
913 : 1 : const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
914 : 1 : LOCK(mempool.cs);
915 : :
916 [ + - ]: 1 : auto txid = Txid::FromUint256(hash);
917 [ + - ]: 1 : const auto entry{mempool.GetEntry(txid)};
918 [ + - ]: 1 : if (entry == nullptr) {
919 [ + - + - ]: 2 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
920 : : }
921 : :
922 [ # # ]: 0 : auto cluster = mempool.GetCluster(txid);
923 : :
924 : 0 : UniValue info(UniValue::VOBJ);
925 [ # # # # ]: 0 : clusterToJSON(mempool, info, cluster);
926 : 0 : return info;
927 [ # # ]: 0 : },
928 [ + - + - : 430 : };
+ + - - ]
929 [ + - ]: 172 : }
930 : :
931 : 74 : static RPCMethod getmempoolentry()
932 : : {
933 : 74 : return RPCMethod{
934 : 74 : "getmempoolentry",
935 [ + - ]: 148 : "Returns mempool data for given transaction\n",
936 : : {
937 [ + - + - ]: 148 : {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
938 : : },
939 [ + - ]: 148 : RPCResult{
940 [ + - + - : 148 : RPCResult::Type::OBJ, "", "", MempoolEntryDescription()},
+ - + - ]
941 : 74 : RPCExamples{
942 [ + - + - : 148 : HelpExampleCli("getmempoolentry", "\"mytxid\"")
+ - ]
943 [ + - + - : 296 : + HelpExampleRpc("getmempoolentry", "\"mytxid\"")
+ - + - ]
944 [ + - ]: 74 : },
945 : 74 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
946 : : {
947 : 1 : auto txid{Txid::FromUint256(ParseHashV(request.params[0], "txid"))};
948 : :
949 : 1 : const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
950 : 1 : LOCK(mempool.cs);
951 : :
952 [ + - ]: 1 : const auto entry{mempool.GetEntry(txid)};
953 [ + - ]: 1 : if (entry == nullptr) {
954 [ + - + - ]: 2 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
955 : : }
956 : :
957 : 0 : UniValue info(UniValue::VOBJ);
958 [ # # ]: 0 : entryToJSON(mempool, info, *entry);
959 [ # # ]: 0 : return info;
960 : 0 : },
961 [ + - + - : 370 : };
+ + - - ]
962 [ + - ]: 148 : }
963 : :
964 : 97 : static RPCMethod gettxspendingprevout()
965 : : {
966 : 97 : return RPCMethod{"gettxspendingprevout",
967 [ + - ]: 194 : "Scans the mempool (and the txospenderindex, if available) to find transactions spending any of the given outputs",
968 : : {
969 [ + - + - ]: 194 : {"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The transaction outputs that we want to check, and within each, the txid (string) vout (numeric).",
970 : : {
971 [ + - + - ]: 194 : {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
972 : : {
973 [ + - + - ]: 194 : {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
974 [ + - + - ]: 194 : {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
975 : : },
976 : : },
977 : : },
978 : : },
979 [ + - + - ]: 194 : {"options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "",
980 : : {
981 [ + - + - : 291 : {"mempool_only", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true if txospenderindex unavailable, otherwise false"}, "If false and mempool lacks a relevant spend, use txospenderindex (throws an exception if not available)."},
+ - ]
982 [ + - + - : 291 : {"return_spending_tx", RPCArg::Type::BOOL, RPCArg::DefaultHint{"false"}, "If true, return the full spending tx."},
+ - ]
983 : : },
984 : : },
985 : : },
986 [ + - ]: 194 : RPCResult{
987 [ + - + - ]: 194 : RPCResult::Type::ARR, "", "",
988 : : {
989 [ + - + - ]: 194 : {RPCResult::Type::OBJ, "", "",
990 : : {
991 [ + - + - ]: 194 : {RPCResult::Type::STR_HEX, "txid", "the transaction id of the checked output"},
992 [ + - + - ]: 194 : {RPCResult::Type::NUM, "vout", "the vout value of the checked output"},
993 [ + - + - ]: 194 : {RPCResult::Type::STR_HEX, "spendingtxid", /*optional=*/true, "the transaction id of the mempool transaction spending this output (omitted if unspent)"},
994 [ + - + - ]: 194 : {RPCResult::Type::STR_HEX, "spendingtx", /*optional=*/true, "the transaction spending this output (only if return_spending_tx is set, omitted if unspent)"},
995 [ + - + - ]: 194 : {RPCResult::Type::STR_HEX, "blockhash", /*optional=*/true, "the hash of the spending block (omitted if unspent or the spending tx is not confirmed)"},
996 : : }},
997 : : }
998 : 1455 : },
[ + - + -
+ - + + +
+ - - -
- ]
999 : 97 : RPCExamples{
1000 [ + - + - : 194 : HelpExampleCli("gettxspendingprevout", "\"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":3}]\"")
+ - ]
1001 [ + - + - : 388 : + HelpExampleRpc("gettxspendingprevout", "\"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":3}]\"")
+ - + - ]
1002 : 582 : + HelpExampleCliNamed("gettxspendingprevout", {{"outputs", "[{\"txid\":\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\",\"vout\":3}]"}, {"return_spending_tx", true}})
[ + - + -
+ - + - +
+ - - ]
1003 [ + - ]: 97 : },
1004 : 97 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
1005 : : {
1006 : 22 : const UniValue& output_params = request.params[0].get_array();
1007 [ - + + + ]: 22 : if (output_params.empty()) {
1008 [ + - + - ]: 4 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, outputs are missing");
1009 : : }
1010 [ + + ]: 20 : const UniValue options{request.params[1].isNull() ? UniValue::VOBJ : request.params[1]};
1011 [ + + + + : 92 : RPCTypeCheckObj(options,
+ + ]
1012 : : {
1013 [ + - ]: 20 : {"mempool_only", UniValueType(UniValue::VBOOL)},
1014 [ + - ]: 20 : {"return_spending_tx", UniValueType(UniValue::VBOOL)},
1015 : : }, /*fAllowNull=*/true, /*fStrict=*/true);
1016 : :
1017 [ + - + + : 17 : const bool mempool_only{options.exists("mempool_only") ? options["mempool_only"].get_bool() : !g_txospenderindex};
+ - + - +
- ]
1018 [ + - - + : 16 : const bool return_spending_tx{options.exists("return_spending_tx") ? options["return_spending_tx"].get_bool() : false};
- - - - -
- ]
1019 : :
1020 : : // Worklist of outpoints to resolve
1021 : 8 : struct Entry {
1022 : : COutPoint outpoint;
1023 : : size_t request_index;
1024 : : };
1025 : 8 : std::vector<Entry> prevouts_to_process;
1026 [ - + + - ]: 8 : prevouts_to_process.reserve(output_params.size());
1027 [ - + + - ]: 8 : for (const size_t idx : std::views::iota(size_t{0}, output_params.size())) {
1028 [ + - + + ]: 8 : const UniValue& o = output_params[idx].get_obj();
1029 : :
1030 [ - + - - : 25 : RPCTypeCheckObj(o,
+ + ]
1031 : : {
1032 [ + - ]: 5 : {"txid", UniValueType(UniValue::VSTR)},
1033 [ + - ]: 5 : {"vout", UniValueType(UniValue::VNUM)},
1034 : : }, /*fAllowNull=*/false, /*fStrict=*/true);
1035 : :
1036 [ # # ]: 0 : const Txid txid = Txid::FromUint256(ParseHashO(o, "txid"));
1037 [ # # # # ]: 0 : const int nOutput{o.find_value("vout").getInt<int>()};
1038 [ # # ]: 0 : if (nOutput < 0) {
1039 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
1040 : : }
1041 [ # # ]: 0 : prevouts_to_process.emplace_back(COutPoint{txid, static_cast<uint32_t>(nOutput)}, idx);
1042 : : }
1043 : :
1044 : 0 : auto make_output = [&output_params, return_spending_tx](const Entry& prevout, const CTransaction* spending_tx = nullptr) {
1045 [ # # ]: 0 : UniValue o{output_params[prevout.request_index]};
1046 [ # # ]: 0 : if (spending_tx) {
1047 [ # # # # : 0 : o.pushKV("spendingtxid", spending_tx->GetHash().ToString());
# # # # ]
1048 [ # # ]: 0 : if (return_spending_tx) {
1049 [ # # # # : 0 : o.pushKV("spendingtx", EncodeHexTx(*spending_tx));
# # # # ]
1050 : : }
1051 : : }
1052 : 0 : return o;
1053 : 0 : };
1054 : :
1055 [ # # # # ]: 0 : std::vector<UniValue> results(output_params.size());
1056 : :
1057 : : // Search the mempool first
1058 : 0 : std::vector<Entry> unresolved;
1059 [ # # # # ]: 0 : unresolved.reserve(prevouts_to_process.size());
1060 : 0 : {
1061 [ # # ]: 0 : const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
1062 [ # # ]: 0 : LOCK(mempool.cs);
1063 : :
1064 : : // Make the result if the spending tx appears in the mempool or this is a mempool_only request
1065 [ # # ]: 0 : for (const auto& prevout : prevouts_to_process) {
1066 [ # # ]: 0 : const auto* spending_tx{mempool.GetConflictTx(prevout.outpoint)};
1067 : :
1068 : : // If the outpoint is not spent in the mempool and this is not a mempool-only
1069 : : // request, we cannot answer it yet.
1070 [ # # ]: 0 : if (!spending_tx && !mempool_only) {
1071 [ # # ]: 0 : unresolved.push_back(prevout);
1072 : : } else {
1073 [ # # ]: 0 : results[prevout.request_index] = make_output(prevout, spending_tx);
1074 : : }
1075 : : }
1076 : 0 : }
1077 : :
1078 : : // mempool_only requests resolve every outpoint above, so only other requests reach the index.
1079 [ # # # # : 0 : if (!unresolved.empty() && (!g_txospenderindex || !g_txospenderindex->BlockUntilSyncedToCurrentChain())) {
# # # # ]
1080 [ # # # # ]: 0 : throw JSONRPCError(RPC_MISC_ERROR, "Mempool lacks a relevant spend, and txospenderindex is unavailable.");
1081 : : }
1082 : :
1083 [ # # ]: 0 : for (const auto& prevout : unresolved) {
1084 [ # # ]: 0 : const auto spender{g_txospenderindex->FindSpender(prevout.outpoint)};
1085 [ # # ]: 0 : if (!spender) {
1086 [ # # ]: 0 : throw JSONRPCError(RPC_MISC_ERROR, spender.error());
1087 : : }
1088 : :
1089 [ # # # # ]: 0 : if (const auto& spender_opt{spender.value()}) {
1090 [ # # ]: 0 : UniValue o{make_output(prevout, spender_opt->tx.get())};
1091 [ # # # # : 0 : o.pushKV("blockhash", spender_opt->block_hash.GetHex());
# # # # ]
1092 : 0 : results[prevout.request_index] = std::move(o);
1093 : 0 : } else {
1094 : : // Only return the input outpoint itself, which indicates it is unspent.
1095 [ # # ]: 0 : results[prevout.request_index] = make_output(prevout);
1096 : : }
1097 : 0 : }
1098 : :
1099 : 0 : UniValue result{UniValue::VARR};
1100 [ # # # # ]: 0 : result.reserve(results.size());
1101 [ # # # # ]: 0 : for (auto& output : results) result.push_back(std::move(output));
1102 : 0 : return result;
1103 : 62 : },
[ + - + -
+ - + - +
- + - - +
- + ]
1104 : 1649 : };
[ + - + -
+ - + - +
- + + + +
+ + + + -
- - - - -
- - ]
1105 : 2619 : }
[ + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- - - - -
- - - - -
- ]
1106 : :
1107 : 1 : UniValue MempoolInfoToJSON(const CTxMemPool& pool)
1108 : : {
1109 : : // Make sure this call is atomic in the pool.
1110 : 1 : LOCK(pool.cs);
1111 : 1 : UniValue ret(UniValue::VOBJ);
1112 [ + - + - : 2 : ret.pushKV("loaded", pool.GetLoadTried());
+ - + - ]
1113 [ + - + - : 2 : ret.pushKV("size", pool.size());
+ - + - ]
1114 [ + - + - : 2 : ret.pushKV("bytes", pool.GetTotalTxSize());
+ - ]
1115 [ + - + - : 2 : ret.pushKV("usage", pool.DynamicMemoryUsage());
+ - + - ]
1116 [ + - + - : 2 : ret.pushKV("total_fee", ValueFromAmount(pool.GetTotalFee()));
+ - ]
1117 [ + - + - : 2 : ret.pushKV("maxmempool", pool.m_opts.max_size_bytes);
+ - ]
1118 [ + - + - : 2 : ret.pushKV("mempoolminfee", ValueFromAmount(std::max(pool.GetMinFee(), pool.m_opts.min_relay_feerate).GetFeePerK()));
+ - + - ]
1119 [ + - + - : 2 : ret.pushKV("minrelaytxfee", ValueFromAmount(pool.m_opts.min_relay_feerate.GetFeePerK()));
+ - ]
1120 [ + - + - : 2 : ret.pushKV("incrementalrelayfee", ValueFromAmount(pool.m_opts.incremental_relay_feerate.GetFeePerK()));
+ - ]
1121 [ + - + - : 2 : ret.pushKV("unbroadcastcount", pool.GetUnbroadcastTxs().size());
+ - + - ]
1122 [ + - + - : 2 : ret.pushKV("permitbaremultisig", pool.m_opts.permit_bare_multisig);
+ - ]
1123 [ + - + - : 3 : ret.pushKV("maxdatacarriersize", pool.m_opts.max_datacarrier_bytes.value_or(0));
+ - + - ]
1124 [ + - + - : 2 : ret.pushKV("limitclustercount", pool.m_opts.limits.cluster_count);
+ - ]
1125 [ + - + - : 2 : ret.pushKV("limitclustersize", pool.m_opts.limits.cluster_size_vbytes);
+ - ]
1126 [ + - + - : 2 : ret.pushKV("optimal", pool.m_txgraph->DoWork(0)); // 0 work is a quick check for known optimality
+ - ]
1127 [ + - + - : 1 : if (IsDeprecatedRPCEnabled("fullrbf")) {
- + ]
1128 [ # # # # : 0 : ret.pushKV("fullrbf", true);
# # ]
1129 : : }
1130 [ + - ]: 1 : return ret;
1131 : 1 : }
1132 : :
1133 : 73 : static RPCMethod getmempoolinfo()
1134 : : {
1135 : 73 : return RPCMethod{"getmempoolinfo",
1136 [ + - ]: 146 : "Returns details on the active state of the TX memory pool.",
1137 : : {},
1138 [ + - ]: 146 : RPCResult{
1139 [ + - ]: 146 : RPCResult::Type::OBJ, "", "",
1140 : 73 : [](){
1141 : 73 : std::vector<RPCResult> list = {
1142 [ + - + - ]: 146 : {RPCResult::Type::BOOL, "loaded", "True if the initial load attempt of the persisted mempool finished"},
1143 [ + - + - ]: 146 : {RPCResult::Type::NUM, "size", "Current tx count"},
1144 [ + - + - ]: 146 : {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"},
1145 [ + - + - ]: 146 : {RPCResult::Type::NUM, "usage", "Total memory usage for the mempool"},
1146 [ + - + - ]: 146 : {RPCResult::Type::STR_AMOUNT, "total_fee", "Total fees for the mempool in " + CURRENCY_UNIT + ", ignoring modified fees through prioritisetransaction"},
1147 [ + - + - ]: 146 : {RPCResult::Type::NUM, "maxmempool", "Maximum memory usage for the mempool"},
1148 [ + - + - ]: 146 : {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"},
1149 [ + - + - ]: 146 : {RPCResult::Type::STR_AMOUNT, "minrelaytxfee", "Current minimum relay fee for transactions"},
1150 [ + - + - ]: 146 : {RPCResult::Type::STR_AMOUNT, "incrementalrelayfee", "minimum fee rate increment for mempool limiting or replacement in " + CURRENCY_UNIT + "/kvB"},
1151 [ + - + - ]: 146 : {RPCResult::Type::NUM, "unbroadcastcount", "Current number of transactions that haven't passed initial broadcast yet"},
1152 [ + - + - ]: 146 : {RPCResult::Type::BOOL, "permitbaremultisig", "True if the mempool accepts transactions with bare multisig outputs"},
1153 [ + - + - ]: 146 : {RPCResult::Type::NUM, "maxdatacarriersize", "Maximum number of bytes that can be used by OP_RETURN outputs in the mempool"},
1154 [ + - + - ]: 146 : {RPCResult::Type::NUM, "limitclustercount", "Maximum number of transactions that can be in a cluster (configured by -limitclustercount)"},
1155 [ + - + - ]: 146 : {RPCResult::Type::NUM, "limitclustersize", "Maximum size of a cluster in virtual bytes (configured by -limitclustersize)"},
1156 [ + - + - ]: 146 : {RPCResult::Type::BOOL, "optimal", "If the mempool is in a known-optimal transaction ordering"},
1157 [ + - + + : 2336 : };
- - ]
1158 [ + - + - : 73 : if (IsDeprecatedRPCEnabled("fullrbf")) {
- + ]
1159 [ # # ]: 0 : list.emplace_back(RPCResult::Type::BOOL, "fullrbf", "True if the mempool accepts RBF without replaceability signaling inspection (DEPRECATED)");
1160 : : }
1161 : 73 : return list;
1162 : 2336 : }()
[ + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - -
- ]
1163 [ + - ]: 146 : },
1164 : 73 : RPCExamples{
1165 [ + - + - : 146 : HelpExampleCli("getmempoolinfo", "")
+ - ]
1166 [ + - + - : 292 : + HelpExampleRpc("getmempoolinfo", "")
+ - + - ]
1167 [ + - ]: 73 : },
1168 : 73 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
1169 : : {
1170 : 1 : return MempoolInfoToJSON(EnsureAnyMemPool(request.context));
1171 : : },
1172 [ + - + - ]: 292 : };
1173 : : }
1174 : :
1175 : 68 : static RPCMethod importmempool()
1176 : : {
1177 : 68 : return RPCMethod{
1178 : 68 : "importmempool",
1179 [ + - ]: 136 : "Import a mempool.dat file and attempt to add its contents to the mempool.\n"
1180 : : "Warning: Importing untrusted files is dangerous, especially if metadata from the file is taken over.",
1181 : : {
1182 [ + - + - ]: 136 : {"filepath", RPCArg::Type::STR, RPCArg::Optional::NO, "The mempool file"},
1183 [ + - ]: 136 : {"options",
1184 : : RPCArg::Type::OBJ_NAMED_PARAMS,
1185 : 68 : RPCArg::Optional::OMITTED,
1186 [ + - ]: 136 : "",
1187 : : {
1188 [ + - + - ]: 136 : {"use_current_time", RPCArg::Type::BOOL, RPCArg::Default{true},
1189 [ + - ]: 136 : "Whether to use the current system time or use the entry time metadata from the mempool file.\n"
1190 : : "Warning: Importing untrusted metadata may lead to unexpected issues and undesirable behavior."},
1191 [ + - + - ]: 136 : {"apply_fee_delta_priority", RPCArg::Type::BOOL, RPCArg::Default{false},
1192 [ + - ]: 136 : "Whether to apply the fee delta metadata from the mempool file.\n"
1193 : : "It will be added to any existing fee deltas.\n"
1194 : : "The fee delta can be set by the prioritisetransaction RPC.\n"
1195 : : "Warning: Importing untrusted metadata may lead to unexpected issues and undesirable behavior.\n"
1196 : : "Only set this bool if you understand what it does."},
1197 [ + - + - ]: 136 : {"apply_unbroadcast_set", RPCArg::Type::BOOL, RPCArg::Default{false},
1198 [ + - ]: 136 : "Whether to apply the unbroadcast set metadata from the mempool file.\n"
1199 : : "Warning: Importing untrusted metadata may lead to unexpected issues and undesirable behavior."},
1200 : : },
1201 [ + - ]: 68 : RPCArgOptions{.oneline_description = "options"}},
1202 : : },
1203 [ + - + - : 136 : RPCResult{RPCResult::Type::OBJ, "", "", std::vector<RPCResult>{}},
+ - + - ]
1204 : 204 : RPCExamples{HelpExampleCli("importmempool", "/path/to/mempool.dat") + HelpExampleRpc("importmempool", R"("/path/to/mempool.dat")")},
[ + - + -
+ - + - +
- + - + -
+ - ]
1205 : 68 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue {
1206 : 0 : const NodeContext& node{EnsureAnyNodeContext(request.context)};
1207 : :
1208 : 0 : CTxMemPool& mempool{EnsureMemPool(node)};
1209 : 0 : ChainstateManager& chainman = EnsureChainman(node);
1210 : 0 : Chainstate& chainstate = chainman.ActiveChainstate();
1211 : :
1212 [ # # ]: 0 : if (chainman.IsInitialBlockDownload()) {
1213 [ # # # # ]: 0 : throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Can only import the mempool after the block download and sync is done.");
1214 : : }
1215 : :
1216 : 0 : const fs::path load_path{fs::u8path(self.Arg<std::string_view>("filepath"))};
1217 [ # # # # : 0 : const UniValue& use_current_time{request.params[1]["use_current_time"]};
# # ]
1218 [ # # # # : 0 : const UniValue& apply_fee_delta{request.params[1]["apply_fee_delta_priority"]};
# # ]
1219 [ # # # # : 0 : const UniValue& apply_unbroadcast{request.params[1]["apply_unbroadcast_set"]};
# # ]
1220 [ # # ]: 0 : node::ImportMempoolOptions opts{
1221 [ # # # # ]: 0 : .use_current_time = use_current_time.isNull() ? true : use_current_time.get_bool(),
1222 [ # # # # ]: 0 : .apply_fee_delta_priority = apply_fee_delta.isNull() ? false : apply_fee_delta.get_bool(),
1223 [ # # # # ]: 0 : .apply_unbroadcast_set = apply_unbroadcast.isNull() ? false : apply_unbroadcast.get_bool(),
1224 [ # # # # : 0 : };
# # ]
1225 : :
1226 [ # # # # ]: 0 : if (!node::LoadMempool(mempool, load_path, chainstate, std::move(opts))) {
1227 [ # # # # ]: 0 : throw JSONRPCError(RPC_MISC_ERROR, "Unable to import mempool file, see debug log for details.");
1228 : : }
1229 : :
1230 : 0 : UniValue ret{UniValue::VOBJ};
1231 : 0 : return ret;
1232 : 0 : },
1233 : 748 : };
[ + - + -
+ - + + +
+ - - -
- ]
1234 : 612 : }
[ + - + -
+ - + - +
- - - -
- ]
1235 : :
1236 : 67 : static RPCMethod savemempool()
1237 : : {
1238 : 67 : return RPCMethod{
1239 : 67 : "savemempool",
1240 [ + - ]: 134 : "Dumps the mempool to disk. It will fail until the previous dump is fully loaded.\n",
1241 : : {},
1242 [ + - ]: 134 : RPCResult{
1243 [ + - ]: 134 : RPCResult::Type::OBJ, "", "",
1244 : : {
1245 [ + - + - ]: 134 : {RPCResult::Type::STR, "filename", "the directory and file where the mempool was saved"},
1246 [ + - + - : 268 : }},
+ + - - ]
1247 : 67 : RPCExamples{
1248 [ + - + - : 134 : HelpExampleCli("savemempool", "")
+ - ]
1249 [ + - + - : 268 : + HelpExampleRpc("savemempool", "")
+ - + - ]
1250 [ + - ]: 67 : },
1251 : 67 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
1252 : : {
1253 : 0 : const ArgsManager& args{EnsureAnyArgsman(request.context)};
1254 : 0 : const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
1255 : :
1256 [ # # ]: 0 : if (!mempool.GetLoadTried()) {
1257 [ # # # # ]: 0 : throw JSONRPCError(RPC_MISC_ERROR, "The mempool was not loaded yet");
1258 : : }
1259 : :
1260 : 0 : const fs::path& dump_path = MempoolPath(args);
1261 : :
1262 [ # # # # ]: 0 : if (!DumpMempool(mempool, dump_path)) {
1263 [ # # # # ]: 0 : throw JSONRPCError(RPC_MISC_ERROR, "Unable to dump mempool to disk");
1264 : : }
1265 : :
1266 : 0 : UniValue ret(UniValue::VOBJ);
1267 [ # # # # : 0 : ret.pushKV("filename", dump_path.utf8string());
# # # # ]
1268 : :
1269 : 0 : return ret;
1270 : 0 : },
1271 [ + - + - ]: 268 : };
1272 [ + - ]: 134 : }
1273 : :
1274 : 140 : static std::vector<RPCResult> OrphanDescription()
1275 : : {
1276 : 140 : return {
1277 [ + - + - ]: 280 : RPCResult{RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
1278 [ + - + - ]: 280 : RPCResult{RPCResult::Type::STR_HEX, "wtxid", "The transaction witness hash in hex"},
1279 [ + - + - ]: 280 : RPCResult{RPCResult::Type::NUM, "bytes", "The serialized transaction size in bytes"},
1280 [ + - + - ]: 280 : RPCResult{RPCResult::Type::NUM, "vsize", "(DEPRECATED) use vsize_bip141 instead. The virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted."},
1281 [ + - + - ]: 280 : RPCResult{RPCResult::Type::NUM, "vsize_bip141", "The virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted."},
1282 [ + - + - ]: 280 : RPCResult{RPCResult::Type::NUM, "weight", "The transaction weight as defined in BIP 141."},
1283 [ + - + - ]: 280 : RPCResult{RPCResult::Type::ARR, "from", "",
1284 : : {
1285 [ + - + - ]: 280 : RPCResult{RPCResult::Type::NUM, "peer_id", "Peer ID"},
1286 [ + - + + : 420 : }},
- - ]
1287 [ + - + + : 1400 : };
- - ]
1288 : 2240 : }
[ + - + -
+ - + - +
- + - + -
+ - - - ]
1289 : :
1290 : 0 : static UniValue OrphanToJSON(const node::TxOrphanage::OrphanInfo& orphan)
1291 : : {
1292 : 0 : UniValue o(UniValue::VOBJ);
1293 [ # # # # : 0 : o.pushKV("txid", orphan.tx->GetHash().ToString());
# # # # ]
1294 [ # # # # : 0 : o.pushKV("wtxid", orphan.tx->GetWitnessHash().ToString());
# # # # ]
1295 [ # # # # : 0 : o.pushKV("bytes", orphan.tx->ComputeTotalSize());
# # # # ]
1296 [ # # # # : 0 : o.pushKV("vsize", GetVirtualTransactionSize(*orphan.tx));
# # # # ]
1297 [ # # # # : 0 : o.pushKV("vsize_bip141", GetVirtualTransactionSize(*orphan.tx));
# # # # ]
1298 [ # # # # : 0 : o.pushKV("weight", GetTransactionWeight(*orphan.tx));
# # ]
1299 : 0 : UniValue from(UniValue::VARR);
1300 [ # # ]: 0 : for (const auto fromPeer: orphan.announcers) {
1301 [ # # # # ]: 0 : from.push_back(fromPeer);
1302 : : }
1303 [ # # # # : 0 : o.pushKV("from", from);
# # ]
1304 : 0 : return o;
1305 : 0 : }
1306 : :
1307 : 70 : static RPCMethod getorphantxs()
1308 : : {
1309 : 70 : return RPCMethod{
1310 : 70 : "getorphantxs",
1311 [ + - ]: 140 : "Shows transactions in the tx orphanage.\n"
1312 : : "\nEXPERIMENTAL warning: this call may be changed in future releases.\n",
1313 : : {
1314 [ + - + - : 210 : {"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",
+ - ]
1315 [ + - ]: 140 : RPCArgOptions{.skip_type_check = true}},
1316 : : },
1317 : : {
1318 [ + - ]: 70 : RPCResult{"for verbose = 0",
1319 [ + - + - ]: 140 : RPCResult::Type::ARR, "", "",
1320 : : {
1321 [ + - + - ]: 140 : {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
1322 [ + - + + : 280 : }},
- - ]
1323 [ + - ]: 140 : RPCResult{"for verbose = 1",
1324 [ + - + - ]: 140 : RPCResult::Type::ARR, "", "",
1325 : : {
1326 [ + - + - : 140 : {RPCResult::Type::OBJ, "", "", OrphanDescription()},
+ - ]
1327 [ + - + + : 210 : }},
- - ]
1328 [ + - ]: 140 : RPCResult{"for verbose = 2",
1329 [ + - + - ]: 140 : RPCResult::Type::ARR, "", "",
1330 : : {
1331 [ + - + - ]: 140 : {RPCResult::Type::OBJ, "", "",
1332 [ + - + - : 350 : Cat<std::vector<RPCResult>>(
+ + - - ]
1333 [ + - ]: 140 : OrphanDescription(),
1334 [ + - + - ]: 140 : {{RPCResult::Type::STR_HEX, "hex", "The serialized, hex-encoded transaction data"}}
1335 : : )
1336 : : },
1337 [ + - + + : 210 : }},
- - ]
1338 : : },
1339 : 70 : RPCExamples{
1340 [ + - + - : 140 : HelpExampleCli("getorphantxs", "2")
+ - ]
1341 [ + - + - : 280 : + HelpExampleRpc("getorphantxs", "2")
+ - ]
1342 [ + - ]: 70 : },
1343 : 70 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
1344 : : {
1345 : 8 : const NodeContext& node = EnsureAnyNodeContext(request.context);
1346 : 8 : PeerManager& peerman = EnsurePeerman(node);
1347 : 8 : std::vector<node::TxOrphanage::OrphanInfo> orphanage = peerman.GetOrphanTransactions();
1348 : :
1349 [ + - + + ]: 8 : int verbosity{ParseVerbosity(request.params[0], /*default_verbosity=*/0, /*allow_bool=*/false)};
1350 : :
1351 : 2 : UniValue ret(UniValue::VARR);
1352 : :
1353 [ + + ]: 2 : if (verbosity == 0) {
1354 [ - + ]: 1 : for (auto const& orphan : orphanage) {
1355 [ # # # # : 0 : ret.push_back(orphan.tx->GetHash().ToString());
# # ]
1356 : : }
1357 [ - + ]: 1 : } else if (verbosity == 1) {
1358 [ # # ]: 0 : for (auto const& orphan : orphanage) {
1359 [ # # # # ]: 0 : ret.push_back(OrphanToJSON(orphan));
1360 : : }
1361 [ - + ]: 1 : } else if (verbosity == 2) {
1362 [ # # ]: 0 : for (auto const& orphan : orphanage) {
1363 [ # # ]: 0 : UniValue o{OrphanToJSON(orphan)};
1364 [ # # # # : 0 : o.pushKV("hex", EncodeHexTx(*orphan.tx));
# # # # ]
1365 [ # # # # ]: 0 : ret.push_back(o);
1366 : 0 : }
1367 : : } else {
1368 [ + - + - : 2 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid verbosity value " + ToString(verbosity));
+ - ]
1369 : : }
1370 : :
1371 : 1 : return ret;
1372 : 9 : },
1373 : 770 : };
[ + - + -
+ - + - +
+ + + - -
- - ]
1374 : 1050 : }
[ + - + -
+ - + - +
- + - + -
+ - - - ]
1375 : :
1376 : 306 : static RPCMethod submitpackage()
1377 : : {
1378 : 306 : return RPCMethod{"submitpackage",
1379 [ + - ]: 612 : "Submit a package of raw transactions (serialized, hex-encoded) to local node.\n"
1380 : : "The package will be validated according to consensus and mempool policy rules. If any transaction passes, it will be accepted to mempool.\n"
1381 : : "This RPC is experimental and the interface may be unstable. Refer to doc/policy/packages.md for documentation on package policies.\n"
1382 : : "Warning: successful submission does not mean the transactions will propagate throughout the network.\n"
1383 : : ,
1384 : : {
1385 [ + - + - ]: 612 : {"package", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of raw transactions.\n"
1386 : : "The package must consist of a transaction with (some, all, or none of) its unconfirmed parents. A single transaction is permitted.\n"
1387 : : "None of the parents may depend on each other. Parents that are already in mempool do not need to be present in the package.\n"
1388 : : "The package must be topologically sorted, with the child being the last element in the array if there are multiple elements.",
1389 : : {
1390 [ + - + - ]: 612 : {"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
1391 : : },
1392 : : },
1393 [ + - + - : 918 : {"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},
+ - ]
1394 [ + - ]: 612 : "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
1395 : 306 : "/kvB.\nFee rates larger than 1BTC/kvB are rejected.\nSet to 0 to accept any fee rate."},
1396 [ + - + - : 612 : {"maxburnamount", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_BURN_AMOUNT)},
+ - ]
1397 [ + - ]: 612 : "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"
1398 : : "If burning funds through unspendable outputs is desired, increase this value.\n"
1399 : 306 : "This check is based on heuristics and does not guarantee spendability of outputs.\n"
1400 : : },
1401 : : },
1402 [ + - ]: 612 : RPCResult{
1403 [ + - + - ]: 612 : RPCResult::Type::OBJ, "", "",
1404 : : {
1405 [ + - + - ]: 612 : {RPCResult::Type::STR, "package_msg", "The transaction package result message. \"success\" indicates all transactions were accepted into or are already in the mempool."},
1406 [ + - + - ]: 612 : {RPCResult::Type::OBJ_DYN, "tx-results", "The transaction results keyed by wtxid. An entry is returned for every submitted wtxid.",
1407 : : {
1408 [ + - + - ]: 612 : {RPCResult::Type::OBJ, "wtxid", "transaction wtxid", {
1409 [ + - + - ]: 612 : {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
1410 [ + - + - ]: 612 : {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."},
1411 [ + - + - ]: 612 : {RPCResult::Type::NUM, "vsize_adjusted", /*optional=*/true, "Maximum of sigop-adjusted size (-bytespersigop) and virtual transaction size as defined in BIP 141."},
1412 [ + - + - ]: 612 : {RPCResult::Type::NUM, "vsize", /*optional=*/true, "(DEPRECATED) Was previously erroneously described as the BIP 141 vsize, but is actually sigops-adjusted vsize.\n"
1413 : : "Use vsize_bip141 to actually get that behavior or switch to the explicit vsize_adjusted for retained behavior."},
1414 [ + - + - ]: 612 : {RPCResult::Type::NUM, "vsize_bip141", /*optional=*/true, "Virtual transaction size as defined in BIP 141."},
1415 [ + - + - ]: 612 : {RPCResult::Type::OBJ, "fees", /*optional=*/true, "Transaction fees", {
1416 [ + - + - ]: 612 : {RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT},
1417 [ + - + - ]: 612 : {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."},
1418 [ + - + - ]: 612 : {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.",
1419 [ + - + - ]: 612 : {{RPCResult::Type::STR_HEX, "", "transaction wtxid in hex"},
1420 : : }},
1421 : : }},
1422 [ + - + - ]: 612 : {RPCResult::Type::STR, "error", /*optional=*/true, "Error string if rejected from mempool, or \"package-not-validated\" when the package aborts before any per-tx processing."},
1423 : : }}
1424 : : }},
1425 [ + - + - ]: 612 : {RPCResult::Type::ARR, "replaced-transactions", /*optional=*/true, "List of txids of replaced transactions",
1426 : : {
1427 [ + - + - ]: 612 : {RPCResult::Type::STR_HEX, "", "The transaction id"},
1428 : : }},
1429 : : },
1430 : 11934 : },
[ + - + -
+ - + - +
- + - + -
+ + + + +
+ + + + +
+ + - - -
- - - - -
- - - - ]
1431 : 306 : RPCExamples{
1432 [ + - + - : 612 : HelpExampleRpc("submitpackage", R"(["raw-parent-tx-1", "raw-parent-tx-2", "raw-child-tx"])") +
+ - ]
1433 [ + - + - : 918 : HelpExampleCli("submitpackage", R"('["raw-tx-without-unconfirmed-parents"]')")
+ - + - ]
1434 [ + - ]: 306 : },
1435 : 306 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
1436 : : {
1437 : 117 : const UniValue raw_transactions = request.params[0].get_array();
1438 [ - + + + : 117 : if (raw_transactions.empty() || raw_transactions.size() > MAX_PACKAGE_COUNT) {
+ + ]
1439 : 3 : throw JSONRPCError(RPC_INVALID_PARAMETER,
1440 [ + - + - : 9 : "Array must contain between 1 and " + ToString(MAX_PACKAGE_COUNT) + " transactions.");
+ - ]
1441 : : }
1442 : :
1443 : : // Fee check needs to be run with chainstate and package context
1444 [ + - + + ]: 114 : const CFeeRate max_raw_tx_fee_rate{ParseFeeRate(self.Arg<UniValue>("maxfeerate"))};
1445 [ + + ]: 113 : std::optional<CFeeRate> client_maxfeerate{max_raw_tx_fee_rate};
1446 : : // 0-value is special; it's mapped to no sanity check
1447 [ + + ]: 113 : if (max_raw_tx_fee_rate == CFeeRate(0)) {
1448 : 1 : client_maxfeerate = std::nullopt;
1449 : : }
1450 : :
1451 : : // Burn sanity check is run with no context
1452 [ + - + + : 113 : const CAmount max_burn_amount = request.params[2].isNull() ? 0 : AmountFromValue(request.params[2]);
+ - + + ]
1453 : :
1454 : 112 : std::vector<CTransactionRef> txns;
1455 [ - + + - ]: 112 : txns.reserve(raw_transactions.size());
1456 [ + - + + ]: 246 : for (const auto& rawtx : raw_transactions.getValues()) {
1457 [ + - ]: 170 : CMutableTransaction mtx;
1458 [ + + + - : 170 : if (!DecodeHexTx(mtx, rawtx.get_str())) {
+ + ]
1459 : 5 : throw JSONRPCError(RPC_DESERIALIZATION_ERROR,
1460 [ + - + - : 15 : "TX decode failed: " + rawtx.get_str() + " Make sure the tx has at least one input.");
+ - ]
1461 : : }
1462 : :
1463 [ + + ]: 1073 : for (const auto& out : mtx.vout) {
1464 [ + + + - : 939 : if((out.scriptPubKey.IsUnspendable() || !out.scriptPubKey.HasValidOps()) && out.nValue > max_burn_amount) {
+ + + + ]
1465 [ + - + - ]: 12 : throw JSONRPCTransactionError(TransactionError::MAX_BURN_EXCEEDED);
1466 : : }
1467 : : }
1468 : :
1469 [ + - + - ]: 402 : txns.emplace_back(MakeTransactionRef(std::move(mtx)));
1470 : 170 : }
1471 [ + - ]: 76 : CHECK_NONFATAL(!txns.empty());
1472 [ - + + + : 76 : if (txns.size() > 1 && !IsChildWithParentsTree(txns)) {
+ - - + ]
1473 [ + - + - ]: 34 : throw JSONRPCTransactionError(TransactionError::INVALID_PACKAGE, "package topology disallowed. not child-with-parents or parents depend on each other.");
1474 : : }
1475 : :
1476 [ + - ]: 59 : NodeContext& node = EnsureAnyNodeContext(request.context);
1477 [ + - ]: 59 : CTxMemPool& mempool = EnsureMemPool(node);
1478 [ + - + - ]: 59 : Chainstate& chainstate = EnsureChainman(node).ActiveChainstate();
1479 [ + - + - ]: 177 : const auto package_result = WITH_LOCK(::cs_main, return ProcessNewPackage(chainstate, mempool, txns, /*test_accept=*/ false, client_maxfeerate));
1480 : :
1481 [ + - ]: 59 : std::string package_msg = "success";
1482 : :
1483 : : // First catch package-wide errors, continue if we can
1484 [ - - + - ]: 59 : switch(package_result.m_state.GetResult()) {
1485 : 0 : case PackageValidationResult::PCKG_RESULT_UNSET:
1486 : 0 : {
1487 : : // Belt-and-suspenders check; everything should be successful here
1488 [ # # # # ]: 0 : CHECK_NONFATAL(package_result.m_tx_results.size() == txns.size());
1489 [ # # ]: 0 : for (const auto& tx : txns) {
1490 [ # # # # ]: 0 : CHECK_NONFATAL(mempool.exists(tx->GetHash()));
1491 : : }
1492 : : break;
1493 : : }
1494 : 0 : case PackageValidationResult::PCKG_MEMPOOL_ERROR:
1495 : 0 : {
1496 : : // This only happens with internal bug; user should stop and report
1497 : 0 : throw JSONRPCTransactionError(TransactionError::MEMPOOL_ERROR,
1498 [ # # # # ]: 0 : package_result.m_state.GetRejectReason());
1499 : : }
1500 : 59 : case PackageValidationResult::PCKG_POLICY:
1501 : 59 : case PackageValidationResult::PCKG_TX:
1502 : 59 : {
1503 : : // Package-wide error we want to return, but we also want to return individual responses
1504 [ + - ]: 59 : package_msg = package_result.m_state.ToString();
1505 [ - + + + : 60 : CHECK_NONFATAL(package_result.m_tx_results.size() == txns.size() ||
+ - + - ]
1506 : : package_result.m_tx_results.empty());
1507 : : break;
1508 : : }
1509 : : }
1510 : :
1511 : 59 : size_t num_broadcast{0};
1512 [ + + ]: 118 : for (const auto& tx : txns) {
1513 : : // We don't want to re-submit the txn for validation in BroadcastTransaction
1514 [ + - + - ]: 59 : if (!mempool.exists(tx->GetHash())) {
1515 : 59 : continue;
1516 : : }
1517 : :
1518 : : // We do not expect an error here; we are only broadcasting things already/still in mempool
1519 [ # # ]: 0 : std::string err_string;
1520 [ # # # # : 0 : const auto err = BroadcastTransaction(node, tx, err_string, /*max_tx_fee=*/0, /*max_tx_fee_rate=*/CFeeRate(0), /*broadcast_method=*/node::TxBroadcast::MEMPOOL_AND_BROADCAST_TO_ALL, /*wait_callback=*/true);
# # ]
1521 [ # # ]: 0 : if (err != TransactionError::OK) {
1522 : 0 : throw JSONRPCTransactionError(err,
1523 [ # # ]: 0 : strprintf("transaction broadcast failed: %s (%d transactions were broadcast successfully)",
1524 [ # # ]: 0 : err_string, num_broadcast));
1525 : : }
1526 : 0 : num_broadcast++;
1527 : 0 : }
1528 : :
1529 : 59 : UniValue rpc_result{UniValue::VOBJ};
1530 [ + - + - : 118 : rpc_result.pushKV("package_msg", package_msg);
+ - ]
1531 : 118 : UniValue tx_result_map{UniValue::VOBJ};
1532 : 118 : std::set<Txid> replaced_txids;
1533 [ + + ]: 118 : for (const auto& tx : txns) {
1534 : 59 : UniValue result_inner{UniValue::VOBJ};
1535 [ + - + - : 118 : result_inner.pushKV("txid", tx->GetHash().GetHex());
+ - + - ]
1536 [ + - ]: 59 : const auto wtxid_hex = tx->GetWitnessHash().GetHex();
1537 : 59 : auto it = package_result.m_tx_results.find(tx->GetWitnessHash());
1538 [ + + ]: 59 : if (it == package_result.m_tx_results.end()) {
1539 : : // No per-tx result for this wtxid
1540 : : // Current invariant: per-tx results are all-or-none (every member or empty on package abort).
1541 : : // If any exist yet this one is missing, it's an unexpected partial map.
1542 [ + - ]: 1 : CHECK_NONFATAL(package_result.m_tx_results.empty());
1543 [ + - + - : 2 : result_inner.pushKV("error", "package-not-validated");
+ - ]
1544 [ - + + - ]: 3 : tx_result_map.pushKV(wtxid_hex, std::move(result_inner));
1545 : 1 : continue;
1546 : : }
1547 [ - + - - ]: 58 : const auto& tx_result = it->second;
1548 [ - + - - ]: 58 : switch(it->second.m_result_type) {
1549 : 0 : case MempoolAcceptResult::ResultType::DIFFERENT_WITNESS:
1550 [ # # # # : 0 : result_inner.pushKV("other-wtxid", it->second.m_other_wtxid.value().GetHex());
# # # # #
# ]
1551 : 0 : break;
1552 : 58 : case MempoolAcceptResult::ResultType::INVALID:
1553 [ + - + - : 116 : result_inner.pushKV("error", it->second.m_state.ToString());
+ - + - ]
1554 : 58 : break;
1555 : 0 : case MempoolAcceptResult::ResultType::VALID:
1556 : 0 : case MempoolAcceptResult::ResultType::MEMPOOL_ENTRY:
1557 [ # # # # : 0 : result_inner.pushKV("vsize_adjusted", it->second.m_vsize.value());
# # # # ]
1558 [ # # # # : 0 : result_inner.pushKV("vsize", it->second.m_vsize.value());
# # # # ]
1559 [ # # # # : 0 : result_inner.pushKV("vsize_bip141", GetVirtualTransactionSize(*tx));
# # # # ]
1560 : 0 : UniValue fees(UniValue::VOBJ);
1561 [ # # # # : 0 : fees.pushKV("base", ValueFromAmount(it->second.m_base_fees.value()));
# # # # ]
1562 [ # # ]: 0 : if (tx_result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
1563 : : // Effective feerate is not provided for MEMPOOL_ENTRY transactions even
1564 : : // though modified fees is known, because it is unknown whether package
1565 : : // feerate was used when it was originally submitted.
1566 [ # # # # : 0 : fees.pushKV("effective-feerate", ValueFromAmount(tx_result.m_effective_feerate.value().GetFeePerK()));
# # # # ]
1567 : 0 : UniValue effective_includes_res(UniValue::VARR);
1568 [ # # # # ]: 0 : for (const auto& wtxid : tx_result.m_wtxids_fee_calculations.value()) {
1569 [ # # # # : 0 : effective_includes_res.push_back(wtxid.ToString());
# # ]
1570 : : }
1571 [ # # # # ]: 0 : fees.pushKV("effective-includes", std::move(effective_includes_res));
1572 : 0 : }
1573 [ # # # # ]: 0 : result_inner.pushKV("fees", std::move(fees));
1574 [ # # ]: 0 : for (const auto& ptx : it->second.m_replaced_transactions) {
1575 [ # # ]: 0 : replaced_txids.insert(ptx->GetHash());
1576 : : }
1577 : 0 : break;
1578 : : }
1579 [ - + + - ]: 174 : tx_result_map.pushKV(wtxid_hex, std::move(result_inner));
1580 : 59 : }
1581 [ + - + - ]: 118 : rpc_result.pushKV("tx-results", std::move(tx_result_map));
1582 : 118 : UniValue replaced_list(UniValue::VARR);
1583 [ - - - - : 59 : for (const auto& txid : replaced_txids) replaced_list.push_back(txid.ToString());
- - - + ]
1584 [ + - + - ]: 118 : rpc_result.pushKV("replaced-transactions", std::move(replaced_list));
1585 : 118 : return rpc_result;
1586 : 170 : },
1587 : 3060 : };
[ + - + -
+ - + + +
+ - - -
- ]
1588 : 12240 : }
[ + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- - - - -
- - - - ]
1589 : :
1590 : 32 : void RegisterMempoolRPCCommands(CRPCTable& t)
1591 : : {
1592 : 32 : static const CRPCCommand commands[]{
1593 [ + - ]: 60 : {"rawtransactions", &sendrawtransaction},
1594 [ + - ]: 60 : {"rawtransactions", &getprivatebroadcastinfo},
1595 [ + - ]: 60 : {"rawtransactions", &abortprivatebroadcast},
1596 [ + - ]: 60 : {"rawtransactions", &testmempoolaccept},
1597 [ + - ]: 60 : {"blockchain", &getmempoolancestors},
1598 [ + - ]: 60 : {"blockchain", &getmempooldescendants},
1599 [ + - ]: 60 : {"blockchain", &getmempoolentry},
1600 [ + - ]: 60 : {"blockchain", &getmempoolcluster},
1601 [ + - ]: 60 : {"blockchain", &gettxspendingprevout},
1602 [ + - ]: 60 : {"blockchain", &getmempoolinfo},
1603 [ + - ]: 60 : {"hidden", &getmempoolfeeratediagram},
1604 [ + - ]: 60 : {"blockchain", &getrawmempool},
1605 [ + - ]: 60 : {"blockchain", &importmempool},
1606 [ + - ]: 60 : {"blockchain", &savemempool},
1607 [ + - ]: 60 : {"hidden", &getorphantxs},
1608 [ + - ]: 60 : {"rawtransactions", &submitpackage},
1609 : 512 : };
[ + + + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - - - ]
1610 [ + + ]: 544 : for (const auto& c : commands) {
1611 : 512 : t.appendCommand(c.name, &c);
1612 : : }
1613 : 32 : }
|