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