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