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