Skip to content

Commit 18815b4

Browse files
committed
Merge #11742: rpc: Add testmempoolaccept
b55555d rpc: Add testmempoolaccept (MarcoFalke) Pull request description: To check if a single raw transaction makes it into the current transaction pool, one had to call `sendrawtransaction`. However, on success, this adds the transaction to the mempool with no easy way to undo. The call `testmempoolaccept` is introduced to provide a way to solely check the result without changing the mempool state. Tree-SHA512: 5afd9311190135cee8fc1f229c7d39bf893f1028f29e28d34f70df820198ff97b4bf86b41cbbd6e6c36a5c30073cefa92d541c74a4939c7a2a6fa283dfd41b63
2 parents 821980c + b55555d commit 18815b4

File tree

10 files changed

+399
-14
lines changed

10 files changed

+399
-14
lines changed

src/rpc/client.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
103103
{ "signrawtransactionwithkey", 2, "prevtxs" },
104104
{ "signrawtransactionwithwallet", 1, "prevtxs" },
105105
{ "sendrawtransaction", 1, "allowhighfees" },
106+
{ "testmempoolaccept", 0, "rawtxs" },
107+
{ "testmempoolaccept", 1, "allowhighfees" },
106108
{ "combinerawtransaction", 0, "txs" },
107109
{ "fundrawtransaction", 1, "options" },
108110
{ "fundrawtransaction", 2, "iswitness" },

src/rpc/rawtransaction.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1134,6 +1134,87 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
11341134
return hashTx.GetHex();
11351135
}
11361136

