0
0
Fork 0
mirror of https://github.com/bitcoin/bitcoin.git synced 2025-02-07 10:27:47 -05:00

validation: do not wipe utxo cache for stats/scans/snapshots

This commit is contained in:
Pieter Wuille 2024-08-08 09:56:53 -04:00
parent ee367170cb
commit 1ede4803f1
8 changed files with 26 additions and 24 deletions

View file

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

View file

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

View file

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

View file

@ -77,9 +77,9 @@ FUZZ_TARGET(utxo_total_supply)
tx.vin.emplace_back(txo.first);
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());
chainman.ActiveChainstate().ForceFlushStateToDisk();
chainman.ActiveChainstate().ForceFlushStateToDisk(/*wipe_cache=*/wipe_cache);
utxo_stats = std::move(
*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
@ -89,7 +89,7 @@ FUZZ_TARGET(utxo_total_supply)
// Update internal state to chain tip
StoreLastTxo();
UpdateUtxoStats();
UpdateUtxoStats(/*wipe_cache=*/fuzzed_data_provider.ConsumeBool());
assert(ActiveHeight() == 0);
// 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.
@ -114,7 +114,7 @@ FUZZ_TARGET(utxo_total_supply)
circulation += GetBlockSubsidy(ActiveHeight(), Params().GetConsensus());
assert(ActiveHeight() == 1);
UpdateUtxoStats();
UpdateUtxoStats(/*wipe_cache=*/fuzzed_data_provider.ConsumeBool());
current_block = PrepareNextBlock();
StoreLastTxo();
@ -153,7 +153,7 @@ FUZZ_TARGET(utxo_total_supply)
circulation += GetBlockSubsidy(ActiveHeight(), Params().GetConsensus());
}
UpdateUtxoStats();
UpdateUtxoStats(/*wipe_cache=*/fuzzed_data_provider.ConsumeBool());
if (!was_valid) {
// utxo stats must not change

View file

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

View file

@ -2869,7 +2869,7 @@ bool Chainstate::FlushStateToDisk(
// 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;
// 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.
if (fDoFullFlush || fPeriodicWrite) {
// Ensure we can write block index
@ -2917,7 +2917,7 @@ bool Chainstate::FlushStateToDisk(
return FatalError(m_chainman.GetNotifications(), state, _("Disk space is too low!"));
}
// 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()) {
return FatalError(m_chainman.GetNotifications(), state, _("Failed to write to coin database."));
}
@ -2941,10 +2941,10 @@ bool Chainstate::FlushStateToDisk(
return true;
}
void Chainstate::ForceFlushStateToDisk()
void Chainstate::ForceFlushStateToDisk(bool wipe_cache)
{
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());
}
}
@ -5569,7 +5569,7 @@ bool Chainstate::ResizeCoinsCaches(size_t coinstip_size, size_t coinsdb_size)
ret = FlushStateToDisk(state, FlushStateMode::IF_NEEDED);
} else {
// 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;
}
@ -6038,7 +6038,7 @@ util::Result<void> ChainstateManager::PopulateAndValidateSnapshot(
// returns in `ActivateSnapshot()`, when `MaybeRebalanceCaches()` is
// called, since we've added a snapshot chainstate and therefore will
// have to downsize the IBD chainstate, which will result in a call to
// `FlushStateToDisk(ALWAYS)`.
// `FlushStateToDisk(FORCE_FLUSH)`.
}
assert(index);
@ -6138,7 +6138,7 @@ SnapshotCompletionResult ChainstateManager::MaybeCompleteSnapshotValidation()
assert(this->GetAll().size() == 2);
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);
if (!maybe_au_data) {

View file

@ -448,7 +448,8 @@ enum class FlushStateMode {
NONE,
IF_NEEDED,
PERIODIC,
ALWAYS
FORCE_SYNC,
FORCE_FLUSH,
};
/**
@ -678,7 +679,7 @@ public:
int nManualPruneHeight = 0);
//! 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
//! if we pruned.

View file

@ -100,7 +100,8 @@ FLUSHMODE_NAME = {
0: "NONE",
1: "IF_NEEDED",
2: "PERIODIC",
3: "ALWAYS",
3: "FORCE_SYNC",
4: "FORCE_FLUSH",
}
@ -368,8 +369,8 @@ class UTXOCacheTracepointTest(BitcoinTestFramework):
# 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
# 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": "ALWAYS", "for_prune": False, "size": 0})
expected_flushes.append({"mode": "FORCE_FLUSH", "for_prune": False, "size": UTXOS_IN_CACHE})
expected_flushes.append({"mode": "FORCE_FLUSH", "for_prune": False, "size": 0})
self.stop_node(0)
bpf.perf_buffer_poll(timeout=200)