Merge bitcoin/bitcoin#22415: Make m_mempool optional in CChainState

ceb7b35a39 refactor: move UpdateTip into CChainState (James O'Beirne)
4abf0779d6 refactor: no mempool arg to GetCoinsCacheSizeState (James O'Beirne)
46e3efd1e4 refactor: move UpdateMempoolForReorg into CChainState (James O'Beirne)
617661703a validation: make CChainState::m_mempool optional (James O'Beirne)

Pull request description:

  Make `CChainState::m_mempool` optional by making it a pointer instead of a reference. This will allow a simplification to assumeutxo semantics (see https://github.com/bitcoin/bitcoin/pull/15606#pullrequestreview-692965905) and help facilitate the `-nomempool` option.

ACKs for top commit:
  jnewbery:
    ACK ceb7b35a39
  naumenkogs:
    ACK ceb7b35a39
  ryanofsky:
    Code review ACK ceb7b35a39 (just minor style and test tweaks since last review)
  lsilva01:
    Code review ACK and tested on Signet ACK ceb7b35a39
  MarcoFalke:
    review ACK ceb7b35a39 😌

Tree-SHA512: cc445ad33439d5918cacf80a6354eea8f3d33bb7719573ed5b970fad1a0dab410bcd70be44c862b8aba1b71263b82d79876688c553e339362653dfb3d8ec81e6
This commit is contained in:
MarcoFalke 2021-07-15 13:35:17 +02:00
commit c0224bc962
No known key found for this signature in database
GPG key ID: CE2B75697E69A548
7 changed files with 118 additions and 86 deletions

View file

@ -1349,7 +1349,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
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;

View file

@ -180,7 +180,7 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
// instead of unit tests, but for now we need these here.
RegisterAllCoreRPCCommands(tableRPC);
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);
assert(!m_node.chainman->ActiveChainstate().CanFlushToDisk());

View file

@ -35,7 +35,7 @@ BOOST_AUTO_TEST_CASE(validation_chainstate_resize_caches)
return outp;
};
CChainState& c1 = WITH_LOCK(cs_main, return manager.InitializeChainstate(mempool));
CChainState& c1 = WITH_LOCK(cs_main, return manager.InitializeChainstate(&mempool));
c1.InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
WITH_LOCK(::cs_main, c1.InitCoinsCache(1 << 23));

View file

@ -36,7 +36,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
// Create a legacy (IBD) chainstate.
//
CChainState& c1 = WITH_LOCK(::cs_main, return manager.InitializeChainstate(mempool));
CChainState& c1 = WITH_LOCK(::cs_main, return manager.InitializeChainstate(&mempool));
chainstates.push_back(&c1);
c1.InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
@ -66,7 +66,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
//
const uint256 snapshot_blockhash = GetRandHash();
CChainState& c2 = WITH_LOCK(::cs_main, return manager.InitializeChainstate(
mempool, snapshot_blockhash));
&mempool, snapshot_blockhash));
chainstates.push_back(&c2);
BOOST_CHECK_EQUAL(manager.SnapshotBlockhash().value(), snapshot_blockhash);
@ -129,7 +129,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
// Create a legacy (IBD) chainstate.
//
CChainState& c1 = WITH_LOCK(cs_main, return manager.InitializeChainstate(mempool));
CChainState& c1 = WITH_LOCK(cs_main, return manager.InitializeChainstate(&mempool));
chainstates.push_back(&c1);
c1.InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
@ -147,7 +147,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
// Create a snapshot-based chainstate.
//
CChainState& c2 = WITH_LOCK(cs_main, return manager.InitializeChainstate(mempool, GetRandHash()));
CChainState& c2 = WITH_LOCK(cs_main, return manager.InitializeChainstate(&mempool, GetRandHash()));
chainstates.push_back(&c2);
c2.InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);

View file

