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