1137+
UniValue testmempoolaccept(const JSONRPCRequest& request)
1138+
{
1139+
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
1140+
throw std::runtime_error(
1141+
// clang-format off
1142+
"testmempoolaccept [\"rawtxs\"] ( allowhighfees )\n"
1143+
"\nReturns if raw transaction (serialized, hex-encoded) would be accepted by mempool.\n"
1144+
"\nThis checks if the transaction violates the consensus or policy rules.\n"
1145+
"\nSee sendrawtransaction call.\n"
1146+
"\nArguments:\n"
1147+
"1. [\"rawtxs\"] (array, required) An array of hex strings of raw transactions.\n"
1148+
" Length must be one for now.\n"
1149+
"2. allowhighfees (boolean, optional, default=false) Allow high fees\n"
1150+
"\nResult:\n"
1151+
"[ (array) The result of the mempool acceptance test for each raw transaction in the input array.\n"
1152+
" Length is exactly one for now.\n"
1153+
" {\n"
1154+
" \"txid\" (string) The transaction hash in hex\n"
1155+
" \"allowed\" (boolean) If the mempool allows this tx to be inserted\n"
1156+
" \"reject-reason\" (string) Rejection string (only present when 'allowed' is false)\n"
1157+
" }\n"
1158+
"]\n"
1159+
"\nExamples:\n"
1160+
"\nCreate a transaction\n"
1161+
+ HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") +
1162+
"Sign the transaction, and get back the hex\n"
1163+
+ HelpExampleCli("signrawtransaction", "\"myhex\"") +
1164+
"\nTest acceptance of the transaction (signed hex)\n"
1165+
+ HelpExampleCli("testmempoolaccept", "\"signedhex\"") +
1166+
"\nAs a json rpc call\n"
1167+
+ HelpExampleRpc("testmempoolaccept", "[\"signedhex\"]")
1168+
// clang-format on
1169+
);
1170+
}
1171+
1172+
ObserveSafeMode();
1173+
1174+
RPCTypeCheck(request.params, {UniValue::VARR, UniValue::VBOOL});
1175+
if (request.params[0].get_array().size() != 1) {
1176+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Array must contain exactly one raw transaction for now");
1177+
}
1178+
1179+
CMutableTransaction mtx;
1180+
if (!DecodeHexTx(mtx, request.params[0].get_array()[0].get_str())) {
1181+
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
1182+
}
1183+
CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
1184+
const uint256& tx_hash = tx->GetHash();
1185+
1186+
CAmount max_raw_tx_fee = ::maxTxFee;
1187+
if (!request.params[1].isNull() && request.params[1].get_bool()) {
1188+
max_raw_tx_fee = 0;
1189+
}
1190+
1191+
UniValue result(UniValue::VARR);
1192+
UniValue result_0(UniValue::VOBJ);
1193+
result_0.pushKV("txid", tx_hash.GetHex());
1194+
1195+
CValidationState state;
1196+
bool missing_inputs;
1197+
bool test_accept_res;
1198+
{
1199+
LOCK(cs_main);
1200+
test_accept_res = AcceptToMemoryPool(mempool, state, std::move(tx), &missing_inputs,
1201+
nullptr /* plTxnReplaced */, false /* bypass_limits */, max_raw_tx_fee, /* test_accpet */ true);
1202+
}
1203+
result_0.pushKV("allowed", test_accept_res);
1204+
if (!test_accept_res) {
1205+
if (state.IsInvalid()) {
1206+
result_0.pushKV("reject-reason", strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason()));
1207+
} else if (missing_inputs) {
1208+
result_0.pushKV("reject-reason", "missing-inputs");
1209+
} else {
1210+
result_0.pushKV("reject-reason", state.GetRejectReason());
1211+
}
1212+
}
1213+
1214+
result.push_back(std::move(result_0));
1215+
return result;
1216+
}
1217+
11371218
static const CRPCCommand commands[] =
11381219
{ // category name actor (function) argNames
11391220
// --------------------- ------------------------ ----------------------- ----------
@@ -1145,6 +1226,7 @@ static const CRPCCommand commands[] =
11451226
{ "rawtransactions", "combinerawtransaction", &combinerawtransaction, {"txs"} },
11461227
{ "rawtransactions", "signrawtransaction", &signrawtransaction, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */
11471228
{ "rawtransactions", "signrawtransactionwithkey", &signrawtransactionwithkey, {"hexstring","privkeys","prevtxs","sighashtype"} },
1229+
{ "rawtransactions", "testmempoolaccept", &testmempoolaccept, {"rawtxs","allowhighfees"} },
11481230

11491231
{ "blockchain", "gettxoutproof", &gettxoutproof, {"txids", "blockhash"} },
11501232
{ "blockchain", "verifytxoutproof", &verifytxoutproof, {"proof"} },

src/txmempool.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
447447
// Also assumes that if an entry is in setDescendants already, then all
448448
// in-mempool descendants of it are already in setDescendants as well, so that we
449449
// can save time by not iterating over those entries.
450-
void CTxMemPool::CalculateDescendants(txiter entryit, setEntries &setDescendants)
450+
void CTxMemPool::CalculateDescendants(txiter entryit, setEntries& setDescendants) const
451451
{
452452
setEntries stage;
453453
if (setDescendants.count(entryit) == 0) {

src/txmempool.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -600,7 +600,7 @@ class CTxMemPool
600600
/** Populate setDescendants with all in-mempool descendants of hash.
601601
* Assumes that setDescendants includes all in-mempool descendants of anything
602602
* already in it. */
603-
void CalculateDescendants(txiter it, setEntries &setDescendants);
603+
void CalculateDescendants(txiter it, setEntries& setDescendants) const;
604604

605605
/** The minimum fee to get into the mempool, which may itself not be enough
606606
* for larger-sized transactions.

src/validation.cpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,7 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationSt
543543

544544
static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx,
545545
bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
546-
bool bypass_limits, const CAmount& nAbsurdFee, std::vector<COutPoint>& coins_to_uncache)
546+
bool bypass_limits, const CAmount& nAbsurdFee, std::vector<COutPoint>& coins_to_uncache, bool test_accept)
547547
{
548548
const CTransaction& tx = *ptx;
549549
const uint256 hash = tx.GetHash();
@@ -935,6 +935,11 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
935935
}
936936
}
937937

938+
if (test_accept) {
939+
// Tx was accepted, but not added
940+
return true;
941+
}
942+
938943
// Remove conflicting transactions from the mempool
939944
for (const CTxMemPool::txiter it : allConflicting)
940945
{
@@ -974,10 +979,10 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
974979
/** (try to) add transaction to memory pool with a specified acceptance time **/
975980
static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx,
976981
bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
977-
bool bypass_limits, const CAmount nAbsurdFee)
982+
bool bypass_limits, const CAmount nAbsurdFee, bool test_accept)
978983
{
979984
std::vector<COutPoint> coins_to_uncache;
980-
bool res = AcceptToMemoryPoolWorker(chainparams, pool, state, tx, pfMissingInputs, nAcceptTime, plTxnReplaced, bypass_limits, nAbsurdFee, coins_to_uncache);
985+
bool res = AcceptToMemoryPoolWorker(chainparams, pool, state, tx, pfMissingInputs, nAcceptTime, plTxnReplaced, bypass_limits, nAbsurdFee, coins_to_uncache, test_accept);
981986
if (!res) {
982987
for (const COutPoint& hashTx : coins_to_uncache)
983988
pcoinsTip->Uncache(hashTx);
@@ -990,10 +995,10 @@ static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPo
990995

991996
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx,
992997
bool* pfMissingInputs, std::list<CTransactionRef>* plTxnReplaced,
993-
bool bypass_limits, const CAmount nAbsurdFee)
998+
bool bypass_limits, const CAmount nAbsurdFee, bool test_accept)
994999
{
9951000
const CChainParams& chainparams = Params();
996-
return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, pfMissingInputs, GetTime(), plTxnReplaced, bypass_limits, nAbsurdFee);
1001+
return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, pfMissingInputs, GetTime(), plTxnReplaced, bypass_limits, nAbsurdFee, test_accept);
9971002
}
9981003

9991004
/**
@@ -4640,7 +4645,8 @@ bool LoadMempool(void)
46404645
if (nTime + nExpiryTimeout > nNow) {
46414646
LOCK(cs_main);
46424647
AcceptToMemoryPoolWithTime(chainparams, mempool, state, tx, nullptr /* pfMissingInputs */, nTime,
4643-
nullptr /* plTxnReplaced */, false /* bypass_limits */, 0 /* nAbsurdFee */);
4648+
nullptr /* plTxnReplaced */, false /* bypass_limits */, 0 /* nAbsurdFee */,
4649+
false /* test_accept */);
46444650
if (state.IsValid()) {
46454651
++count;
46464652
} else {

src/validation.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ void PruneBlockFilesManual(int nManualPruneHeight);
308308
* plTxnReplaced will be appended to with all transactions replaced from mempool **/
309309
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx,
310310
bool* pfMissingInputs, std::list<CTransactionRef>* plTxnReplaced,
311-
bool bypass_limits, const CAmount nAbsurdFee);
311+
bool bypass_limits, const CAmount nAbsurdFee, bool test_accept=false);
312312

313313
/** Convert CValidationState to a human-readable message for logging */
314314
std::string FormatStateMessage(const CValidationState &state);

0 commit comments

Comments
 (0)