Skip to content

Commit 6176617

Browse files
jamesobryanofsky
andcommitted
validation: make CChainState::m_mempool optional
Since we now have multiple chainstate objects, only one of them is active at any given time. An active chainstate has a mempool, but there's no point to others having one. This change will simplify proposed assumeutxo semantics. See the discussion here: bitcoin/bitcoin#15606 (review) Co-authored-by: Russell Yanofsky <[email protected]>
1 parent 088b348 commit 6176617

File tree

7 files changed

+64
-40
lines changed

7 files changed

+64
-40
lines changed

src/init.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1349,7 +1349,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
13491349
const int64_t load_block_index_start_time = GetTimeMillis();
13501350
try {
13511351
LOCK(cs_main);
1352-
chainman.InitializeChainstate(*Assert(node.mempool));
1352+
chainman.InitializeChainstate(Assert(node.mempool.get()));
13531353
chainman.m_total_coinstip_cache = nCoinCacheUsage;
13541354
chainman.m_total_coinsdb_cache = nCoinDBCache;
13551355

src/test/util/setup_common.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
180180
// instead of unit tests, but for now we need these here.
181181
RegisterAllCoreRPCCommands(tableRPC);
182182

183-
m_node.chainman->InitializeChainstate(*m_node.mempool);
183+
m_node.chainman->InitializeChainstate(m_node.mempool.get());
184184
m_node.chainman->ActiveChainstate().InitCoinsDB(
185185
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
186186
assert(!m_node.chainman->ActiveChainstate().CanFlushToDisk());

src/test/validation_chainstate_tests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ BOOST_AUTO_TEST_CASE(validation_chainstate_resize_caches)
3535
return outp;
3636
};
3737

38-
CChainState& c1 = WITH_LOCK(cs_main, return manager.InitializeChainstate(mempool));
38+
CChainState& c1 = WITH_LOCK(cs_main, return manager.InitializeChainstate(&mempool));
3939
c1.InitCoinsDB(
4040
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
4141
WITH_LOCK(::cs_main, c1.InitCoinsCache(1 << 23));

src/test/validation_chainstatemanager_tests.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
3636

3737
// Create a legacy (IBD) chainstate.
3838
//
39-
CChainState& c1 = WITH_LOCK(::cs_main, return manager.InitializeChainstate(mempool));
39+
CChainState& c1 = WITH_LOCK(::cs_main, return manager.InitializeChainstate(&mempool));
4040
chainstates.push_back(&c1);
4141
c1.InitCoinsDB(
4242
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
@@ -66,7 +66,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
6666
//
6767
const uint256 snapshot_blockhash = GetRandHash();
6868
CChainState& c2 = WITH_LOCK(::cs_main, return manager.InitializeChainstate(
69-
mempool, snapshot_blockhash));
69+
&mempool, snapshot_blockhash));
7070
chainstates.push_back(&c2);
7171

7272
BOOST_CHECK_EQUAL(manager.SnapshotBlockhash().value(), snapshot_blockhash);
@@ -129,7 +129,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
129129

130130
// Create a legacy (IBD) chainstate.
131131
//
132-
CChainState& c1 = WITH_LOCK(cs_main, return manager.InitializeChainstate(mempool));
132+
CChainState& c1 = WITH_LOCK(cs_main, return manager.InitializeChainstate(&mempool));
133133
chainstates.push_back(&c1);
134134
c1.InitCoinsDB(
135135
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
@@ -147,7 +147,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
147147

148148
// Create a snapshot-based chainstate.
149149
//
150-
CChainState& c2 = WITH_LOCK(cs_main, return manager.InitializeChainstate(mempool, GetRandHash()));
150+
CChainState& c2 = WITH_LOCK(cs_main, return manager.InitializeChainstate(&mempool, GetRandHash()));
151151
chainstates.push_back(&c2);
152152
c2.InitCoinsDB(
153153
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);

src/test/validation_flush_tests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
2020
{
2121
CTxMemPool mempool;
2222
BlockManager blockman{};
23-
CChainState chainstate{mempool, blockman};
23+
CChainState chainstate{&mempool, blockman};
2424
chainstate.InitCoinsDB(/*cache_size_bytes*/ 1 << 10, /*in_memory*/ true, /*should_wipe*/ false);
2525
WITH_LOCK(::cs_main, chainstate.InitCoinsCache(1 << 10));
2626
CTxMemPool tx_pool{};

src/validation.cpp

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,8 @@ static bool IsCurrentForFeeEstimation(CChainState& active_chainstate) EXCLUSIVE_
329329
return true;
330330
}
331331

332-
/* Make mempool consistent after a reorg, by re-adding or recursively erasing
332+
/**
333+
* Make mempool consistent after a reorg, by re-adding or recursively erasing
333334
* disconnected block transactions from the mempool, and also removing any
334335
* other transactions from the mempool that are no longer valid given the new
335336
* tip/height.
@@ -1208,7 +1209,7 @@ void CoinsViews::InitCache()
12081209
m_cacheview = std::make_unique<CCoinsViewCache>(&m_catcherview);
12091210
}
12101211

1211-
CChainState::CChainState(CTxMemPool& mempool, BlockManager& blockman, std::optional<uint256> from_snapshot_blockhash)
1212+
CChainState::CChainState(CTxMemPool* mempool, BlockManager& blockman, std::optional<uint256> from_snapshot_blockhash)
12121213
: m_mempool(mempool),
12131214
m_params(::Params()),
12141215
m_blockman(blockman),
@@ -2053,7 +2054,7 @@ bool CChainState::FlushStateToDisk(
20532054
bool fFlushForPrune = false;
20542055
bool fDoFullFlush = false;
20552056

2056-
CoinsCacheSizeState cache_state = GetCoinsCacheSizeState(&m_mempool);
2057+
CoinsCacheSizeState cache_state = GetCoinsCacheSizeState(m_mempool);
20572058
LOCK(cs_LastBlockFile);
20582059
if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) && !fReindex) {
20592060
// make sure we don't prune above the blockfilterindexes bestblocks
@@ -2205,11 +2206,13 @@ static void AppendWarning(bilingual_str& res, const bilingual_str& warn)
22052206
}
22062207

22072208
/** Check warning conditions and do some notifications on new chain tip set. */
2208-
static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const CChainParams& chainParams, CChainState& active_chainstate)
2209+
static void UpdateTip(CTxMemPool* mempool, const CBlockIndex* pindexNew, const CChainParams& chainParams, CChainState& active_chainstate)
22092210
EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
22102211
{
22112212
// New best block
2212-
mempool.AddTransactionsUpdated(1);
2213+
if (mempool) {
2214+
mempool->AddTransactionsUpdated(1);
2215+
}
22132216

22142217
{
22152218
LOCK(g_best_block_mutex);
@@ -2254,7 +2257,7 @@ static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const C
22542257
bool CChainState::DisconnectTip(BlockValidationState& state, DisconnectedBlockTransactions* disconnectpool)
22552258
{
22562259
AssertLockHeld(cs_main);
2257-
AssertLockHeld(m_mempool.cs);
2260+
if (m_mempool) AssertLockHeld(m_mempool->cs);
22582261

22592262
CBlockIndex *pindexDelete = m_chain.Tip();
22602263
assert(pindexDelete);
@@ -2280,15 +2283,15 @@ bool CChainState::DisconnectTip(BlockValidationState& state, DisconnectedBlockTr
22802283
return false;
22812284
}
22822285

2283-
if (disconnectpool) {
2286+
if (disconnectpool && m_mempool) {
22842287
// Save transactions to re-add to mempool at end of reorg
22852288
for (auto it = block.vtx.rbegin(); it != block.vtx.rend(); ++it) {
22862289
disconnectpool->addTransaction(*it);
22872290
}
22882291
while (disconnectpool->DynamicMemoryUsage() > MAX_DISCONNECTED_TX_POOL_SIZE * 1000) {
22892292
// Drop the earliest entry, and remove its children from the mempool.
22902293
auto it = disconnectpool->queuedTx.get<insertion_order>().begin();
2291-
m_mempool.removeRecursive(**it, MemPoolRemovalReason::REORG);
2294+
m_mempool->removeRecursive(**it, MemPoolRemovalReason::REORG);
22922295
disconnectpool->removeEntry(it);
22932296
}
22942297
}
@@ -2357,7 +2360,7 @@ class ConnectTrace {
23572360
bool CChainState::ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool)
23582361
{
23592362
AssertLockHeld(cs_main);
2360-
AssertLockHeld(m_mempool.cs);
2363+
if (m_mempool) AssertLockHeld(m_mempool->cs);
23612364

23622365
assert(pindexNew->pprev == m_chain.Tip());
23632366
// Read block from disk.
@@ -2401,8 +2404,10 @@ bool CChainState::ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew
24012404
int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4;
24022405
LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime5 - nTime4) * MILLI, nTimeChainState * MICRO, nTimeChainState * MILLI / nBlocksTotal);
24032406
// Remove conflicting transactions from the mempool.;
2404-
m_mempool.removeForBlock(blockConnecting.vtx, pindexNew->nHeight);
2405-
disconnectpool.removeForBlock(blockConnecting.vtx);
2407+
if (m_mempool) {
2408+
m_mempool->removeForBlock(blockConnecting.vtx, pindexNew->nHeight);
2409+
disconnectpool.removeForBlock(blockConnecting.vtx);
2410+
}
24062411
// Update m_chain & related variables.
24072412
m_chain.SetTip(pindexNew);
24082413
UpdateTip(m_mempool, pindexNew, m_params, *this);
@@ -2495,7 +2500,7 @@ void CChainState::PruneBlockIndexCandidates() {
24952500
bool CChainState::ActivateBestChainStep(BlockValidationState& state, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace)
24962501
{
24972502
AssertLockHeld(cs_main);
2498-
AssertLockHeld(m_mempool.cs);
2503+
if (m_mempool) AssertLockHeld(m_mempool->cs);
24992504

25002505
const CBlockIndex* pindexOldTip = m_chain.Tip();
25012506
const CBlockIndex* pindexFork = m_chain.FindFork(pindexMostWork);
@@ -2507,7 +2512,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, CBlockIndex
25072512
if (!DisconnectTip(state, &disconnectpool)) {
25082513
// This is likely a fatal error, but keep the mempool consistent,
25092514
// just in case. Only remove from the mempool in this case.
2510-
UpdateMempoolForReorg(*this, m_mempool, disconnectpool, false);
2515+
if (m_mempool) UpdateMempoolForReorg(*this, *m_mempool, disconnectpool, false);
25112516

25122517
// If we're unable to disconnect a block during normal operation,
25132518
// then that is a failure of our local system -- we should abort
@@ -2551,7 +2556,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, CBlockIndex
25512556
// A system error occurred (disk space, database error, ...).
25522557
// Make the mempool consistent with the current tip, just in case
25532558
// any observers try to use it before shutdown.
2554-
UpdateMempoolForReorg(*this, m_mempool, disconnectpool, false);
2559+
if (m_mempool) UpdateMempoolForReorg(*this, *m_mempool, disconnectpool, false);
25552560
return false;
25562561
}
25572562
} else {
@@ -2565,12 +2570,12 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, CBlockIndex
25652570
}
25662571
}
25672572

2568-
if (fBlocksDisconnected) {
2573+
if (fBlocksDisconnected && m_mempool) {
25692574
// If any blocks were disconnected, disconnectpool may be non empty. Add
25702575
// any disconnected transactions back to the mempool.
2571-
UpdateMempoolForReorg(*this, m_mempool, disconnectpool, true);
2576+
UpdateMempoolForReorg(*this, *m_mempool, disconnectpool, true);
25722577
}
2573-
m_mempool.check(*this);
2578+
if (m_mempool) m_mempool->check(*this);
25742579

25752580
CheckForkWarningConditions();
25762581

@@ -2642,7 +2647,8 @@ bool CChainState::ActivateBestChain(BlockValidationState& state, std::shared_ptr
26422647

26432648
{
26442649
LOCK(cs_main);
2645-
LOCK(m_mempool.cs); // Lock transaction pool for at least as long as it takes for connectTrace to be consumed
2650+
// Lock transaction pool for at least as long as it takes for connectTrace to be consumed
2651+
LOCK(MempoolMutex());
26462652
CBlockIndex* starting_tip = m_chain.Tip();
26472653
bool blocks_connected = false;
26482654
do {
@@ -2792,7 +2798,9 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, CBlockIndex* pind
27922798
LimitValidationInterfaceQueue();
27932799

27942800
LOCK(cs_main);
2795-
LOCK(m_mempool.cs); // Lock for as long as disconnectpool is in scope to make sure UpdateMempoolForReorg is called after DisconnectTip without unlocking in between
2801+
// Lock for as long as disconnectpool is in scope to make sure UpdateMempoolForReorg is
2802+
// called after DisconnectTip without unlocking in between
2803+
LOCK(MempoolMutex());
27962804
if (!m_chain.Contains(pindex)) break;
27972805
pindex_was_in_chain = true;
27982806
CBlockIndex *invalid_walk_tip = m_chain.Tip();
@@ -2806,7 +2814,9 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, CBlockIndex* pind
28062814
// transactions back to the mempool if disconnecting was successful,
28072815
// and we're not doing a very deep invalidation (in which case
28082816
// keeping the mempool up to date is probably futile anyway).
2809-
UpdateMempoolForReorg(*this, m_mempool, disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret);
2817+
if (m_mempool) {
2818+
UpdateMempoolForReorg(*this, *m_mempool, disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret);
2819+
}
28102820
if (!ret) return false;
28112821
assert(invalid_walk_tip->pprev == m_chain.Tip());
28122822

@@ -3817,10 +3827,11 @@ bool CChainState::LoadBlockIndexDB()
38173827

38183828
void CChainState::LoadMempool(const ArgsManager& args)
38193829
{
3830+
if (!m_mempool) return;
38203831
if (args.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
3821-
::LoadMempool(m_mempool, *this);
3832+
::LoadMempool(*m_mempool, *this);
38223833
}
3823-
m_mempool.SetIsLoaded(!ShutdownRequested());
3834+
m_mempool->SetIsLoaded(!ShutdownRequested());
38243835
}
38253836

38263837
bool CChainState::LoadChainTip()
@@ -4684,7 +4695,8 @@ std::vector<CChainState*> ChainstateManager::GetAll()
46844695
return out;
46854696
}
46864697

4687-
CChainState& ChainstateManager::InitializeChainstate(CTxMemPool& mempool, const std::optional<uint256>& snapshot_blockhash)
4698+
CChainState& ChainstateManager::InitializeChainstate(
4699+
CTxMemPool* mempool, const std::optional<uint256>& snapshot_blockhash)
46884700
{
46894701
bool is_snapshot = snapshot_blockhash.has_value();
46904702
std::unique_ptr<CChainState>& to_modify =
@@ -4763,7 +4775,7 @@ bool ChainstateManager::ActivateSnapshot(
47634775
}
47644776

47654777
auto snapshot_chainstate = WITH_LOCK(::cs_main, return std::make_unique<CChainState>(
4766-
this->ActiveChainstate().m_mempool, m_blockman, base_blockhash));
4778+
/* mempool */ nullptr, m_blockman, base_blockhash));
47674779

47684780
{
47694781
LOCK(::cs_main);
@@ -4879,7 +4891,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
48794891
}
48804892

48814893
const auto snapshot_cache_state = WITH_LOCK(::cs_main,
4882-
return snapshot_chainstate.GetCoinsCacheSizeState(&snapshot_chainstate.m_mempool));
4894+
return snapshot_chainstate.GetCoinsCacheSizeState(snapshot_chainstate.m_mempool));
48834895

48844896
if (snapshot_cache_state >=
48854897
CoinsCacheSizeState::CRITICAL) {

src/validation.h

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -587,8 +587,9 @@ class CChainState
587587
*/
588588
mutable std::atomic<bool> m_cached_finished_ibd{false};
589589

590-
//! mempool that is kept in sync with the chain
591-
CTxMemPool& m_mempool;
590+
//! Optional mempool that is kept in sync with the chain.
591+
//! Only the active chainstate has a mempool.
592+
CTxMemPool* m_mempool;
592593

593594
const CChainParams& m_params;
594595

@@ -600,7 +601,10 @@ class CChainState
600601
//! CChainState instances.
601602
BlockManager& m_blockman;
602603

603-
explicit CChainState(CTxMemPool& mempool, BlockManager& blockman, std::optional<uint256> from_snapshot_blockhash = std::nullopt);
604+
explicit CChainState(
605+
CTxMemPool* mempool,
606+
BlockManager& blockman,
607+
std::optional<uint256> from_snapshot_blockhash = std::nullopt);
604608

605609
/**
606610
* Initialize the CoinsViews UTXO set database management data structures. The in-memory
@@ -729,7 +733,7 @@ class CChainState
729733
CCoinsViewCache& view, bool fJustCheck = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
730734

731735
// Apply the effects of a block disconnection on the UTXO set.
732-
bool DisconnectTip(BlockValidationState& state, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs);
736+
bool DisconnectTip(BlockValidationState& state, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool->cs);
733737

734738
// Manual block validity manipulation:
735739
/** Mark a block as precious and reorganize.
@@ -784,8 +788,8 @@ class CChainState
784788
std::string ToString() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
785789

786790
private:
787-
bool ActivateBestChainStep(BlockValidationState& state, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs);
788-
bool ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs);
791+
bool ActivateBestChainStep(BlockValidationState& state, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool->cs);
792+
bool ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool->cs);
789793

790794
void InvalidBlockFound(CBlockIndex* pindex, const BlockValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
791795
CBlockIndex* FindMostWorkChain() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -798,6 +802,12 @@ class CChainState
798802

799803
bool LoadBlockIndexDB() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
800804

805+
//! Indirection necessary to make lock annotations work with an optional mempool.
806+
RecursiveMutex* MempoolMutex() const LOCK_RETURNED(m_mempool->cs)
807+
{
808+
return m_mempool ? &m_mempool->cs : nullptr;
809+
}
810+
801811
friend ChainstateManager;
802812
};
803813

@@ -907,7 +917,9 @@ class ChainstateManager
907917
// constructor
908918
//! @param[in] snapshot_blockhash If given, signify that this chainstate
909919
//! is based on a snapshot.
910-
CChainState& InitializeChainstate(CTxMemPool& mempool, const std::optional<uint256>& snapshot_blockhash = std::nullopt)
920+
CChainState& InitializeChainstate(
921+
CTxMemPool* mempool,
922+
const std::optional<uint256>& snapshot_blockhash = std::nullopt)
911923
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
912924

913925
//! Get all chainstates currently being used.

0 commit comments

Comments
 (0)