Skip to content

Commit d96c59c

Browse files
committed
validation: add ChainMan logic for completing UTXO snapshot validation
Trigger completion when a background validation chainstate reaches the same height as a UTXO snapshot, and handle cleaning up the chainstate on subsequent startup.
1 parent f2a4f33 commit d96c59c

File tree

3 files changed

+421
-3
lines changed

3 files changed

+421
-3
lines changed

src/node/chainstate.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ static ChainstateLoadResult CompleteChainstateInitialization(
8585
return options.reindex || options.reindex_chainstate || chainstate->CoinsTip().GetBestBlock().IsNull();
8686
};
8787

88+
assert(chainman.m_total_coinstip_cache > 0);
89+
assert(chainman.m_total_coinsdb_cache > 0);
90+
8891
// Conservative value which is arbitrarily chosen, as it will ultimately be changed
8992
// by a call to `chainman.MaybeRebalanceCaches()`. We just need to make sure
9093
// that the sum of the two caches (40%) does not exceed the allowable amount
@@ -183,6 +186,47 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize
183186
return {init_status, init_error};
184187
}
185188

189+
// If a snapshot chainstate was fully validated by a background chainstate during
190+
// the last run, detect it here and clean up the now-unneeded background
191+
// chainstate.
192+
//
193+
// Why is this cleanup done here (on subsequent restart) and not just when the
194+
// snapshot is actually validated? Because this entails unusual
195+
// filesystem operations to move leveldb data directories around, and that seems
196+
// too risky to do in the middle of normal runtime.
197+
auto snapshot_completion = chainman.MaybeCompleteSnapshotValidation();
198+
199+
if (snapshot_completion == SnapshotCompletionResult::SKIPPED) {
200+
// do nothing; expected case
201+
} else if (snapshot_completion == SnapshotCompletionResult::SUCCESS) {
202+
LogPrintf("[snapshot] cleaning up unneeded background chainstate, then reinitializing\n");
203+
if (!chainman.ValidatedSnapshotCleanup()) {
204+
AbortNode("Background chainstate cleanup failed unexpectedly.");
205+
}
206+
207+
// Because ValidatedSnapshotCleanup() has torn down chainstates with
208+
// ChainstateManager::ResetChainstates(), reinitialize them here without
209+
// duplicating the blockindex work above.
210+
assert(chainman.GetAll().empty());
211+
assert(!chainman.IsSnapshotActive());
212+
assert(!chainman.IsSnapshotValidated());
213+
214+
chainman.InitializeChainstate(options.mempool);
215+
216+
// A reload of the block index is required to recompute setBlockIndexCandidates
217+
// for the fully validated chainstate.
218+
chainman.ActiveChainstate().UnloadBlockIndex();
219+
220+
auto [init_status, init_error] = CompleteChainstateInitialization(chainman, cache_sizes, options);
221+
if (init_status != ChainstateLoadStatus::SUCCESS) {
222+
return {init_status, init_error};
223+
}
224+
} else {
225+
return {ChainstateLoadStatus::FAILURE, _(
226+
"UTXO snapshot failed to validate. "
227+
"Restart to resume normal initial block download, or try loading a different snapshot.")};
228+
}
229+
186230
return {ChainstateLoadStatus::SUCCESS, {}};
187231
}
188232

0 commit comments

Comments
 (0)