Skip to content

Commit f82baf0

Browse files
committed
[refactor] return MempoolAcceptResult
This creates a cleaner interface with ATMP, allows us to make results const, and makes accessing values that don't make sense (e.g. fee when tx is invalid) an error.
1 parent 9db10a5 commit f82baf0

9 files changed

+106
-92
lines changed

src/bench/block_assemble.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,8 @@ static void AssembleBlock(benchmark::Bench& bench)
4848
LOCK(::cs_main); // Required for ::AcceptToMemoryPool.
4949

5050
for (const auto& txr : txs) {
51-
TxValidationState state;
52-
bool ret{::AcceptToMemoryPool(*test_setup.m_node.mempool, state, txr, nullptr /* plTxnReplaced */, false /* bypass_limits */)};
53-
assert(ret);
51+
const MempoolAcceptResult res = ::AcceptToMemoryPool(*test_setup.m_node.mempool, txr, false /* bypass_limits */);
52+
assert(res.m_result_type == MempoolAcceptResult::ResultType::VALID);
5453
}
5554
}
5655

src/net_processing.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2178,10 +2178,10 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
21782178
if (orphan_it == mapOrphanTransactions.end()) continue;
21792179

21802180
const CTransactionRef porphanTx = orphan_it->second.tx;
2181-
TxValidationState state;
2182-
std::list<CTransactionRef> removed_txn;
2181+
const MempoolAcceptResult result = AcceptToMemoryPool(m_mempool, porphanTx, false /* bypass_limits */);
2182+
const TxValidationState& state = result.m_state;
21832183

2184-
if (AcceptToMemoryPool(m_mempool, state, porphanTx, &removed_txn, false /* bypass_limits */)) {
2184+
if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
21852185
LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString());
21862186
RelayTransaction(orphanHash, porphanTx->GetWitnessHash(), m_connman);
21872187
for (unsigned int i = 0; i < porphanTx->vout.size(); i++) {
@@ -2193,7 +2193,7 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
21932193
}
21942194
}
21952195
EraseOrphanTx(orphanHash);
2196-
for (const CTransactionRef& removedTx : removed_txn) {
2196+
for (const CTransactionRef& removedTx : result.m_replaced_transactions.value()) {
21972197
AddToCompactExtraTransactions(removedTx);
21982198
}
21992199
break;
@@ -3197,10 +3197,10 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
31973197
return;
31983198
}
31993199

3200-
TxValidationState state;
3201-
std::list<CTransactionRef> lRemovedTxn;
3200+
const MempoolAcceptResult result = AcceptToMemoryPool(m_mempool, ptx, false /* bypass_limits */);
3201+
const TxValidationState& state = result.m_state;
32023202

3203-
if (AcceptToMemoryPool(m_mempool, state, ptx, &lRemovedTxn, false /* bypass_limits */)) {
3203+
if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
32043204
m_mempool.check(&::ChainstateActive().CoinsTip());
32053205
// As this version of the transaction was acceptable, we can forget about any
32063206
// requests for it.
@@ -3223,7 +3223,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
32233223
tx.GetHash().ToString(),
32243224
m_mempool.size(), m_mempool.DynamicMemoryUsage() / 1000);
32253225

