This commit is contained in:
Pieter Wuille 2025-03-13 02:02:58 +01:00 committed by GitHub
commit d15aa49c94
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 26 additions and 24 deletions

View file

@ -265,7 +265,7 @@ epilogue:
LOCK(cs_main); LOCK(cs_main);
for (Chainstate* chainstate : chainman.GetAll()) { for (Chainstate* chainstate : chainman.GetAll()) {
if (chainstate->CanFlushToDisk()) { if (chainstate->CanFlushToDisk()) {
chainstate->ForceFlushStateToDisk(); chainstate->ForceFlushStateToDisk(/*wipe_cache=*/true);
chainstate->ResetCoinsViews(); chainstate->ResetCoinsViews();
} }
} }

View file

@ -342,7 +342,7 @@ void Shutdown(NodeContext& node)
LOCK(cs_main); LOCK(cs_main);
for (Chainstate* chainstate : node.chainman->GetAll()) { for (Chainstate* chainstate : node.chainman->GetAll()) {
if (chainstate->CanFlushToDisk()) { if (chainstate->CanFlushToDisk()) {
chainstate->ForceFlushStateToDisk(); chainstate->ForceFlushStateToDisk(/*wipe_cache=*/true);
} }
} }
} }
@ -368,7 +368,7 @@ void Shutdown(NodeContext& node)
LOCK(cs_main); LOCK(cs_main);
for (Chainstate* chainstate : node.chainman->GetAll()) { for (Chainstate* chainstate : node.chainman->GetAll()) {
if (chainstate->CanFlushToDisk()) { if (chainstate->CanFlushToDisk()) {
chainstate->ForceFlushStateToDisk(); chainstate->ForceFlushStateToDisk(/*wipe_cache=*/true);
chainstate->ResetCoinsViews(); chainstate->ResetCoinsViews();
} }
} }

View file

