Skip to content

Commit 38b2a0a

Browse files
author
MarcoFalke
committed
Merge bitcoin#23173: Add ChainstateManager::ProcessTransaction
0fdb619 [validation] Always call mempool.check() after processing a new transaction (John Newbery) 2c64270 [refactor] Don't call AcceptToMemoryPool() from outside validation.cpp (John Newbery) 92a3aee [validation] Add CChainState::ProcessTransaction() (John Newbery) 36167fa [logging/documentation] Remove reference to AcceptToMemoryPool from error string (John Newbery) 4c24142 [validation] Remove comment about AcceptToMemoryPool() (John Newbery) 5759fd1 [test] Don't set bypass_limits to true in txvalidation_tests.cpp (John Newbery) 497c9e2 [test] Don't set bypass_limits to true in txvalidationcache_tests.cpp (John Newbery) Pull request description: Similarly to how bitcoin#18698 added `ProcessNewBlock()` and `ProcessNewBlockHeaders()` methods to the `ChainstateManager` class, this PR adds a new `ProcessTransaction()` method. Code outside validation no longer calls `AcceptToMemoryPool()` directly, but calls through the higher-level `ProcessTransaction()` method. Advantages: - The interface is simplified. Calling code no longer needs to know about the active chainstate or mempool object, since `AcceptToMemoryPool()` can only ever be called for the active chainstate, and that chainstate knows which mempool it's using. We can also remove the `bypass_limits` argument, since that can only be used internally in validation. - responsibility for calling `CTxMemPool::check()` is removed from the callers, and run automatically by `ChainstateManager` every time `ProcessTransaction()` is called. ACKs for top commit: lsilva01: tACK 0fdb619 on Ubuntu 20.04 theStack: Code-review ACK 0fdb619 ryanofsky: Code review ACK 0fdb619. Only changes since last review: splitting & joining commits, adding more explanations to commit messages, tweaking MEMPOOL_ERROR string, fixing up argument name comments. Tree-SHA512: 0b395c2e3ef242f0d41d47174b1646b0a73aeece38f1fe29349837e6fb832f4bf8d57e1a1eaed82a97c635cfd59015a7e07f824e0d7c00b2bee4144e80608172
2 parents ed47949 + 0fdb619 commit 38b2a0a

12 files changed

+54
-32
lines changed

src/bench/block_assemble.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ static void AssembleBlock(benchmark::Bench& bench)
3434
txs.at(b) = MakeTransactionRef(tx);
3535
}
3636
{
37-
LOCK(::cs_main); // Required for ::AcceptToMemoryPool.
37+
LOCK(::cs_main);
3838

3939
for (const auto& txr : txs) {
40-
const MempoolAcceptResult res = ::AcceptToMemoryPool(test_setup->m_node.chainman->ActiveChainstate(), *test_setup->m_node.mempool, txr, false /* bypass_limits */);
40+
const MempoolAcceptResult res = test_setup->m_node.chainman->ProcessTransaction(txr);
4141
assert(res.m_result_type == MempoolAcceptResult::ResultType::VALID);
4242
}
4343
}

src/consensus/validation.h

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ enum class TxValidationResult {
5353
*/
5454
TX_CONFLICT,
5555
TX_MEMPOOL_POLICY, //!< violated mempool's fee/size/descendant/RBF/etc limits
56+
TX_NO_MEMPOOL, //!< this node does not have a mempool so can't validate the transaction
5657
};
5758

