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