@ -1000,7 +1000,7 @@ static RPCHelpMan gettxoutsetinfo()
NodeContext& node = EnsureAnyNodeContext(request.context); NodeContext& node = EnsureAnyNodeContext(request.context);
ChainstateManager& chainman = EnsureChainman(node); ChainstateManager& chainman = EnsureChainman(node);
Chainstate& active_chainstate = chainman.ActiveChainstate(); Chainstate& active_chainstate = chainman.ActiveChainstate();
active_chainstate.ForceFlushStateToDisk(); active_chainstate.ForceFlushStateToDisk(/*wipe_cache=*/false);
CCoinsView* coins_view; CCoinsView* coins_view;
BlockManager* blockman; BlockManager* blockman;
@ -2319,7 +2319,7 @@ static RPCHelpMan scantxoutset()
ChainstateManager& chainman = EnsureChainman(node); ChainstateManager& chainman = EnsureChainman(node);
LOCK(cs_main); LOCK(cs_main);
Chainstate& active_chainstate = chainman.ActiveChainstate(); Chainstate& active_chainstate = chainman.ActiveChainstate();
active_chainstate.ForceFlushStateToDisk(); active_chainstate.ForceFlushStateToDisk(/*wipe_cache=*/false);
pcursor = CHECK_NONFATAL(active_chainstate.CoinsDB().Cursor()); pcursor = CHECK_NONFATAL(active_chainstate.CoinsDB().Cursor());
tip = CHECK_NONFATAL(active_chainstate.m_chain.Tip()); tip = CHECK_NONFATAL(active_chainstate.m_chain.Tip());
} }
@ -3123,7 +3123,7 @@ PrepareUTXOSnapshot(
// //
AssertLockHeld(::cs_main); AssertLockHeld(::cs_main);
chainstate.ForceFlushStateToDisk(); chainstate.ForceFlushStateToDisk(/*wipe_cache=*/false);
maybe_stats = GetUTXOStats(&chainstate.CoinsDB(), chainstate.m_blockman, CoinStatsHashType::HASH_SERIALIZED, interruption_point); maybe_stats = GetUTXOStats(&chainstate.CoinsDB(), chainstate.m_blockman, CoinStatsHashType::HASH_SERIALIZED, interruption_point);
if (!maybe_stats) { if (!maybe_stats) {

View file

@ -87,9 +87,9 @@ FUZZ_TARGET(utxo_total_supply)
tx.vin.emplace_back(txo.first); tx.vin.emplace_back(txo.first);
tx.vout.emplace_back(txo.second.nValue, txo.second.scriptPubKey); // "Forward" coin with no fee tx.vout.emplace_back(txo.second.nValue, txo.second.scriptPubKey); // "Forward" coin with no fee
}; };
const auto UpdateUtxoStats = [&]() { const auto UpdateUtxoStats = [&](bool wipe_cache) {
LOCK(chainman.GetMutex()); LOCK(chainman.GetMutex());
chainman.ActiveChainstate().ForceFlushStateToDisk(); chainman.ActiveChainstate().ForceFlushStateToDisk(/*wipe_cache=*/wipe_cache);
utxo_stats = std::move( utxo_stats = std::move(
*Assert(kernel::ComputeUTXOStats(kernel::CoinStatsHashType::NONE, &chainman.ActiveChainstate().CoinsDB(), chainman.m_blockman, {}))); *Assert(kernel::ComputeUTXOStats(kernel::CoinStatsHashType::NONE, &chainman.ActiveChainstate().CoinsDB(), chainman.m_blockman, {})));
// Check that miner can't print more money than they are allowed to // Check that miner can't print more money than they are allowed to
@ -99,7 +99,7 @@ FUZZ_TARGET(utxo_total_supply)
// Update internal state to chain tip // Update internal state to chain tip
StoreLastTxo(); StoreLastTxo();
UpdateUtxoStats(); UpdateUtxoStats(/*wipe_cache=*/fuzzed_data_provider.ConsumeBool());
assert(ActiveHeight() == 0); assert(ActiveHeight() == 0);
// Get at which height we duplicate the coinbase // Get at which height we duplicate the coinbase
// Assuming that the fuzzer will mine relatively short chains (less than 200 blocks), we want the duplicate coinbase to be not too high. // Assuming that the fuzzer will mine relatively short chains (less than 200 blocks), we want the duplicate coinbase to be not too high.
@ -124,7 +124,7 @@ FUZZ_TARGET(utxo_total_supply)
circulation += GetBlockSubsidy(ActiveHeight(), Params().GetConsensus()); circulation += GetBlockSubsidy(ActiveHeight(), Params().GetConsensus());
assert(ActiveHeight() == 1); assert(ActiveHeight() == 1);
UpdateUtxoStats(); UpdateUtxoStats(/*wipe_cache=*/fuzzed_data_provider.ConsumeBool());
current_block = PrepareNextBlock(); current_block = PrepareNextBlock();
StoreLastTxo(); StoreLastTxo();
@ -163,7 +163,7 @@ FUZZ_TARGET(utxo_total_supply)
circulation += GetBlockSubsidy(ActiveHeight(), Params().GetConsensus()); circulation += GetBlockSubsidy(ActiveHeight(), Params().GetConsensus());
} }
UpdateUtxoStats(); UpdateUtxoStats(/*wipe_cache=*/fuzzed_data_provider.ConsumeBool());
if (!was_valid) { if (!was_valid) {
// utxo stats must not change // utxo stats must not change

View file

@ -375,7 +375,7 @@ struct SnapshotTestSetup : TestChain100Setup {
{ {
for (Chainstate* cs : chainman.GetAll()) { for (Chainstate* cs : chainman.GetAll()) {
LOCK(::cs_main); LOCK(::cs_main);
cs->ForceFlushStateToDisk(); cs->ForceFlushStateToDisk(/*wipe_cache=*/true);
} }
// Process all callbacks referring to the old manager before wiping it. // Process all callbacks referring to the old manager before wiping it.
m_node.validation_signals->SyncWithValidationInterfaceQueue(); m_node.validation_signals->SyncWithValidationInterfaceQueue();

View file

@ -2895,7 +2895,7 @@ bool Chainstate::FlushStateToDisk(
// It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage. // It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage.
bool fPeriodicFlush = mode == FlushStateMode::PERIODIC && nNow > m_last_flush + DATABASE_FLUSH_INTERVAL; bool fPeriodicFlush = mode == FlushStateMode::PERIODIC && nNow > m_last_flush + DATABASE_FLUSH_INTERVAL;
// Combine all conditions that result in a full cache flush. // Combine all conditions that result in a full cache flush.
fDoFullFlush = (mode == FlushStateMode::ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune; fDoFullFlush = (mode == FlushStateMode::FORCE_FLUSH) || (mode == FlushStateMode::FORCE_SYNC) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune;
// Write blocks and block index to disk. // Write blocks and block index to disk.
if (fDoFullFlush || fPeriodicWrite) { if (fDoFullFlush || fPeriodicWrite) {
// Ensure we can write block index // Ensure we can write block index
@ -2944,7 +2944,7 @@ bool Chainstate::FlushStateToDisk(
return FatalError(m_chainman.GetNotifications(), state, _("Disk space is too low!")); return FatalError(m_chainman.GetNotifications(), state, _("Disk space is too low!"));
} }
// Flush the chainstate (which may refer to block index entries). // Flush the chainstate (which may refer to block index entries).
const auto empty_cache{(mode == FlushStateMode::ALWAYS) || fCacheLarge || fCacheCritical}; const auto empty_cache{(mode == FlushStateMode::FORCE_FLUSH) || fCacheLarge || fCacheCritical};
if (empty_cache ? !CoinsTip().Flush() : !CoinsTip().Sync()) { if (empty_cache ? !CoinsTip().Flush() : !CoinsTip().Sync()) {
return FatalError(m_chainman.GetNotifications(), state, _("Failed to write to coin database.")); return FatalError(m_chainman.GetNotifications(), state, _("Failed to write to coin database."));
} }
@ -2968,10 +2968,10 @@ bool Chainstate::FlushStateToDisk(
return true; return true;
} }
void Chainstate::ForceFlushStateToDisk() void Chainstate::ForceFlushStateToDisk(bool wipe_cache)
{ {
BlockValidationState state; BlockValidationState state;
if (!this->FlushStateToDisk(state, FlushStateMode::ALWAYS)) { if (!this->FlushStateToDisk(state, wipe_cache ? FlushStateMode::FORCE_FLUSH : FlushStateMode::FORCE_SYNC)) {
LogPrintf("%s: failed to flush state (%s)\n", __func__, state.ToString()); LogPrintf("%s: failed to flush state (%s)\n", __func__, state.ToString());
} }
} }
@ -5608,7 +5608,7 @@ bool Chainstate::ResizeCoinsCaches(size_t coinstip_size, size_t coinsdb_size)
ret = FlushStateToDisk(state, FlushStateMode::IF_NEEDED); ret = FlushStateToDisk(state, FlushStateMode::IF_NEEDED);
} else { } else {
// Otherwise, flush state to disk and deallocate the in-memory coins map. // Otherwise, flush state to disk and deallocate the in-memory coins map.
ret = FlushStateToDisk(state, FlushStateMode::ALWAYS); ret = FlushStateToDisk(state, FlushStateMode::FORCE_FLUSH);
} }
return ret; return ret;
} }
@ -6079,7 +6079,7 @@ util::Result<void> ChainstateManager::PopulateAndValidateSnapshot(
// returns in `ActivateSnapshot()`, when `MaybeRebalanceCaches()` is // returns in `ActivateSnapshot()`, when `MaybeRebalanceCaches()` is
// called, since we've added a snapshot chainstate and therefore will // called, since we've added a snapshot chainstate and therefore will
// have to downsize the IBD chainstate, which will result in a call to // have to downsize the IBD chainstate, which will result in a call to
// `FlushStateToDisk(ALWAYS)`. // `FlushStateToDisk(FORCE_FLUSH)`.
} }
assert(index); assert(index);
@ -6179,7 +6179,7 @@ SnapshotCompletionResult ChainstateManager::MaybeCompleteSnapshotValidation()
assert(this->GetAll().size() == 2); assert(this->GetAll().size() == 2);
CCoinsViewDB& ibd_coins_db = m_ibd_chainstate->CoinsDB(); CCoinsViewDB& ibd_coins_db = m_ibd_chainstate->CoinsDB();
m_ibd_chainstate->ForceFlushStateToDisk(); m_ibd_chainstate->ForceFlushStateToDisk(/*wipe_cache=*/true);
const auto& maybe_au_data = m_options.chainparams.AssumeutxoForHeight(curr_height); const auto& maybe_au_data = m_options.chainparams.AssumeutxoForHeight(curr_height);
if (!maybe_au_data) { if (!maybe_au_data) {

View file

@ -440,7 +440,8 @@ enum class FlushStateMode {
NONE, NONE,
IF_NEEDED, IF_NEEDED,
PERIODIC, PERIODIC,
ALWAYS FORCE_SYNC,
FORCE_FLUSH,
}; };
/** /**
@ -670,7 +671,7 @@ public:
int nManualPruneHeight = 0); int nManualPruneHeight = 0);
//! Unconditionally flush all changes to disk. //! Unconditionally flush all changes to disk.
void ForceFlushStateToDisk(); void ForceFlushStateToDisk(bool wipe_cache);
//! Prune blockfiles from the disk if necessary and then flush chainstate changes //! Prune blockfiles from the disk if necessary and then flush chainstate changes
//! if we pruned. //! if we pruned.

View file

@ -106,7 +106,8 @@ FLUSHMODE_NAME = {
0: "NONE", 0: "NONE",
1: "IF_NEEDED", 1: "IF_NEEDED",
2: "PERIODIC", 2: "PERIODIC",
3: "ALWAYS", 3: "FORCE_SYNC",
4: "FORCE_FLUSH",
} }
@ -374,8 +375,8 @@ class UTXOCacheTracepointTest(BitcoinTestFramework):
# A node shutdown causes two flushes. One that flushes UTXOS_IN_CACHE # A node shutdown causes two flushes. One that flushes UTXOS_IN_CACHE
# UTXOs and one that flushes 0 UTXOs. Normally the 0-UTXO-flush is the # UTXOs and one that flushes 0 UTXOs. Normally the 0-UTXO-flush is the
# second flush, however it can happen that the order changes. # second flush, however it can happen that the order changes.
expected_flushes.append({"mode": "ALWAYS", "for_prune": False, "size": UTXOS_IN_CACHE}) expected_flushes.append({"mode": "FORCE_FLUSH", "for_prune": False, "size": UTXOS_IN_CACHE})
expected_flushes.append({"mode": "ALWAYS", "for_prune": False, "size": 0}) expected_flushes.append({"mode": "FORCE_FLUSH", "for_prune": False, "size": 0})
self.stop_node(0) self.stop_node(0)
bpf.perf_buffer_poll(timeout=200) bpf.perf_buffer_poll(timeout=200)