Skip to content

Commit a64078e

Browse files
committed
Break validation <-> txmempool circular dependency
No behavior change. Parameterize removeForReorg using a CChain and callable that encapsulates validation logic. The mempool shouldn't need to know a bunch of details about coinbase maturity and lock finality. Instead, just pass in a callable function that says true/false. Breaks circular dependency by removing txmempool's dependency on validation.
1 parent 64e4963 commit a64078e

File tree

4 files changed

+37
-38
lines changed

4 files changed

+37
-38
lines changed

src/txmempool.cpp

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
#include <util/moneystr.h>
1717
#include <util/system.h>
1818
#include <util/time.h>
19-
#include <validation.h>
2019
#include <validationinterface.h>
2120

2221
#include <cmath>
@@ -634,43 +633,12 @@ void CTxMemPool::removeRecursive(const CTransaction &origTx, MemPoolRemovalReaso
634633
RemoveStaged(setAllRemoves, false, reason);
635634
}
636635

637-
void CTxMemPool::removeForReorg(CChainState& active_chainstate, int flags)
636+
void CTxMemPool::removeForReorg(CChain& chain, std::function<bool(txiter)> check_final_and_mature)
638637
{
639638
// Remove transactions spending a coinbase which are now immature and no-longer-final transactions
640639
AssertLockHeld(cs);
641640
AssertLockHeld(::cs_main);
642641

643-
const auto check_final_and_mature = [this, &active_chainstate, flags](txiter it)
644-
EXCLUSIVE_LOCKS_REQUIRED(cs, ::cs_main) {
645-
bool should_remove = false;
646-
AssertLockHeld(cs);
647-
AssertLockHeld(::cs_main);
648-
const CTransaction& tx = it->GetTx();
649-
LockPoints lp = it->GetLockPoints();
650-
const bool validLP = TestLockPointValidity(active_chainstate.m_chain, &lp);
651-
CCoinsViewMemPool view_mempool(&active_chainstate.CoinsTip(), *this);
652-
if (!CheckFinalTx(active_chainstate.m_chain.Tip(), tx, flags)
653-
|| !CheckSequenceLocks(active_chainstate.m_chain.Tip(), view_mempool, tx, flags, &lp, validLP)) {
654-
// Note if CheckSequenceLocks fails the LockPoints may still be invalid
655-
// So it's critical that we remove the tx and not depend on the LockPoints.
656-
should_remove = true;
657-
} else if (it->GetSpendsCoinbase()) {
658-
for (const CTxIn& txin : tx.vin) {
659-
indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash);
660-
if (it2 != mapTx.end())
661-
continue;
662-
const Coin &coin = active_chainstate.CoinsTip().AccessCoin(txin.prevout);
663-
assert(!coin.IsSpent());
664-
unsigned int nMemPoolHeight = active_chainstate.m_chain.Tip()->nHeight + 1;
665-
if (coin.IsSpent() || (coin.IsCoinBase() && ((signed long)nMemPoolHeight) - coin.nHeight < COINBASE_MATURITY)) {
666-
should_remove = true;
667-
break;
668-
}
669-
}
670-
}
671-
return should_remove;
672-
};
673-
674642
setEntries txToRemove;
675643
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
676644
if (check_final_and_mature(it)) txToRemove.insert(it);
@@ -680,7 +648,6 @@ void CTxMemPool::removeForReorg(CChainState& active_chainstate, int flags)
680648
CalculateDescendants(it, setAllRemoves);
681649
}
682650
RemoveStaged(setAllRemoves, false, MemPoolRemovalReason::REORG);
683-
auto chain = active_chainstate.m_chain;
684651
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
685652
LockPoints lp = it->GetLockPoints();
686653
if (!TestLockPointValidity(chain, &lp)) {

src/txmempool.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,10 @@ class CTxMemPool
589589
void addUnchecked(const CTxMemPoolEntry& entry, setEntries& setAncestors, bool validFeeEstimate = true) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
590590

591591
void removeRecursive(const CTransaction& tx, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs);
592-
void removeForReorg(CChainState& active_chainstate, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
592+
/** After reorg, check if mempool entries are now non-final, premature coinbase spends, or have
593+
* invalid lockpoints. Update lockpoints and remove entries (and descendants of entries) that
594+
* are no longer valid. */
595+
void removeForReorg(CChain& chain, std::function<bool(txiter)> check_final_and_mature) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
593596
void removeConflicts(const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(cs);
594597
void removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight) EXCLUSIVE_LOCKS_REQUIRED(cs);
595598

src/validation.cpp

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,8 +350,39 @@ void CChainState::MaybeUpdateMempoolForReorg(
350350
// the disconnectpool that were added back and cleans up the mempool state.
351351
m_mempool->UpdateTransactionsFromBlock(vHashUpdate);
352352

353+
const auto check_final_and_mature = [this, flags=STANDARD_LOCKTIME_VERIFY_FLAGS](CTxMemPool::txiter it)
354+
EXCLUSIVE_LOCKS_REQUIRED(m_mempool->cs, ::cs_main) {
355+
bool should_remove = false;
356+
AssertLockHeld(m_mempool->cs);
357+
AssertLockHeld(::cs_main);
358+
const CTransaction& tx = it->GetTx();
359+
LockPoints lp = it->GetLockPoints();
360+
bool validLP = TestLockPointValidity(m_chain, &lp);
361+
CCoinsViewMemPool view_mempool(&CoinsTip(), *m_mempool);
362+
if (!CheckFinalTx(m_chain.Tip(), tx, flags)
363+
|| !CheckSequenceLocks(m_chain.Tip(), view_mempool, tx, flags, &lp, validLP)) {
364+
// Note if CheckSequenceLocks fails the LockPoints may still be invalid
365+
// So it's critical that we remove the tx and not depend on the LockPoints.
366+
should_remove = true;
367+
} else if (it->GetSpendsCoinbase()) {
368+
for (const CTxIn& txin : tx.vin) {
369+
auto it2 = m_mempool->mapTx.find(txin.prevout.hash);
370+
if (it2 != m_mempool->mapTx.end())
371+
continue;
372+
const Coin &coin = CoinsTip().AccessCoin(txin.prevout);
373+
assert(!coin.IsSpent());
374+
unsigned int nMemPoolHeight = m_chain.Tip()->nHeight + 1;
375+
if (coin.IsSpent() || (coin.IsCoinBase() && ((signed long)nMemPoolHeight) - coin.nHeight < COINBASE_MATURITY)) {
376+
should_remove = true;
377+
break;
378+
}
379+
}
380+
}
381+
return should_remove;
382+
};
383+
353384
// We also need to remove any now-immature transactions
354-
m_mempool->removeForReorg(*this, STANDARD_LOCKTIME_VERIFY_FLAGS);
385+
m_mempool->removeForReorg(m_chain, check_final_and_mature);
355386
// Re-limit mempool size, in case we added any transactions
356387
LimitMempoolSize(
357388
*m_mempool,

test/lint/lint-circular-dependencies.sh

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,10 @@ EXPECTED_CIRCULAR_DEPENDENCIES=(
1515
"index/base -> validation -> index/blockfilterindex -> index/base"
1616
"index/coinstatsindex -> node/coinstats -> index/coinstatsindex"
1717
"policy/fees -> txmempool -> policy/fees"
18-
"policy/rbf -> txmempool -> validation -> policy/rbf"
1918
"qt/addresstablemodel -> qt/walletmodel -> qt/addresstablemodel"
2019
"qt/recentrequeststablemodel -> qt/walletmodel -> qt/recentrequeststablemodel"
2120
"qt/sendcoinsdialog -> qt/walletmodel -> qt/sendcoinsdialog"
2221
"qt/transactiontablemodel -> qt/walletmodel -> qt/transactiontablemodel"
23-
"txmempool -> validation -> txmempool"
2422
"wallet/fees -> wallet/wallet -> wallet/fees"
2523
"wallet/wallet -> wallet/walletdb -> wallet/wallet"
2624
"node/coinstats -> validation -> node/coinstats"

0 commit comments

Comments
 (0)