@ -20,10 +20,9 @@ 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));
CTxMemPool tx_pool{};
constexpr bool is_64_bit = sizeof(void*) == 8;
@ -57,7 +56,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
// Without any coins in the cache, we shouldn't need to flush.
BOOST_CHECK_EQUAL(
chainstate.GetCoinsCacheSizeState(&tx_pool, MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0),
chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0),
CoinsCacheSizeState::OK);
// If the initial memory allocations of cacheCoins don't match these common
@ -72,7 +71,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
}
BOOST_CHECK_EQUAL(
chainstate.GetCoinsCacheSizeState(&tx_pool, MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0),
chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0),
CoinsCacheSizeState::CRITICAL);
BOOST_TEST_MESSAGE("Exiting cache flush tests early due to unsupported arch");
@ -93,7 +92,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
print_view_mem_usage(view);
BOOST_CHECK_EQUAL(view.AccessCoin(res).DynamicMemoryUsage(), COIN_SIZE);
BOOST_CHECK_EQUAL(
chainstate.GetCoinsCacheSizeState(&tx_pool, MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0),
chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0),
CoinsCacheSizeState::OK);
}
@ -101,26 +100,26 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
for (int i{0}; i < 4; ++i) {
add_coin(view);
print_view_mem_usage(view);
if (chainstate.GetCoinsCacheSizeState(&tx_pool, MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0) ==
if (chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0) ==
CoinsCacheSizeState::CRITICAL) {
break;
}
}
BOOST_CHECK_EQUAL(
chainstate.GetCoinsCacheSizeState(&tx_pool, MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0),
chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0),
CoinsCacheSizeState::CRITICAL);
// Passing non-zero max mempool usage should allow us more headroom.
BOOST_CHECK_EQUAL(
chainstate.GetCoinsCacheSizeState(&tx_pool, MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 1 << 10),
chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 1 << 10),
CoinsCacheSizeState::OK);
for (int i{0}; i < 3; ++i) {
add_coin(view);
print_view_mem_usage(view);
BOOST_CHECK_EQUAL(
chainstate.GetCoinsCacheSizeState(&tx_pool, MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 1 << 10),
chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 1 << 10),
CoinsCacheSizeState::OK);
}
@ -136,7 +135,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
BOOST_CHECK(usage_percentage >= 0.9);
BOOST_CHECK(usage_percentage < 1);
BOOST_CHECK_EQUAL(
chainstate.GetCoinsCacheSizeState(&tx_pool, MAX_COINS_CACHE_BYTES, 1 << 10),
chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, 1 << 10),
CoinsCacheSizeState::LARGE);
}
@ -144,7 +143,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
for (int i{0}; i < 1000; ++i) {
add_coin(view);
BOOST_CHECK_EQUAL(
chainstate.GetCoinsCacheSizeState(&tx_pool),
chainstate.GetCoinsCacheSizeState(),
CoinsCacheSizeState::OK);
}
@ -152,7 +151,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
// preallocated memory that doesn't get reclaimed even after flush.
BOOST_CHECK_EQUAL(
chainstate.GetCoinsCacheSizeState(&tx_pool, MAX_COINS_CACHE_BYTES, 0),
chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, 0),
CoinsCacheSizeState::CRITICAL);
view.SetBestBlock(InsecureRand256());
@ -160,7 +159,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
print_view_mem_usage(view);
BOOST_CHECK_EQUAL(
chainstate.GetCoinsCacheSizeState(&tx_pool, MAX_COINS_CACHE_BYTES, 0),
chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, 0),
CoinsCacheSizeState::CRITICAL);
}

View file

