diff --git a/src/init.cpp b/src/init.cpp --- a/src/init.cpp +++ b/src/init.cpp @@ -2628,7 +2628,7 @@ const int64_t load_block_index_start_time = GetTimeMillis(); try { LOCK(cs_main); - chainman.InitializeChainstate(*Assert(node.mempool)); + chainman.InitializeChainstate(Assert(node.mempool.get())); chainman.m_total_coinstip_cache = nCoinCacheUsage; chainman.m_total_coinsdb_cache = nCoinDBCache; diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -212,7 +212,7 @@ SetRPCWarmupFinished(); } - m_node.chainman->InitializeChainstate(*m_node.mempool); + m_node.chainman->InitializeChainstate(m_node.mempool.get()); m_node.chainman->ActiveChainstate().InitCoinsDB( /* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false); diff --git a/src/test/validation_chainstate_tests.cpp b/src/test/validation_chainstate_tests.cpp --- a/src/test/validation_chainstate_tests.cpp +++ b/src/test/validation_chainstate_tests.cpp @@ -36,7 +36,7 @@ }; CChainState &c1 = - *WITH_LOCK(cs_main, return &manager.InitializeChainstate(mempool)); + *WITH_LOCK(cs_main, return &manager.InitializeChainstate(&mempool)); c1.InitCoinsDB( /* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false); diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp --- a/src/test/validation_chainstatemanager_tests.cpp +++ b/src/test/validation_chainstatemanager_tests.cpp @@ -36,7 +36,7 @@ // Create a legacy (IBD) chainstate. // CChainState &c1 = - *WITH_LOCK(::cs_main, return &manager.InitializeChainstate(mempool)); + *WITH_LOCK(::cs_main, return &manager.InitializeChainstate(&mempool)); chainstates.push_back(&c1); c1.InitCoinsDB( /* cache_size_bytes */ 1 << 23, /* in_memory */ true, @@ -69,7 +69,7 @@ const BlockHash snapshot_blockhash{GetRandHash()}; CChainState &c2 = *WITH_LOCK( ::cs_main, - return &manager.InitializeChainstate(mempool, snapshot_blockhash)); + return &manager.InitializeChainstate(&mempool, snapshot_blockhash)); chainstates.push_back(&c2); BOOST_CHECK_EQUAL(manager.SnapshotBlockhash().value(), snapshot_blockhash); @@ -135,7 +135,7 @@ // Create a legacy (IBD) chainstate. // CChainState &c1 = - *WITH_LOCK(cs_main, return &manager.InitializeChainstate(mempool)); + *WITH_LOCK(cs_main, return &manager.InitializeChainstate(&mempool)); chainstates.push_back(&c1); c1.InitCoinsDB( /* cache_size_bytes */ 1 << 23, /* in_memory */ true, @@ -156,7 +156,7 @@ // CChainState &c2 = *WITH_LOCK(cs_main, return &manager.InitializeChainstate( - mempool, BlockHash{GetRandHash()})); + &mempool, BlockHash{GetRandHash()})); chainstates.push_back(&c2); c2.InitCoinsDB( /* cache_size_bytes */ 1 << 23, /* in_memory */ true, diff --git a/src/test/validation_flush_tests.cpp b/src/test/validation_flush_tests.cpp --- a/src/test/validation_flush_tests.cpp +++ b/src/test/validation_flush_tests.cpp @@ -19,7 +19,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate) { CTxMemPool mempool; BlockManager blockman{}; - CChainState chainstate{mempool, blockman}; + CChainState chainstate{&mempool, blockman}; chainstate.InitCoinsDB(/*cache_size_bytes*/ 1 << 10, /*in_memory*/ true, /*should_wipe*/ false); WITH_LOCK(::cs_main, chainstate.InitCoinsCache(1 << 10)); diff --git a/src/validation.h b/src/validation.h --- a/src/validation.h +++ b/src/validation.h @@ -831,8 +831,9 @@ */ mutable std::atomic m_cached_finished_ibd{false}; - //! mempool that is kept in sync with the chain - CTxMemPool &m_mempool; + //! Optional mempool that is kept in sync with the chain. + //! Only the active chainstate has a mempool. + CTxMemPool *m_mempool; const CChainParams &m_params; @@ -861,7 +862,7 @@ BlockManager &m_blockman; explicit CChainState( - CTxMemPool &mempool, BlockManager &blockman, + CTxMemPool *mempool, BlockManager &blockman, std::optional from_snapshot_blockhash = std::nullopt); /** @@ -998,7 +999,7 @@ // Block disconnection on our pcoinsTip: bool DisconnectTip(BlockValidationState &state, DisconnectedBlockTransactions *disconnectpool) - EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs); + EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool->cs); // Manual block validity manipulation: /** @@ -1114,13 +1115,13 @@ CBlockIndex *pindexMostWork, const std::shared_ptr &pblock, bool &fInvalidFound, ConnectTrace &connectTrace) - EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs); + EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool->cs); bool ConnectTip(const Config &config, BlockValidationState &state, CBlockIndex *pindexNew, const std::shared_ptr &pblock, ConnectTrace &connectTrace, DisconnectedBlockTransactions &disconnectpool) - EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs); + EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool->cs); void InvalidBlockFound(CBlockIndex *pindex, const BlockValidationState &state) EXCLUSIVE_LOCKS_REQUIRED(cs_main); @@ -1153,6 +1154,12 @@ const CBlockIndex *FindBlockToFinalize(CBlockIndex *pindexNew) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + //! Indirection necessary to make lock annotations work with an optional + //! mempool. + RecursiveMutex *MempoolMutex() const LOCK_RETURNED(m_mempool->cs) { + return m_mempool ? &m_mempool->cs : nullptr; + } + friend ChainstateManager; }; @@ -1262,7 +1269,7 @@ //! @param[in] snapshot_blockhash If given, signify that this chainstate //! is based on a snapshot. CChainState & - InitializeChainstate(CTxMemPool &mempool, + InitializeChainstate(CTxMemPool *mempool, const std::optional &snapshot_blockhash = std::nullopt) LIFETIMEBOUND EXCLUSIVE_LOCKS_REQUIRED(::cs_main); diff --git a/src/validation.cpp b/src/validation.cpp --- a/src/validation.cpp +++ b/src/validation.cpp @@ -926,7 +926,7 @@ m_cacheview = std::make_unique(&m_catcherview); } -CChainState::CChainState(CTxMemPool &mempool, BlockManager &blockman, +CChainState::CChainState(CTxMemPool *mempool, BlockManager &blockman, std::optional from_snapshot_blockhash) : m_mempool(mempool), m_params(::Params()), m_blockman(blockman), m_from_snapshot_blockhash(from_snapshot_blockhash) {} @@ -1979,7 +1979,7 @@ CChainState::GetCoinsCacheSizeState(const CTxMemPool *tx_pool, size_t max_coins_cache_size_bytes, size_t max_mempool_size_bytes) { - int64_t nMempoolUsage = tx_pool->DynamicMemoryUsage(); + int64_t nMempoolUsage = tx_pool ? tx_pool->DynamicMemoryUsage() : 0; int64_t cacheSize = CoinsTip().DynamicMemoryUsage(); int64_t nTotalSpace = max_coins_cache_size_bytes + @@ -2019,8 +2019,7 @@ bool fFlushForPrune = false; bool fDoFullFlush = false; - CoinsCacheSizeState cache_state = - GetCoinsCacheSizeState(&m_mempool); + CoinsCacheSizeState cache_state = GetCoinsCacheSizeState(m_mempool); LOCK(cs_LastBlockFile); if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) && !fReindex) { @@ -2195,12 +2194,14 @@ } /** Check warning conditions and do some notifications on new chain tip set. */ -static void UpdateTip(CTxMemPool &mempool, CBlockIndex *pindexNew, +static void UpdateTip(CTxMemPool *mempool, CBlockIndex *pindexNew, const CChainParams ¶ms, CChainState &active_chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { // New best block - mempool.AddTransactionsUpdated(1); + if (mempool) { + mempool->AddTransactionsUpdated(1); + } { LOCK(g_best_block_mutex); @@ -2234,7 +2235,10 @@ bool CChainState::DisconnectTip(BlockValidationState &state, DisconnectedBlockTransactions *disconnectpool) { AssertLockHeld(cs_main); - AssertLockHeld(m_mempool.cs); + if (m_mempool) { + AssertLockHeld(m_mempool->cs); + } + CBlockIndex *pindexDelete = m_chain.Tip(); const Consensus::Params &consensusParams = m_params.GetConsensus(); @@ -2270,22 +2274,24 @@ return false; } - // If this block is deactivating a fork, we move all mempool transactions - // in front of disconnectpool for reprocessing in a future - // updateMempoolForReorg call - if (pindexDelete->pprev != nullptr && - GetNextBlockScriptFlags(consensusParams, pindexDelete) != - GetNextBlockScriptFlags(consensusParams, pindexDelete->pprev)) { - LogPrint(BCLog::MEMPOOL, - "Disconnecting mempool due to rewind of upgrade block\n"); - if (disconnectpool) { - disconnectpool->importMempool(m_mempool); + if (m_mempool) { + // If this block is deactivating a fork, we move all mempool + // transactions in front of disconnectpool for reprocessing in a future + // updateMempoolForReorg call + if (pindexDelete->pprev != nullptr && + GetNextBlockScriptFlags(consensusParams, pindexDelete) != + GetNextBlockScriptFlags(consensusParams, pindexDelete->pprev)) { + LogPrint(BCLog::MEMPOOL, + "Disconnecting mempool due to rewind of upgrade block\n"); + if (disconnectpool) { + disconnectpool->importMempool(*m_mempool); + } + m_mempool->clear(); } - m_mempool.clear(); - } - if (disconnectpool) { - disconnectpool->addForBlock(block.vtx, m_mempool); + if (disconnectpool) { + disconnectpool->addForBlock(block.vtx, *m_mempool); + } } // If the tip is finalized, then undo it. @@ -2441,7 +2447,9 @@ ConnectTrace &connectTrace, DisconnectedBlockTransactions &disconnectpool) { AssertLockHeld(cs_main); - AssertLockHeld(m_mempool.cs); + if (m_mempool) { + AssertLockHeld(m_mempool->cs); + } const Consensus::Params &consensusParams = m_params.GetConsensus(); @@ -2520,18 +2528,21 @@ nTimeChainState * MILLI / nBlocksTotal); // Remove conflicting transactions from the mempool.; - m_mempool.removeForBlock(blockConnecting.vtx, pindexNew->nHeight); - disconnectpool.removeForBlock(blockConnecting.vtx); + if (m_mempool) { + m_mempool->removeForBlock(blockConnecting.vtx, pindexNew->nHeight); + disconnectpool.removeForBlock(blockConnecting.vtx); - // If this block is activating a fork, we move all mempool transactions - // in front of disconnectpool for reprocessing in a future - // updateMempoolForReorg call - if (pindexNew->pprev != nullptr && - GetNextBlockScriptFlags(consensusParams, pindexNew) != - GetNextBlockScriptFlags(consensusParams, pindexNew->pprev)) { - LogPrint(BCLog::MEMPOOL, - "Disconnecting mempool due to acceptance of upgrade block\n"); - disconnectpool.importMempool(m_mempool); + // If this block is activating a fork, we move all mempool transactions + // in front of disconnectpool for reprocessing in a future + // updateMempoolForReorg call + if (pindexNew->pprev != nullptr && + GetNextBlockScriptFlags(consensusParams, pindexNew) != + GetNextBlockScriptFlags(consensusParams, pindexNew->pprev)) { + LogPrint( + BCLog::MEMPOOL, + "Disconnecting mempool due to acceptance of upgrade block\n"); + disconnectpool.importMempool(*m_mempool); + } } // Update m_chain & related variables. @@ -2749,7 +2760,9 @@ CBlockIndex *pindexMostWork, const std::shared_ptr &pblock, bool &fInvalidFound, ConnectTrace &connectTrace) { AssertLockHeld(cs_main); - AssertLockHeld(m_mempool.cs); + if (m_mempool) { + AssertLockHeld(m_mempool->cs); + } const CBlockIndex *pindexOldTip = m_chain.Tip(); const CBlockIndex *pindexFork = m_chain.FindFork(pindexMostWork); @@ -2761,8 +2774,10 @@ if (!DisconnectTip(state, &disconnectpool)) { // This is likely a fatal error, but keep the mempool consistent, // just in case. Only remove from the mempool in this case. - disconnectpool.updateMempoolForReorg(config, *this, false, - m_mempool); + if (m_mempool) { + disconnectpool.updateMempoolForReorg(config, *this, false, + *m_mempool); + } // If we're unable to disconnect a block during normal operation, // then that is a failure of our local system -- we should abort @@ -2815,8 +2830,10 @@ // A system error occurred (disk space, database error, ...). // Make the mempool consistent with the current tip, just in // case any observers try to use it before shutdown. - disconnectpool.updateMempoolForReorg(config, *this, false, - m_mempool); + if (m_mempool) { + disconnectpool.updateMempoolForReorg(config, *this, false, + *m_mempool); + } return false; } else { PruneBlockIndexCandidates(); @@ -2831,17 +2848,21 @@ } } - if (fBlocksDisconnected || !disconnectpool.isEmpty()) { - // If any blocks were disconnected, we need to update the mempool even - // if disconnectpool is empty. The disconnectpool may also be non-empty - // if the mempool was imported due to new validation rules being in - // effect. - LogPrint(BCLog::MEMPOOL, "Updating mempool due to reorganization or " - "rules upgrade/downgrade\n"); - disconnectpool.updateMempoolForReorg(config, *this, true, m_mempool); - } + if (m_mempool) { + if (fBlocksDisconnected || !disconnectpool.isEmpty()) { + // If any blocks were disconnected, we need to update the mempool + // even if disconnectpool is empty. The disconnectpool may also be + // non-empty if the mempool was imported due to new validation rules + // being in effect. + LogPrint(BCLog::MEMPOOL, + "Updating mempool due to reorganization or " + "rules upgrade/downgrade\n"); + disconnectpool.updateMempoolForReorg(config, *this, true, + *m_mempool); + } - m_mempool.check(this->CoinsTip(), this->m_chain.Height() + 1); + m_mempool->check(this->CoinsTip(), this->m_chain.Height() + 1); + } // Callbacks/notifications for a new best chain. if (fInvalidFound) { @@ -2930,7 +2951,7 @@ LOCK(cs_main); // Lock transaction pool for at least as long as it takes for // connectTrace to be consumed - LOCK(m_mempool.cs); + LOCK(MempoolMutex()); CBlockIndex *starting_tip = m_chain.Tip(); bool blocks_connected = false; do { @@ -3125,7 +3146,7 @@ // Lock for as long as disconnectpool is in scope to make sure // UpdateMempoolForReorg is called after DisconnectTip without unlocking // in between - LOCK(m_mempool.cs); + LOCK(MempoolMutex()); if (!m_chain.Contains(pindex)) { break; @@ -3146,9 +3167,12 @@ // transactions back to the mempool if disconnecting was successful, // and we're not doing a very deep invalidation (in which case // keeping the mempool up to date is probably futile anyway). - disconnectpool.updateMempoolForReorg( - config, *this, - /* fAddToMempool = */ (++disconnected <= 10) && ret, m_mempool); + if (m_mempool) { + disconnectpool.updateMempoolForReorg( + config, *this, + /* fAddToMempool = */ (++disconnected <= 10) && ret, + *m_mempool); + } if (!ret) { return false; @@ -4623,10 +4647,13 @@ } void CChainState::LoadMempool(const Config &config, const ArgsManager &args) { + if (!m_mempool) { + return; + } if (args.GetBoolArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) { - ::LoadMempool(config, m_mempool, *this); + ::LoadMempool(config, *m_mempool, *this); } - m_mempool.SetIsLoaded(!ShutdownRequested()); + m_mempool->SetIsLoaded(!ShutdownRequested()); } bool CChainState::LoadChainTip() { @@ -5780,7 +5807,7 @@ } CChainState &ChainstateManager::InitializeChainstate( - CTxMemPool &mempool, const std::optional &snapshot_blockhash) { + CTxMemPool *mempool, const std::optional &snapshot_blockhash) { bool is_snapshot = snapshot_blockhash.has_value(); std::unique_ptr &to_modify = is_snapshot ? m_snapshot_chainstate : m_ibd_chainstate; @@ -5860,9 +5887,8 @@ } auto snapshot_chainstate = WITH_LOCK( - ::cs_main, - return std::make_unique(this->ActiveChainstate().m_mempool, - m_blockman, base_blockhash)); + ::cs_main, return std::make_unique( + /* mempool */ nullptr, m_blockman, base_blockhash)); { LOCK(::cs_main); @@ -5990,7 +6016,7 @@ const auto snapshot_cache_state = WITH_LOCK( ::cs_main, return snapshot_chainstate.GetCoinsCacheSizeState( - &snapshot_chainstate.m_mempool)); + snapshot_chainstate.m_mempool)); if (snapshot_cache_state >= CoinsCacheSizeState::CRITICAL) { LogPrintfToBeContinued(