Skip to content

Commit 3d2f24b

Browse files
author
MarcoFalke
committed
Merge bitcoin/bitcoin#24656: refactor: Move mempool RPCs to rpc/mempool
fac5a51 Move mempool RPCs to rpc/mempool (MarcoFalke) fa0f666 style: Add static keyword where possible in rpc/mempool (MarcoFalke) Pull request description: This moves the remaining mempool RPCs to `rpc/mempool`. Previously all mempool RPCs from the `blockchain` category have been moved. This patch moves the ones from the `rawtransactions` category. In the future, as a follow-up to this refactoring patch, it could be considered whether a new `mempool` category should be introduced. Beside a clearer code organization, this pull request should also reduce the compile time and space of the `rawtransactions.cpp` file. ACKs for top commit: promag: Code review ACK fac5a51. Tree-SHA512: 5578b894b68d0595869a9b03ed8dceebe3366f73dec5f090ccc36ff4002b1bc4d58af77546c2d71537c1be03694d9a28c4b1bfbb3569560997879293c5c0301e
2 parents 2f0f056 + fac5a51 commit 3d2f24b

File tree

2 files changed

+221
-216
lines changed

2 files changed

+221
-216
lines changed

src/rpc/mempool.cpp

Lines changed: 221 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,219 @@
1414
#include <rpc/util.h>
1515
#include <txmempool.h>
1616
#include <univalue.h>
17+
#include <util/moneystr.h>
1718
#include <validation.h>
1819

