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