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