Skip to content

Commit b55555d

Browse files
author
MarcoFalke
committed
rpc: Add testmempoolaccept
1 parent b43aba8 commit b55555d

File tree

10 files changed

+399
-14
lines changed

10 files changed

+399
-14
lines changed

Diff for: src/rpc/client.cpp

+2
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" },

Diff for: src/rpc/rawtransaction.cpp

+82
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"} },

Diff for: src/txmempool.cpp

+1-1
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) {

Diff for: src/txmempool.h

+1-1
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.

Diff for: src/validation.cpp

+12-6
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationSt
542542

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

937+
if (test_accept) {
938+
// Tx was accepted, but not added
939+
return true;
940+
}
941+
937942
// Remove conflicting transactions from the mempool
938943
for (const CTxMemPool::txiter it : allConflicting)
939944
{
@@ -973,10 +978,10 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
973978
/** (try to) add transaction to memory pool with a specified acceptance time **/
974979
static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx,
975980
bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
976-
bool bypass_limits, const CAmount nAbsurdFee)
981+
bool bypass_limits, const CAmount nAbsurdFee, bool test_accept)
977982
{
978983
std::vector<COutPoint> coins_to_uncache;
979-
bool res = AcceptToMemoryPoolWorker(chainparams, pool, state, tx, pfMissingInputs, nAcceptTime, plTxnReplaced, bypass_limits, nAbsurdFee, coins_to_uncache);
984+
bool res = AcceptToMemoryPoolWorker(chainparams, pool, state, tx, pfMissingInputs, nAcceptTime, plTxnReplaced, bypass_limits, nAbsurdFee, coins_to_uncache, test_accept);
980985
if (!res) {
981986
for (const COutPoint& hashTx : coins_to_uncache)
982987
pcoinsTip->Uncache(hashTx);
@@ -989,10 +994,10 @@ static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPo
989994

990995
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx,
991996
bool* pfMissingInputs, std::list<CTransactionRef>* plTxnReplaced,
992-
bool bypass_limits, const CAmount nAbsurdFee)
997+
bool bypass_limits, const CAmount nAbsurdFee, bool test_accept)
993998
{
994999
const CChainParams& chainparams = Params();
995-
return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, pfMissingInputs, GetTime(), plTxnReplaced, bypass_limits, nAbsurdFee);
1000+
return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, pfMissingInputs, GetTime(), plTxnReplaced, bypass_limits, nAbsurdFee, test_accept);
9961001
}
9971002

9981003
/**
@@ -4639,7 +4644,8 @@ bool LoadMempool(void)
46394644
if (nTime + nExpiryTimeout > nNow) {
46404645
LOCK(cs_main);
46414646
AcceptToMemoryPoolWithTime(chainparams, mempool, state, tx, nullptr /* pfMissingInputs */, nTime,
4642-
nullptr /* plTxnReplaced */, false /* bypass_limits */, 0 /* nAbsurdFee */);
4647+
nullptr /* plTxnReplaced */, false /* bypass_limits */, 0 /* nAbsurdFee */,
4648+
false /* test_accept */);
46434649
if (state.IsValid()) {
46444650
++count;
46454651
} else {

Diff for: src/validation.h

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

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

0 commit comments

Comments
 (0)