5859
/** A "reason" why a block was invalid, suitable for determining whether the

src/net_processing.cpp

+8-11
Original file line numberDiff line numberDiff line change
@@ -461,9 +461,9 @@ class PeerManagerImpl final : public PeerManager
461461
bool AlreadyHaveTx(const GenTxid& gtxid) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
462462

463463
/**
464-
* Filter for transactions that were recently rejected by
465-
* AcceptToMemoryPool. These are not rerequested until the chain tip
466-
* changes, at which point the entire filter is reset.
464+
* Filter for transactions that were recently rejected by the mempool.
465+
* These are not rerequested until the chain tip changes, at which point
466+
* the entire filter is reset.
467467
*
468468
* Without this filter we'd be re-requesting txs from each of our peers,
469469
* increasing bandwidth consumption considerably. For instance, with 100
@@ -1409,6 +1409,7 @@ bool PeerManagerImpl::MaybePunishNodeForTx(NodeId nodeid, const TxValidationStat
14091409
case TxValidationResult::TX_WITNESS_STRIPPED:
14101410
case TxValidationResult::TX_CONFLICT:
14111411
case TxValidationResult::TX_MEMPOOL_POLICY:
1412+
case TxValidationResult::TX_NO_MEMPOOL:
14121413
break;
14131414
}
14141415
if (message != "") {
@@ -2242,7 +2243,7 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
22422243
const auto [porphanTx, from_peer] = m_orphanage.GetTx(orphanHash);
22432244
if (porphanTx == nullptr) continue;
22442245

2245-
const MempoolAcceptResult result = AcceptToMemoryPool(m_chainman.ActiveChainstate(), m_mempool, porphanTx, false /* bypass_limits */);
2246+
const MempoolAcceptResult result = m_chainman.ProcessTransaction(porphanTx);
22462247
const TxValidationState& state = result.m_state;
22472248

22482249
if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
@@ -2299,8 +2300,6 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
22992300
break;
23002301
}
23012302
}
2302-
CChainState& active_chainstate = m_chainman.ActiveChainstate();
2303-
m_mempool.check(active_chainstate.CoinsTip(), active_chainstate.m_chain.Height() + 1);
23042303
}
23052304

23062305
bool PeerManagerImpl::PrepareBlockFilterRequest(CNode& peer,
@@ -3259,12 +3258,10 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
32593258
return;
32603259
}
32613260

3262-
const MempoolAcceptResult result = AcceptToMemoryPool(m_chainman.ActiveChainstate(), m_mempool, ptx, false /* bypass_limits */);
3261+
const MempoolAcceptResult result = m_chainman.ProcessTransaction(ptx);
32633262
const TxValidationState& state = result.m_state;
32643263

32653264
if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
3266-
CChainState& active_chainstate = m_chainman.ActiveChainstate();
3267-
m_mempool.check(active_chainstate.CoinsTip(), active_chainstate.m_chain.Height() + 1);
32683265
// As this version of the transaction was acceptable, we can forget about any
32693266
// requests for it.
32703267
m_txrequest.ForgetTxHash(tx.GetHash());
@@ -3383,8 +3380,8 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
33833380
}
33843381

33853382
// If a tx has been detected by m_recent_rejects, we will have reached
3386-
// this point and the tx will have been ignored. Because we haven't run
3387-
// the tx through AcceptToMemoryPool, we won't have computed a DoS
3383+
// this point and the tx will have been ignored. Because we haven't
3384+
// submitted the tx to our mempool, we won't have computed a DoS
33883385
// score for it or determined exactly why we consider it invalid.
33893386
//
33903387
// This means we won't penalize any peer subsequently relaying a DoSy

src/node/transaction.cpp

+2-4
Original file line numberDiff line numberDiff line change
@@ -70,17 +70,15 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
7070
if (max_tx_fee > 0) {
7171
// First, call ATMP with test_accept and check the fee. If ATMP
7272
// fails here, return error immediately.
73-
const MempoolAcceptResult result = AcceptToMemoryPool(node.chainman->ActiveChainstate(), *node.mempool, tx, false /* bypass_limits */,
74-
true /* test_accept */);
73+
const MempoolAcceptResult result = node.chainman->ProcessTransaction(tx, /*test_accept=*/ true);
7574
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
7675
return HandleATMPError(result.m_state, err_string);
7776
} else if (result.m_base_fees.value() > max_tx_fee) {
7877
return TransactionError::MAX_FEE_EXCEEDED;
7978
}
8079
}
8180
// Try to submit the transaction to the mempool.
82-
const MempoolAcceptResult result = AcceptToMemoryPool(node.chainman->ActiveChainstate(), *node.mempool, tx, false /* bypass_limits */,
83-
false /* test_accept */);
81+
const MempoolAcceptResult result = node.chainman->ProcessTransaction(tx, /*test_accept=*/ false);
8482
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
8583
return HandleATMPError(result.m_state, err_string);
8684
}