@ -329,23 +329,14 @@ static bool IsCurrentForFeeEstimation(CChainState& active_chainstate) EXCLUSIVE_
return true;
}
/* Make mempool consistent after a reorg, by re-adding or recursively erasing
* disconnected block transactions from the mempool, and also removing any
* other transactions from the mempool that are no longer valid given the new
* tip/height.
*
* Note: we assume that disconnectpool only contains transactions that are NOT
* confirmed in the current chain nor already in the mempool (otherwise,
* in-mempool descendants of such transactions would be removed).
*
* Passing fAddToMempool=false will skip trying to add the transactions back,
* and instead just erase from the mempool as needed.
*/
static void UpdateMempoolForReorg(CChainState& active_chainstate, CTxMemPool& mempool, DisconnectedBlockTransactions& disconnectpool, bool fAddToMempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, mempool.cs)
void CChainState::MaybeUpdateMempoolForReorg(
DisconnectedBlockTransactions& disconnectpool,
bool fAddToMempool)
{
if (!m_mempool) return;
AssertLockHeld(cs_main);
AssertLockHeld(mempool.cs);
AssertLockHeld(m_mempool->cs);
std::vector<uint256> vHashUpdate;
// disconnectpool's insertion_order index sorts the entries from
// oldest to newest, but the oldest entry will be the last tx from the
@ -357,11 +348,13 @@ static void UpdateMempoolForReorg(CChainState& active_chainstate, CTxMemPool& me
while (it != disconnectpool.queuedTx.get<insertion_order>().rend()) {
// ignore validation errors in resurrected transactions
if (!fAddToMempool || (*it)->IsCoinBase() ||
AcceptToMemoryPool(active_chainstate, mempool, *it, true /* bypass_limits */).m_result_type != MempoolAcceptResult::ResultType::VALID) {
AcceptToMemoryPool(
*this, *m_mempool, *it, true /* bypass_limits */).m_result_type !=
MempoolAcceptResult::ResultType::VALID) {
// If the transaction doesn't make it in to the mempool, remove any
// transactions that depend on it (which would now be orphans).
mempool.removeRecursive(**it, MemPoolRemovalReason::REORG);
} else if (mempool.exists((*it)->GetHash())) {
m_mempool->removeRecursive(**it, MemPoolRemovalReason::REORG);
} else if (m_mempool->exists((*it)->GetHash())) {
vHashUpdate.push_back((*it)->GetHash());
}
++it;
@ -372,12 +365,16 @@ static void UpdateMempoolForReorg(CChainState& active_chainstate, CTxMemPool& me
// previously-confirmed transactions back to the mempool.
// UpdateTransactionsFromBlock finds descendants of any transactions in
// the disconnectpool that were added back and cleans up the mempool state.
mempool.UpdateTransactionsFromBlock(vHashUpdate);
m_mempool->UpdateTransactionsFromBlock(vHashUpdate);
// We also need to remove any now-immature transactions
mempool.removeForReorg(active_chainstate, STANDARD_LOCKTIME_VERIFY_FLAGS);
m_mempool->removeForReorg(*this, STANDARD_LOCKTIME_VERIFY_FLAGS);
// Re-limit mempool size, in case we added any transactions
LimitMempoolSize(mempool, active_chainstate.CoinsTip(), gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
LimitMempoolSize(
*m_mempool,
this->CoinsTip(),
gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000,
std::chrono::hours{gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
}
/**
@ -1212,7 +1209,7 @@ void CoinsViews::InitCache()
m_cacheview = std::make_unique<CCoinsViewCache>(&m_catcherview);
}
CChainState::CChainState(CTxMemPool& mempool, BlockManager& blockman, std::optional<uint256> from_snapshot_blockhash)
CChainState::CChainState(CTxMemPool* mempool, BlockManager& blockman, std::optional<uint256> from_snapshot_blockhash)
: m_mempool(mempool),
m_params(::Params()),
m_blockman(blockman),
@ -2005,20 +2002,18 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
return true;
}
CoinsCacheSizeState CChainState::GetCoinsCacheSizeState(const CTxMemPool* tx_pool)
CoinsCacheSizeState CChainState::GetCoinsCacheSizeState()
{
return this->GetCoinsCacheSizeState(
tx_pool,
m_coinstip_cache_size_bytes,
gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
}
CoinsCacheSizeState CChainState::GetCoinsCacheSizeState(
const CTxMemPool* tx_pool,
size_t max_coins_cache_size_bytes,
size_t max_mempool_size_bytes)
{
const int64_t nMempoolUsage = tx_pool ? tx_pool->DynamicMemoryUsage() : 0;
const int64_t nMempoolUsage = m_mempool ? m_mempool->DynamicMemoryUsage() : 0;
int64_t cacheSize = CoinsTip().DynamicMemoryUsage();
int64_t nTotalSpace =
max_coins_cache_size_bytes + std::max<int64_t>(max_mempool_size_bytes - nMempoolUsage, 0);
@ -2057,7 +2052,7 @@ bool CChainState::FlushStateToDisk(
bool fFlushForPrune = false;
bool fDoFullFlush = false;
CoinsCacheSizeState cache_state = GetCoinsCacheSizeState(&m_mempool);
CoinsCacheSizeState cache_state = GetCoinsCacheSizeState();
LOCK(cs_LastBlockFile);
if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) && !fReindex) {
// make sure we don't prune above the blockfilterindexes bestblocks
@ -2208,12 +2203,12 @@ static void AppendWarning(bilingual_str& res, const bilingual_str& warn)
res += warn;
}
/** Check warning conditions and do some notifications on new chain tip set. */
static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const CChainParams& chainParams, CChainState& active_chainstate)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
void CChainState::UpdateTip(const CBlockIndex* pindexNew)
{
// New best block
mempool.AddTransactionsUpdated(1);
if (m_mempool) {
m_mempool->AddTransactionsUpdated(1);
}
{
LOCK(g_best_block_mutex);
@ -2222,11 +2217,11 @@ static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const C
}
bilingual_str warning_messages;
if (!active_chainstate.IsInitialBlockDownload()) {
if (!this->IsInitialBlockDownload()) {
const CBlockIndex* pindex = pindexNew;
for (int bit = 0; bit < VERSIONBITS_NUM_BITS; bit++) {
WarningBitsConditionChecker checker(bit);
ThresholdState state = checker.GetStateFor(pindex, chainParams.GetConsensus(), warningcache[bit]);
ThresholdState state = checker.GetStateFor(pindex, m_params.GetConsensus(), warningcache[bit]);
if (state == ThresholdState::ACTIVE || state == ThresholdState::LOCKED_IN) {
const bilingual_str warning = strprintf(_("Unknown new rules activated (versionbit %i)"), bit);
if (state == ThresholdState::ACTIVE) {
@ -2241,14 +2236,14 @@ static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const C
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion,
log(pindexNew->nChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx,
FormatISO8601DateTime(pindexNew->GetBlockTime()),
GuessVerificationProgress(chainParams.TxData(), pindexNew), active_chainstate.CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), active_chainstate.CoinsTip().GetCacheSize(),
GuessVerificationProgress(m_params.TxData(), pindexNew), this->CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), this->CoinsTip().GetCacheSize(),
!warning_messages.empty() ? strprintf(" warning='%s'", warning_messages.original) : "");
}
/** Disconnect m_chain's tip.
* After calling, the mempool will be in an inconsistent state, with
* transactions from disconnected blocks being added to disconnectpool. You
* should make the mempool consistent again by calling UpdateMempoolForReorg.
* should make the mempool consistent again by calling MaybeUpdateMempoolForReorg.
* with cs_main held.
*
* If disconnectpool is nullptr, then no disconnected transactions are added to
@ -2258,7 +2253,7 @@ static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const C
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();
assert(pindexDelete);
@ -2284,7 +2279,7 @@ bool CChainState::DisconnectTip(BlockValidationState& state, DisconnectedBlockTr
return false;
}
if (disconnectpool) {
if (disconnectpool && m_mempool) {
// Save transactions to re-add to mempool at end of reorg
for (auto it = block.vtx.rbegin(); it != block.vtx.rend(); ++it) {
disconnectpool->addTransaction(*it);
@ -2292,14 +2287,14 @@ bool CChainState::DisconnectTip(BlockValidationState& state, DisconnectedBlockTr
while (disconnectpool->DynamicMemoryUsage() > MAX_DISCONNECTED_TX_POOL_SIZE * 1000) {
// Drop the earliest entry, and remove its children from the mempool.
auto it = disconnectpool->queuedTx.get<insertion_order>().begin();
m_mempool.removeRecursive(**it, MemPoolRemovalReason::REORG);
m_mempool->removeRecursive(**it, MemPoolRemovalReason::REORG);
disconnectpool->removeEntry(it);
}
}
m_chain.SetTip(pindexDelete->pprev);
UpdateTip(m_mempool, pindexDelete->pprev, m_params, *this);
UpdateTip(pindexDelete->pprev);
// Let wallets know transactions went from 1-confirmed to
// 0-confirmed or conflicted:
GetMainSignals().BlockDisconnected(pblock, pindexDelete);
@ -2361,7 +2356,7 @@ public:
bool CChainState::ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool)
{
AssertLockHeld(cs_main);
AssertLockHeld(m_mempool.cs);
if (m_mempool) AssertLockHeld(m_mempool->cs);
assert(pindexNew->pprev == m_chain.Tip());
// Read block from disk.
@ -2405,11 +2400,13 @@ bool CChainState::ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew
int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4;
LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime5 - nTime4) * MILLI, nTimeChainState * MICRO, 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);
}
// Update m_chain & related variables.
m_chain.SetTip(pindexNew);
UpdateTip(m_mempool, pindexNew, m_params, *this);
UpdateTip(pindexNew);
int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
LogPrint(BCLog::BENCH, " - Connect postprocess: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime6 - nTime5) * MILLI, nTimePostConnect * MICRO, nTimePostConnect * MILLI / nBlocksTotal);
@ -2499,7 +2496,7 @@ void CChainState::PruneBlockIndexCandidates() {
bool CChainState::ActivateBestChainStep(BlockValidationState& state, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& 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);
@ -2511,7 +2508,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, CBlockIndex
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.
UpdateMempoolForReorg(*this, m_mempool, disconnectpool, false);
MaybeUpdateMempoolForReorg(disconnectpool, false);
// If we're unable to disconnect a block during normal operation,
// then that is a failure of our local system -- we should abort
@ -2555,7 +2552,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, CBlockIndex
// 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.
UpdateMempoolForReorg(*this, m_mempool, disconnectpool, false);
MaybeUpdateMempoolForReorg(disconnectpool, false);
return false;
}
} else {
@ -2572,9 +2569,9 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, CBlockIndex
if (fBlocksDisconnected) {
// If any blocks were disconnected, disconnectpool may be non empty. Add
// any disconnected transactions back to the mempool.
UpdateMempoolForReorg(*this, m_mempool, disconnectpool, true);
MaybeUpdateMempoolForReorg(disconnectpool, true);
}
m_mempool.check(*this);
if (m_mempool) m_mempool->check(*this);
CheckForkWarningConditions();
@ -2646,7 +2643,8 @@ bool CChainState::ActivateBestChain(BlockValidationState& state, std::shared_ptr
{
LOCK(cs_main);
LOCK(m_mempool.cs); // Lock transaction pool for at least as long as it takes for connectTrace to be consumed
// Lock transaction pool for at least as long as it takes for connectTrace to be consumed
LOCK(MempoolMutex());
CBlockIndex* starting_tip = m_chain.Tip();
bool blocks_connected = false;
do {
@ -2796,7 +2794,9 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, CBlockIndex* pind
LimitValidationInterfaceQueue();
LOCK(cs_main);
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
// Lock for as long as disconnectpool is in scope to make sure MaybeUpdateMempoolForReorg is
// called after DisconnectTip without unlocking in between
LOCK(MempoolMutex());
if (!m_chain.Contains(pindex)) break;
pindex_was_in_chain = true;
CBlockIndex *invalid_walk_tip = m_chain.Tip();
@ -2810,7 +2810,7 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, CBlockIndex* pind
// 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).
UpdateMempoolForReorg(*this, m_mempool, disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret);
MaybeUpdateMempoolForReorg(disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret);
if (!ret) return false;
assert(invalid_walk_tip->pprev == m_chain.Tip());
@ -3821,10 +3821,11 @@ bool CChainState::LoadBlockIndexDB()
void CChainState::LoadMempool(const ArgsManager& args)
{
if (!m_mempool) return;
if (args.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
::LoadMempool(m_mempool, *this);
::LoadMempool(*m_mempool, *this);
}
m_mempool.SetIsLoaded(!ShutdownRequested());
m_mempool->SetIsLoaded(!ShutdownRequested());
}
bool CChainState::LoadChainTip()
@ -4688,7 +4689,8 @@ std::vector<CChainState*> ChainstateManager::GetAll()
return out;
}
CChainState& ChainstateManager::InitializeChainstate(CTxMemPool& mempool, const std::optional<uint256>& snapshot_blockhash)
CChainState& ChainstateManager::InitializeChainstate(
CTxMemPool* mempool, const std::optional<uint256>& snapshot_blockhash)
{
bool is_snapshot = snapshot_blockhash.has_value();
std::unique_ptr<CChainState>& to_modify =
@ -4767,7 +4769,7 @@ bool ChainstateManager::ActivateSnapshot(
}
auto snapshot_chainstate = WITH_LOCK(::cs_main, return std::make_unique<CChainState>(
this->ActiveChainstate().m_mempool, m_blockman, base_blockhash));
/* mempool */ nullptr, m_blockman, base_blockhash));
{
LOCK(::cs_main);
@ -4883,7 +4885,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
}
const auto snapshot_cache_state = WITH_LOCK(::cs_main,
return snapshot_chainstate.GetCoinsCacheSizeState(&snapshot_chainstate.m_mempool));
return snapshot_chainstate.GetCoinsCacheSizeState());
if (snapshot_cache_state >=
CoinsCacheSizeState::CRITICAL) {

View file

@ -587,8 +587,9 @@ protected:
*/
mutable std::atomic<bool> 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;
@ -600,7 +601,10 @@ public:
//! CChainState instances.
BlockManager& m_blockman;
explicit CChainState(CTxMemPool& mempool, BlockManager& blockman, std::optional<uint256> from_snapshot_blockhash = std::nullopt);
explicit CChainState(
CTxMemPool* mempool,
BlockManager& blockman,
std::optional<uint256> from_snapshot_blockhash = std::nullopt);
/**
* Initialize the CoinsViews UTXO set database management data structures. The in-memory
@ -729,7 +733,7 @@ public:
CCoinsViewCache& view, bool fJustCheck = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
// Apply the effects of a block disconnection on the UTXO set.
bool DisconnectTip(BlockValidationState& state, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs);
bool DisconnectTip(BlockValidationState& state, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool->cs);
// Manual block validity manipulation:
/** Mark a block as precious and reorganize.
@ -773,19 +777,17 @@ public:
//! Dictates whether we need to flush the cache to disk or not.
//!
//! @return the state of the size of the coins cache.
CoinsCacheSizeState GetCoinsCacheSizeState(const CTxMemPool* tx_pool)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
CoinsCacheSizeState GetCoinsCacheSizeState() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
CoinsCacheSizeState GetCoinsCacheSizeState(
const CTxMemPool* tx_pool,
size_t max_coins_cache_size_bytes,
size_t max_mempool_size_bytes) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
std::string ToString() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
private:
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);
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);
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);
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);
void InvalidBlockFound(CBlockIndex* pindex, const BlockValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
CBlockIndex* FindMostWorkChain() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@ -798,6 +800,33 @@ private:
bool LoadBlockIndexDB() 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;
}
/**
* Make mempool consistent after a reorg, by re-adding or recursively erasing
* disconnected block transactions from the mempool, and also removing any
* other transactions from the mempool that are no longer valid given the new
* tip/height.
*
* Note: we assume that disconnectpool only contains transactions that are NOT
* confirmed in the current chain nor already in the mempool (otherwise,
* in-mempool descendants of such transactions would be removed).
*
* Passing fAddToMempool=false will skip trying to add the transactions back,
* and instead just erase from the mempool as needed.
*/
void MaybeUpdateMempoolForReorg(
DisconnectedBlockTransactions& disconnectpool,
bool fAddToMempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool->cs);
/** Check warning conditions and do some notifications on new chain tip set. */
void UpdateTip(const CBlockIndex* pindexNew)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
friend ChainstateManager;
};
@ -907,7 +936,9 @@ public:
// constructor
//! @param[in] snapshot_blockhash If given, signify that this chainstate
//! is based on a snapshot.
CChainState& InitializeChainstate(CTxMemPool& mempool, const std::optional<uint256>& snapshot_blockhash = std::nullopt)
CChainState& InitializeChainstate(
CTxMemPool* mempool,
const std::optional<uint256>& snapshot_blockhash = std::nullopt)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
//! Get all chainstates currently being used.