3226-
for (const CTransactionRef& removedTx : lRemovedTxn) {
3226+
for (const CTransactionRef& removedTx : result.m_replaced_transactions.value()) {
32273227
AddToCompactExtraTransactions(removedTx);
32283228
}
32293229

src/node/transaction.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,22 +50,22 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
5050
}
5151
if (!node.mempool->exists(hashTx)) {
5252
// Transaction is not already in the mempool.
53-
TxValidationState state;
5453
if (max_tx_fee > 0) {
5554
// First, call ATMP with test_accept and check the fee. If ATMP
5655
// fails here, return error immediately.
57-
CAmount fee{0};
58-
if (!AcceptToMemoryPool(*node.mempool, state, tx,
59-
nullptr /* plTxnReplaced */, false /* bypass_limits */, /* test_accept */ true, &fee)) {
60-
return HandleATMPError(state, err_string);
61-
} else if (fee > max_tx_fee) {
56+
const MempoolAcceptResult result = AcceptToMemoryPool(*node.mempool, tx, false /* bypass_limits */,
57+
true /* test_accept */);
58+
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
59+
return HandleATMPError(result.m_state, err_string);
60+
} else if (result.m_base_fees.value() > max_tx_fee) {
6261
return TransactionError::MAX_FEE_EXCEEDED;
6362
}
6463
}
6564
// Try to submit the transaction to the mempool.
66-
if (!AcceptToMemoryPool(*node.mempool, state, tx,
67-
nullptr /* plTxnReplaced */, false /* bypass_limits */)) {
68-
return HandleATMPError(state, err_string);
65+
const MempoolAcceptResult result = AcceptToMemoryPool(*node.mempool, tx, false /* bypass_limits */,
66+
false /* test_accept */);
67+
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
68+
return HandleATMPError(result.m_state, err_string);
6969
}
7070

7171
// Transaction was accepted to the mempool.

src/rpc/rawtransaction.cpp

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -946,18 +946,13 @@ static RPCHelpMan testmempoolaccept()
946946
result_0.pushKV("txid", tx->GetHash().GetHex());
947947
result_0.pushKV("wtxid", tx->GetWitnessHash().GetHex());
948948

949-
TxValidationState state;
950-
bool test_accept_res;
951-
CAmount fee{0};
952-
{
953-
LOCK(cs_main);
954-
test_accept_res = AcceptToMemoryPool(mempool, state, std::move(tx),
955-
nullptr /* plTxnReplaced */, false /* bypass_limits */, /* test_accept */ true, &fee);
956-
}
949+
const MempoolAcceptResult accept_result = WITH_LOCK(cs_main, return AcceptToMemoryPool(mempool, std::move(tx),
950+
false /* bypass_limits */, /* test_accept */ true));
957951

958952
// Only return the fee and vsize if the transaction would pass ATMP.
959953
// These can be used to calculate the feerate.
960-
if (test_accept_res) {
954+
if (accept_result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
955+
const CAmount fee = accept_result.m_base_fees.value();
961956
// Check that fee does not exceed maximum fee
962957
if (max_raw_tx_fee && fee > max_raw_tx_fee) {
963958
result_0.pushKV("allowed", false);
@@ -972,6 +967,7 @@ static RPCHelpMan testmempoolaccept()
972967
result.push_back(std::move(result_0));
973968
} else {
974969
result_0.pushKV("allowed", false);
970+
const TxValidationState state = accept_result.m_state;
975971
if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
976972
result_0.pushKV("reject-reason", "missing-inputs");
977973
} else {

src/test/txvalidation_tests.cpp

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,25 +30,21 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
3030

3131
BOOST_CHECK(CTransaction(coinbaseTx).IsCoinBase());
3232

33-
TxValidationState state;
34-
3533
LOCK(cs_main);
3634

3735
unsigned int initialPoolSize = m_node.mempool->size();
36+
const MempoolAcceptResult result = AcceptToMemoryPool(*m_node.mempool, MakeTransactionRef(coinbaseTx),
37+
true /* bypass_limits */);
3838

39-
BOOST_CHECK_EQUAL(
40-
false,
41-
AcceptToMemoryPool(*m_node.mempool, state, MakeTransactionRef(coinbaseTx),
42-
nullptr /* plTxnReplaced */,
43-
true /* bypass_limits */));
39+
BOOST_CHECK(result.m_result_type == MempoolAcceptResult::ResultType::INVALID);
4440

4541
// Check that the transaction hasn't been added to mempool.
4642
BOOST_CHECK_EQUAL(m_node.mempool->size(), initialPoolSize);
4743

4844
// Check that the validation state reflects the unsuccessful attempt.
49-
BOOST_CHECK(state.IsInvalid());
50-
BOOST_CHECK_EQUAL(state.GetRejectReason(), "coinbase");
51-
BOOST_CHECK(state.GetResult() == TxValidationResult::TX_CONSENSUS);
45+
BOOST_CHECK(result.m_state.IsInvalid());
46+
BOOST_CHECK_EQUAL(result.m_state.GetRejectReason(), "coinbase");
47+
BOOST_CHECK(result.m_state.GetResult() == TxValidationResult::TX_CONSENSUS);
5248
}
5349

5450
BOOST_AUTO_TEST_SUITE_END()

src/test/txvalidationcache_tests.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
2828
const auto ToMemPool = [this](const CMutableTransaction& tx) {
2929
LOCK(cs_main);
3030

31-
TxValidationState state;
32-
return AcceptToMemoryPool(*m_node.mempool, state, MakeTransactionRef(tx),
33-
nullptr /* plTxnReplaced */, true /* bypass_limits */);
31+
const MempoolAcceptResult result = AcceptToMemoryPool(*m_node.mempool, MakeTransactionRef(tx),
32+
true /* bypass_limits */);
33+
return result.m_result_type == MempoolAcceptResult::ResultType::VALID;
3434
};
3535

3636
// Create a double-spend of mature coinbase txn:

src/test/validation_block_tests.cpp

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -283,15 +283,9 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
283283
// Add the txs to the tx pool
284284
{
285285
LOCK(cs_main);
286-
TxValidationState state;
287-
std::list<CTransactionRef> plTxnReplaced;
288286
for (const auto& tx : txs) {
289-
BOOST_REQUIRE(AcceptToMemoryPool(
290-
*m_node.mempool,
291-
state,
292-
tx,
293-
&plTxnReplaced,
294-
/* bypass_limits */ false));
287+
const MempoolAcceptResult result = AcceptToMemoryPool(*m_node.mempool, tx, false /* bypass_limits */);
288+
BOOST_REQUIRE(result.m_result_type == MempoolAcceptResult::ResultType::VALID);
295289
}
296290
}
297291

src/validation.cpp

Lines changed: 29 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -380,10 +380,8 @@ static void UpdateMempoolForReorg(CTxMemPool& mempool, DisconnectedBlockTransact
380380
auto it = disconnectpool.queuedTx.get<insertion_order>().rbegin();
381381
while (it != disconnectpool.queuedTx.get<insertion_order>().rend()) {
382382
// ignore validation errors in resurrected transactions
383-
TxValidationState stateDummy;
384383
if (!fAddToMempool || (*it)->IsCoinBase() ||
385-
!AcceptToMemoryPool(mempool, stateDummy, *it,
386-
nullptr /* plTxnReplaced */, true /* bypass_limits */)) {
384+
AcceptToMemoryPool(mempool, *it, true /* bypass_limits */).m_result_type != MempoolAcceptResult::ResultType::VALID) {
387385
// If the transaction doesn't make it in to the mempool, remove any
388386
// transactions that depend on it (which would now be orphans).
389387
mempool.removeRecursive(**it, MemPoolRemovalReason::REORG);
@@ -465,7 +463,7 @@ class MemPoolAccept
465463
const CChainParams& m_chainparams;
466464
TxValidationState &m_state;
467465
const int64_t m_accept_time;
468-
std::list<CTransactionRef>* m_replaced_transactions;
466+
std::list<CTransactionRef> m_replaced_transactions;
469467
const bool m_bypass_limits;
470468
/*
471469
* Return any outpoints which were not previously present in the coins
@@ -476,11 +474,11 @@ class MemPoolAccept
476474
*/
477475
std::vector<COutPoint>& m_coins_to_uncache;
478476
const bool m_test_accept;
479-
CAmount* m_fee_out;
477+
CAmount m_fee_out;
480478
};
481479

482480
// Single transaction acceptance
483-
bool AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
481+
MempoolAcceptResult AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
484482

485483
private:
486484
// All the intermediate state that gets passed between the various levels
@@ -688,10 +686,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
688686
return false; // state filled in by CheckTxInputs
689687
}
690688

691-
// If fee_out is passed, return the fee to the caller
692-
if (args.m_fee_out) {
693-
*args.m_fee_out = nFees;
694-
}
689+
args.m_fee_out = nFees;
695690

696691
// Check for non-standard pay-to-script-hash in inputs
697692
const auto& params = args.m_chainparams.GetConsensus();
@@ -1007,8 +1002,7 @@ bool MemPoolAccept::Finalize(ATMPArgs& args, Workspace& ws)
10071002
hash.ToString(),
10081003
FormatMoney(nModifiedFees - nConflictingFees),
10091004
(int)entry->GetTxSize() - (int)nConflictingSize);
1010-
if (args.m_replaced_transactions)
1011-
args.m_replaced_transactions->push_back(it->GetSharedTx());
1005+
args.m_replaced_transactions.push_back(it->GetSharedTx());
10121006
}
10131007
m_pool.RemoveStaged(allConflicting, false, MemPoolRemovalReason::REPLACED);
10141008

@@ -1031,46 +1025,51 @@ bool MemPoolAccept::Finalize(ATMPArgs& args, Workspace& ws)
10311025
return true;
10321026
}
10331027

1034-
bool MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args)
1028+
MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args)
10351029
{
10361030
AssertLockHeld(cs_main);
10371031
LOCK(m_pool.cs); // mempool "read lock" (held through GetMainSignals().TransactionAddedToMempool())
10381032

10391033
Workspace workspace(ptx);
10401034

1041-
if (!PreChecks(args, workspace)) return false;
1035+
if (!PreChecks(args, workspace)) return MempoolAcceptResult(args.m_state);
10421036

10431037
// Only compute the precomputed transaction data if we need to verify
10441038
// scripts (ie, other policy checks pass). We perform the inexpensive
10451039
// checks first and avoid hashing and signature verification unless those
10461040
// checks pass, to mitigate CPU exhaustion denial-of-service attacks.
10471041
PrecomputedTransactionData txdata;
10481042

1049-
if (!PolicyScriptChecks(args, workspace, txdata)) return false;
1043+
if (!PolicyScriptChecks(args, workspace, txdata)) return MempoolAcceptResult(args.m_state);
10501044

1051-
if (!ConsensusScriptChecks(args, workspace, txdata)) return false;
1045+
if (!ConsensusScriptChecks(args, workspace, txdata)) return MempoolAcceptResult(args.m_state);
10521046

10531047
// Tx was accepted, but not added
1054-
if (args.m_test_accept) return true;
1048+
if (args.m_test_accept) {
1049+
return MempoolAcceptResult(std::move(args.m_replaced_transactions), args.m_fee_out);
1050+
}
10551051

1056-
if (!Finalize(args, workspace)) return false;
1052+
if (!Finalize(args, workspace)) return MempoolAcceptResult(args.m_state);
10571053

10581054
GetMainSignals().TransactionAddedToMempool(ptx, m_pool.GetAndIncrementSequence());
10591055

1060-
return true;
1056+
return MempoolAcceptResult(std::move(args.m_replaced_transactions), args.m_fee_out);
10611057
}
10621058