19-
static std::vector<RPCResult> MempoolEntryDescription() { return {
20+
using node::DEFAULT_MAX_RAW_TX_FEE_RATE;
21+
using node::NodeContext;
22+
23+
static RPCHelpMan sendrawtransaction()
24+
{
25+
return RPCHelpMan{"sendrawtransaction",
26+
"\nSubmit a raw transaction (serialized, hex-encoded) to local node and network.\n"
27+
"\nThe transaction will be sent unconditionally to all peers, so using sendrawtransaction\n"
28+
"for manual rebroadcast may degrade privacy by leaking the transaction's origin, as\n"
29+
"nodes will normally not rebroadcast non-wallet transactions already in their mempool.\n"
30+
"\nA specific exception, RPC_TRANSACTION_ALREADY_IN_CHAIN, may throw if the transaction cannot be added to the mempool.\n"
31+
"\nRelated RPCs: createrawtransaction, signrawtransactionwithkey\n",
32+
{
33+
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
34+
{"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},
35+
"Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
36+
"/kvB.\nSet to 0 to accept any fee rate.\n"},
37+
},
38+
RPCResult{
39+
RPCResult::Type::STR_HEX, "", "The transaction hash in hex"
40+
},
41+
RPCExamples{
42+
"\nCreate a transaction\n"
43+
+ HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") +
44+
"Sign the transaction, and get back the hex\n"
45+
+ HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") +
46+
"\nSend the transaction (signed hex)\n"
47+
+ HelpExampleCli("sendrawtransaction", "\"signedhex\"") +
48+
"\nAs a JSON-RPC call\n"
49+
+ HelpExampleRpc("sendrawtransaction", "\"signedhex\"")
50+
},
51+
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
52+
{
53+
RPCTypeCheck(request.params, {
54+
UniValue::VSTR,
55+
UniValueType(), // VNUM or VSTR, checked inside AmountFromValue()
56+
});
57+
58+
CMutableTransaction mtx;
59+
if (!DecodeHexTx(mtx, request.params[0].get_str())) {
60+
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed. Make sure the tx has at least one input.");
61+
}
62+
CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
63+
64+
const CFeeRate max_raw_tx_fee_rate = request.params[1].isNull() ?
65+
DEFAULT_MAX_RAW_TX_FEE_RATE :
66+
CFeeRate(AmountFromValue(request.params[1]));
67+
68+
int64_t virtual_size = GetVirtualTransactionSize(*tx);
69+
CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size);
70+
71+
std::string err_string;
72+
AssertLockNotHeld(cs_main);
73+
NodeContext& node = EnsureAnyNodeContext(request.context);
74+
const TransactionError err = BroadcastTransaction(node, tx, err_string, max_raw_tx_fee, /*relay*/ true, /*wait_callback*/ true);
75+
if (TransactionError::OK != err) {
76+
throw JSONRPCTransactionError(err, err_string);
77+
}
78+
79+
return tx->GetHash().GetHex();
80+
},
81+
};
82+
}
83+
84+
static RPCHelpMan testmempoolaccept()
85+
{
86+
return RPCHelpMan{"testmempoolaccept",
87+
"\nReturns result of mempool acceptance tests indicating if raw transaction(s) (serialized, hex-encoded) would be accepted by mempool.\n"
88+
"\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"
89+
"\nIf one transaction fails, other transactions may not be fully validated (the 'allowed' key will be blank).\n"
90+
"\nThe maximum number of transactions allowed is " + ToString(MAX_PACKAGE_COUNT) + ".\n"
91+
"\nThis checks if transactions violate the consensus or policy rules.\n"
92+
"\nSee sendrawtransaction call.\n",
93+
{
94+
{"rawtxs", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of hex strings of raw transactions.",
95+
{
96+
{"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
97+
},
98+
},
99+
{"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},
100+
"Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + "/kvB\n"},
101+
},
102+
RPCResult{
103+
RPCResult::Type::ARR, "", "The result of the mempool acceptance test for each raw transaction in the input array.\n"
104+
"Returns results for each transaction in the same order they were passed in.\n"
105+
"Transactions that cannot be fully validated due to failures in other transactions will not contain an 'allowed' result.\n",
106+
{
107+
{RPCResult::Type::OBJ, "", "",
108+
{
109+
{RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
110+
{RPCResult::Type::STR_HEX, "wtxid", "The transaction witness hash in hex"},
111+
{RPCResult::Type::STR, "package-error", /*optional=*/true, "Package validation error, if any (only possible if rawtxs had more than 1 transaction)."},
112+
{RPCResult::Type::BOOL, "allowed", /*optional=*/true, "Whether this tx would be accepted to the mempool and pass client-specified maxfeerate. "
113+
"If not present, the tx was not fully validated due to a failure in another tx in the list."},
114+
{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)"},
115+
{RPCResult::Type::OBJ, "fees", /*optional=*/true, "Transaction fees (only present if 'allowed' is true)",
116+
{
117+
{RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT},
118+
}},
119+
{RPCResult::Type::STR, "reject-reason", /*optional=*/true, "Rejection string (only present when 'allowed' is false)"},
120+
}},
121+
}
122+
},
123+
RPCExamples{
124+
"\nCreate a transaction\n"
125+
+ HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") +
126+
"Sign the transaction, and get back the hex\n"
127+
+ HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") +
128+
"\nTest acceptance of the transaction (signed hex)\n"
129+
+ HelpExampleCli("testmempoolaccept", R"('["signedhex"]')") +
130+
"\nAs a JSON-RPC call\n"
131+
+ HelpExampleRpc("testmempoolaccept", "[\"signedhex\"]")
132+
},
133+
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
134+
{
135+
RPCTypeCheck(request.params, {
136+
UniValue::VARR,
137+
UniValueType(), // VNUM or VSTR, checked inside AmountFromValue()
138+
});
139+
const UniValue raw_transactions = request.params[0].get_array();
140+
if (raw_transactions.size() < 1 || raw_transactions.size() > MAX_PACKAGE_COUNT) {
141+
throw JSONRPCError(RPC_INVALID_PARAMETER,
142+
"Array must contain between 1 and " + ToString(MAX_PACKAGE_COUNT) + " transactions.");
143+
}
144+
145+
const CFeeRate max_raw_tx_fee_rate = request.params[1].isNull() ?
146+
DEFAULT_MAX_RAW_TX_FEE_RATE :
147+
CFeeRate(AmountFromValue(request.params[1]));
148+
149+
std::vector<CTransactionRef> txns;
150+
txns.reserve(raw_transactions.size());
151+
for (const auto& rawtx : raw_transactions.getValues()) {
152+
CMutableTransaction mtx;
153+
if (!DecodeHexTx(mtx, rawtx.get_str())) {
154+
throw JSONRPCError(RPC_DESERIALIZATION_ERROR,
155+
"TX decode failed: " + rawtx.get_str() + " Make sure the tx has at least one input.");
156+
}
157+
txns.emplace_back(MakeTransactionRef(std::move(mtx)));
158+
}
159+
160+
NodeContext& node = EnsureAnyNodeContext(request.context);
161+
CTxMemPool& mempool = EnsureMemPool(node);
162+
ChainstateManager& chainman = EnsureChainman(node);
163+
CChainState& chainstate = chainman.ActiveChainstate();
164+
const PackageMempoolAcceptResult package_result = [&] {
165+
LOCK(::cs_main);
166+
if (txns.size() > 1) return ProcessNewPackage(chainstate, mempool, txns, /* test_accept */ true);
167+
return PackageMempoolAcceptResult(txns[0]->GetWitnessHash(),
168+
chainman.ProcessTransaction(txns[0], /*test_accept=*/true));
169+
}();
170+
171+
UniValue rpc_result(UniValue::VARR);
172+
// We will check transaction fees while we iterate through txns in order. If any transaction fee
173+
// exceeds maxfeerate, we will leave the rest of the validation results blank, because it
174+
// doesn't make sense to return a validation result for a transaction if its ancestor(s) would
175+
// not be submitted.
176+
bool exit_early{false};
177+
for (const auto& tx : txns) {
178+
UniValue result_inner(UniValue::VOBJ);
179+
result_inner.pushKV("txid", tx->GetHash().GetHex());
180+
result_inner.pushKV("wtxid", tx->GetWitnessHash().GetHex());
181+
if (package_result.m_state.GetResult() == PackageValidationResult::PCKG_POLICY) {
182+
result_inner.pushKV("package-error", package_result.m_state.GetRejectReason());
183+
}
184+
auto it = package_result.m_tx_results.find(tx->GetWitnessHash());
185+
if (exit_early || it == package_result.m_tx_results.end()) {
186+
// Validation unfinished. Just return the txid and wtxid.
187+
rpc_result.push_back(result_inner);
188+
continue;
189+
}
190+
const auto& tx_result = it->second;
191+
// Package testmempoolaccept doesn't allow transactions to already be in the mempool.
192+
CHECK_NONFATAL(tx_result.m_result_type != MempoolAcceptResult::ResultType::MEMPOOL_ENTRY);
193+
if (tx_result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
194+
const CAmount fee = tx_result.m_base_fees.value();
195+
// Check that fee does not exceed maximum fee
196+
const int64_t virtual_size = tx_result.m_vsize.value();
197+
const CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size);
198+
if (max_raw_tx_fee && fee > max_raw_tx_fee) {
199+
result_inner.pushKV("allowed", false);
200+
result_inner.pushKV("reject-reason", "max-fee-exceeded");
201+
exit_early = true;
202+
} else {
203+
// Only return the fee and vsize if the transaction would pass ATMP.
204+
// These can be used to calculate the feerate.
205+
result_inner.pushKV("allowed", true);
206+
result_inner.pushKV("vsize", virtual_size);
207+
UniValue fees(UniValue::VOBJ);
208+
fees.pushKV("base", ValueFromAmount(fee));
209+
result_inner.pushKV("fees", fees);
210+
}
211+
} else {
212+
result_inner.pushKV("allowed", false);
213+
const TxValidationState state = tx_result.m_state;
214+
if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
215+
result_inner.pushKV("reject-reason", "missing-inputs");
216+
} else {
217+
result_inner.pushKV("reject-reason", state.GetRejectReason());
218+
}
219+
}
220+
rpc_result.push_back(result_inner);
221+
}
222+
return rpc_result;
223+
},
224+
};
225+
}
226+
227+
static std::vector<RPCResult> MempoolEntryDescription()
228+
{
229+
return {
20230
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."},
21231
RPCResult{RPCResult::Type::NUM, "weight", "transaction weight as defined in BIP 141."},
22232
RPCResult{RPCResult::Type::STR_AMOUNT, "fee", /*optional=*/true,
@@ -50,7 +260,8 @@ static std::vector<RPCResult> MempoolEntryDescription() { return {
50260
{RPCResult{RPCResult::Type::STR_HEX, "transactionid", "child transaction id"}}},
51261
RPCResult{RPCResult::Type::BOOL, "bip125-replaceable", "Whether this transaction could be replaced due to BIP125 (replace-by-fee)"},
52262
RPCResult{RPCResult::Type::BOOL, "unbroadcast", "Whether this transaction is currently unbroadcast (initial broadcast not yet acknowledged by any peers)"},
53-
};}
263+
};
264+
}
54265

55266
static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPoolEntry& e) EXCLUSIVE_LOCKS_REQUIRED(pool.cs)
56267
{
@@ -164,7 +375,7 @@ UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose, bool include_mempoo
164375
}
165376
}
166377