src/rpc/rawtransaction.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -946,12 +946,13 @@ static RPCHelpMan testmempoolaccept()
946946

947947
NodeContext& node = EnsureAnyNodeContext(request.context);
948948
CTxMemPool& mempool = EnsureMemPool(node);
949-
CChainState& chainstate = EnsureChainman(node).ActiveChainstate();
949+
ChainstateManager& chainman = EnsureChainman(node);
950+
CChainState& chainstate = chainman.ActiveChainstate();
950951
const PackageMempoolAcceptResult package_result = [&] {
951952
LOCK(::cs_main);
952953
if (txns.size() > 1) return ProcessNewPackage(chainstate, mempool, txns, /* test_accept */ true);
953954
return PackageMempoolAcceptResult(txns[0]->GetWitnessHash(),
954-
AcceptToMemoryPool(chainstate, mempool, txns[0], /* bypass_limits */ false, /* test_accept*/ true));
955+
chainman.ProcessTransaction(txns[0], /*test_accept=*/ true));
955956
}();
956957

957958
UniValue rpc_result(UniValue::VARR);

src/test/txvalidation_tests.cpp

+1-2
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
3737
LOCK(cs_main);
3838

3939
unsigned int initialPoolSize = m_node.mempool->size();
40-
const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool, MakeTransactionRef(coinbaseTx),
41-
true /* bypass_limits */);
40+
const MempoolAcceptResult result = m_node.chainman->ProcessTransaction(MakeTransactionRef(coinbaseTx));
4241

4342
BOOST_CHECK(result.m_result_type == MempoolAcceptResult::ResultType::INVALID);
4443

src/test/txvalidationcache_tests.cpp

+1-2
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, Dersig100Setup)
3636
const auto ToMemPool = [this](const CMutableTransaction& tx) {
3737
LOCK(cs_main);
3838

39-
const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool, MakeTransactionRef(tx),
40-
true /* bypass_limits */);
39+
const MempoolAcceptResult result = m_node.chainman->ProcessTransaction(MakeTransactionRef(tx));
4140
return result.m_result_type == MempoolAcceptResult::ResultType::VALID;
4241
};
4342

src/test/util/setup_common.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ CMutableTransaction TestChain100Setup::CreateValidMempoolTransaction(CTransactio
315315
// If submit=true, add transaction to the mempool.
316316
if (submit) {
317317
LOCK(cs_main);
318-
const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool.get(), MakeTransactionRef(mempool_txn), /* bypass_limits */ false);
318+
const MempoolAcceptResult result = m_node.chainman->ProcessTransaction(MakeTransactionRef(mempool_txn));
319319
assert(result.m_result_type == MempoolAcceptResult::ResultType::VALID);
320320
}
321321

src/test/validation_block_tests.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
273273
{
274274
LOCK(cs_main);
275275
for (const auto& tx : txs) {
276-
const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool, tx, false /* bypass_limits */);
276+
const MempoolAcceptResult result = m_node.chainman->ProcessTransaction(tx);
277277
BOOST_REQUIRE(result.m_result_type == MempoolAcceptResult::ResultType::VALID);
278278
}
279279
}

src/util/error.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ bilingual_str TransactionErrorString(const TransactionError err)
2020
case TransactionError::P2P_DISABLED:
2121
return Untranslated("Peer-to-peer functionality missing or disabled");
2222
case TransactionError::MEMPOOL_REJECTED:
23-
return Untranslated("Transaction rejected by AcceptToMemoryPool");
23+
return Untranslated("Transaction rejected by mempool");
2424
case TransactionError::MEMPOOL_ERROR:
25-
return Untranslated("AcceptToMemoryPool failed");
25+
return Untranslated("Mempool internal error");
2626
case TransactionError::INVALID_PSBT:
2727
return Untranslated("PSBT is not well-formed");
2828
case TransactionError::PSBT_MISMATCH:

src/validation.cpp

+13-2
Original file line numberDiff line numberDiff line change
@@ -1723,8 +1723,6 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
17231723
// can be duplicated to remove the ability to spend the first instance -- even after
17241724
// being sent to another address.
17251725
// See BIP30, CVE-2012-1909, and http://r6.ca/blog/20120206T005236Z.html for more information.
1726-
// This logic is not necessary for memory pool transactions, as AcceptToMemoryPool
1727-
// already refuses previously-known transaction ids entirely.
17281726
// This rule was originally applied to all blocks with a timestamp after March 15, 2012, 0:00 UTC.
17291727
// Now that the whole chain is irreversibly beyond that time it is applied to all blocks except the
17301728
// two in the chain that violate it. This prevents exploiting the issue against nodes during their
@@ -3488,6 +3486,19 @@ bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const s
34883486
return true;
34893487
}
34903488

3489+
MempoolAcceptResult ChainstateManager::ProcessTransaction(const CTransactionRef& tx, bool test_accept)
3490+
{
3491+
CChainState& active_chainstate = ActiveChainstate();
3492+
if (!active_chainstate.m_mempool) {
3493+
TxValidationState state;
3494+
state.Invalid(TxValidationResult::TX_NO_MEMPOOL, "no-mempool");
3495+
return MempoolAcceptResult::Failure(state);
3496+
}
3497+
auto result = AcceptToMemoryPool(active_chainstate, *active_chainstate.m_mempool, tx, /*bypass_limits=*/ false, test_accept);
3498+
active_chainstate.m_mempool->check(active_chainstate.CoinsTip(), active_chainstate.m_chain.Height() + 1);
3499+
return result;
3500+
}
3501+
34913502
bool TestBlockValidity(BlockValidationState& state,
34923503
const CChainParams& chainparams,
34933504
CChainState& chainstate,

src/validation.h

+19-3
Original file line numberDiff line numberDiff line change
@@ -208,9 +208,16 @@ struct PackageMempoolAcceptResult
208208
};
209209

210210
/**
211-
* (Try to) add a transaction to the memory pool.
212-
* @param[in] bypass_limits When true, don't enforce mempool fee limits.
213-
* @param[in] test_accept When true, run validation checks but don't submit to mempool.
211+
* Try to add a transaction to the mempool. This is an internal function and is
212+
* exposed only for testing. Client code should use ChainstateManager::ProcessTransaction()
213+
*
214+
* @param[in] active_chainstate Reference to the active chainstate.
215+
* @param[in] pool Reference to the node's mempool.
216+
* @param[in] tx The transaction to submit for mempool acceptance.
217+
* @param[in] bypass_limits When true, don't enforce mempool fee and capacity limits.
218+
* @param[in] test_accept When true, run validation checks but don't submit to mempool.
219+
*
220+
* @returns a MempoolAcceptResult indicating whether the transaction was accepted/rejected with reason.
214221
*/
215222
MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPool& pool, const CTransactionRef& tx,
216223
bool bypass_limits, bool test_accept=false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -996,6 +1003,15 @@ class ChainstateManager
9961003
*/
9971004
bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex = nullptr) LOCKS_EXCLUDED(cs_main);
9981005

1006+
/**
1007+
* Try to add a transaction to the memory pool.
1008+
*
1009+
* @param[in] tx The transaction to submit for mempool acceptance.
1010+
* @param[in] test_accept When true, run validation checks but don't submit to mempool.
1011+
*/
1012+
[[nodiscard]] MempoolAcceptResult ProcessTransaction(const CTransactionRef& tx, bool test_accept=false)
1013+
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
1014+
9991015
//! Load the block tree and coins database from disk, initializing state if we're running with -reindex
10001016
bool LoadBlockIndex() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
10011017

0 commit comments

Comments
 (0)