10631059
} // anon namespace
10641060

10651061
/** (try to) add transaction to memory pool with a specified acceptance time **/
1066-
static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, TxValidationState &state, const CTransactionRef &tx,
1067-
int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
1068-
bool bypass_limits, bool test_accept, CAmount* fee_out=nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
1062+
static MempoolAcceptResult AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool,
1063+
const CTransactionRef &tx, int64_t nAcceptTime,
1064+
bool bypass_limits, bool test_accept)
1065+
EXCLUSIVE_LOCKS_REQUIRED(cs_main)
10691066
{
1067+
TxValidationState state;
10701068
std::vector<COutPoint> coins_to_uncache;
1071-
MemPoolAccept::ATMPArgs args { chainparams, state, nAcceptTime, plTxnReplaced, bypass_limits, coins_to_uncache, test_accept, fee_out };
1072-
bool res = MemPoolAccept(pool).AcceptSingleTransaction(tx, args);
1073-
if (!res) {
1069+
MemPoolAccept::ATMPArgs args { chainparams, state, nAcceptTime, {}, bypass_limits, coins_to_uncache, test_accept, {} };
1070+
1071+
const MempoolAcceptResult result = MemPoolAccept(pool).AcceptSingleTransaction(tx, args);
1072+
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
10741073
// Remove coins that were not present in the coins cache before calling ATMPW;
10751074
// this is to prevent memory DoS in case we receive a large number of
10761075
// invalid transactions that attempt to overrun the in-memory coins cache
@@ -1082,15 +1081,13 @@ static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPo
10821081
// After we've (potentially) uncached entries, ensure our coins cache is still within its size limits
10831082
BlockValidationState state_dummy;
10841083
::ChainstateActive().FlushStateToDisk(chainparams, state_dummy, FlushStateMode::PERIODIC);
1085-
return res;
1084+
return result;
10861085
}
10871086

1088-
bool AcceptToMemoryPool(CTxMemPool& pool, TxValidationState &state, const CTransactionRef &tx,
1089-
std::list<CTransactionRef>* plTxnReplaced,
1090-
bool bypass_limits, bool test_accept, CAmount* fee_out)
1087+
MempoolAcceptResult AcceptToMemoryPool(CTxMemPool& pool, const CTransactionRef &tx, bool bypass_limits, bool test_accept)
10911088
{
10921089
const CChainParams& chainparams = Params();
1093-
return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, GetTime(), plTxnReplaced, bypass_limits, test_accept, fee_out);
1090+
return AcceptToMemoryPoolWithTime(chainparams, pool, tx, GetTime(), bypass_limits, test_accept);
10941091
}
10951092

10961093
CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock)
@@ -5029,13 +5026,10 @@ bool LoadMempool(CTxMemPool& pool)
50295026
if (amountdelta) {
50305027
pool.PrioritiseTransaction(tx->GetHash(), amountdelta);
50315028
}
5032-
TxValidationState state;
50335029
if (nTime > nNow - nExpiryTimeout) {
50345030
LOCK(cs_main);
5035-
AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, nTime,
5036-
nullptr /* plTxnReplaced */, false /* bypass_limits */,
5037-
false /* test_accept */);
5038-
if (state.IsValid()) {
5031+
if (AcceptToMemoryPoolWithTime(chainparams, pool, tx, nTime, false /* bypass_limits */,
5032+
false /* test_accept */).m_result_type == MempoolAcceptResult::ResultType::VALID) {
50395033
++count;
50405034
} else {
50415035
// mempool may contain the transaction already, e.g. from

0 commit comments

Comments
 (0)