167-
RPCHelpMan getrawmempool()
378+
static RPCHelpMan getrawmempool()
168379
{
169380
return RPCHelpMan{"getrawmempool",
170381
"\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n"
@@ -214,7 +425,7 @@ RPCHelpMan getrawmempool()
214425
};
215426
}
216427

217-
RPCHelpMan getmempoolancestors()
428+
static RPCHelpMan getmempoolancestors()
218429
{
219430
return RPCHelpMan{"getmempoolancestors",
220431
"\nIf txid is in the mempool, returns all in-mempool ancestors.\n",
@@ -278,7 +489,7 @@ RPCHelpMan getmempoolancestors()
278489
};
279490
}
280491

281-
RPCHelpMan getmempooldescendants()
492+
static RPCHelpMan getmempooldescendants()
282493
{
283494
return RPCHelpMan{"getmempooldescendants",
284495
"\nIf txid is in the mempool, returns all in-mempool descendants.\n",
@@ -343,7 +554,7 @@ RPCHelpMan getmempooldescendants()
343554
};
344555
}
345556

346-
RPCHelpMan getmempoolentry()
557+
static RPCHelpMan getmempoolentry()
347558
{
348559
return RPCHelpMan{"getmempoolentry",
349560
"\nReturns mempool data for given transaction\n",
@@ -394,7 +605,7 @@ UniValue MempoolInfoToJSON(const CTxMemPool& pool)
394605
return ret;
395606
}
396607

397-
RPCHelpMan getmempoolinfo()
608+
static RPCHelpMan getmempoolinfo()
398609
{
399610
return RPCHelpMan{"getmempoolinfo",
400611
"\nReturns details on the active state of the TX memory pool.\n",
@@ -423,7 +634,7 @@ RPCHelpMan getmempoolinfo()
423634
};
424635
}
425636

426-
RPCHelpMan savemempool()
637+
static RPCHelpMan savemempool()
427638
{
428639
return RPCHelpMan{"savemempool",
429640
"\nDumps the mempool to disk. It will fail until the previous dump is fully loaded.\n",
@@ -463,6 +674,8 @@ void RegisterMempoolRPCCommands(CRPCTable& t)
463674
static const CRPCCommand commands[]{
464675
// category actor (function)
465676
// -------- ----------------
677+
{"rawtransactions", &sendrawtransaction},
678+
{"rawtransactions", &testmempoolaccept},
466679
{"blockchain", &getmempoolancestors},
467680
{"blockchain", &getmempooldescendants},
468681
{"blockchain", &getmempoolentry},

0 commit comments

Comments
 (0)