mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-03-15 18:10:26 +01:00
Merge #10758: Fix some chainstate-init-order bugs.
c0025d0Fix segfault when shutting down before fully loading (Matt Corallo)1385697Order chainstate init more logically. (Matt Corallo)ff3a219Call RewindBlockIndex even if we're about to run -reindex-chainstate (Matt Corallo)b0f3249More user-friendly error message if UTXO DB runs ahead of block DB (Matt Corallo)eda888eFix some LoadChainTip-related init-order bugs. (Matt Corallo) Pull request description: This does a number of things to clean up chainstate init order, fixing some issues as it goes: * Order chainstate init more logically - first all of the blocktree-related loading, then coinsdb, then pcoinsTip/chainActive. Only create objects as needed. * More clearly document exactly what is and isn't called in -reindex and -reindex-chainstate both with comments noting calls as no-ops and by adding if guards. * Move the writing of fTxIndex to LoadBlockIndex - this fixes a bug introduced ind6af06d68awhere InitBlockIndex was writing to fTxIndex which had not yet been checked (because LoadChainTip hadn't yet initialized the chainActive, which would otherwise have resulted in InitBlockIndex being a NOP), allowing you to modify -txindex without reindex, potentially corrupting your chainstate! * Rename InitBlockIndex to LoadGenesisBlock, which is now a more natural name for it. Also check mapBlockIndex instead of chainActive, fixing a bug where we'd write the genesis block out on every start. * Move LoadGenesisBlock further down in init. This is a more logical location for it, as it is after all of the blockindex-related loading and checking, but before any of the UTXO-related loading and checking. * Give LoadChainTip a return value - allowing it to indicate that the UTXO DB ran ahead of the block DB. This just provides a nicer error message instead of the previous mysterious assert(!setBlockIndexCandidates.empty()) error. * Calls ActivateBestChain in case we just loaded the genesis block in LoadChainTip, avoiding relying on the ActivateBestChain in ThreadImport before continuing init process. * Move all of the VerifyDB()-related stuff into a -reindex + -reindex-chainstate if guard. It couldn't do anything useful as chainActive.Tip() would be null at this point anyway. Tree-SHA512: 3c96ee7ed44f4130bee3479a40c5cd99a619fda5e309c26d60b54feab9f6ec60fabab8cf47a049c9cf15e88999b2edb7f16cbe6819e97273560b201a89d90762
This commit is contained in:
105
src/init.cpp
105
src/init.cpp
@@ -216,7 +216,9 @@ void Shutdown()
|
||||
}
|
||||
|
||||
// FlushStateToDisk generates a SetBestChain callback, which we should avoid missing
|
||||
FlushStateToDisk();
|
||||
if (pcoinsTip != nullptr) {
|
||||
FlushStateToDisk();
|
||||
}
|
||||
|
||||
// After there are no more peers/RPC left to give us new data which may generate
|
||||
// CValidationInterface callbacks, flush them...
|
||||
@@ -646,7 +648,7 @@ void ThreadImport(std::vector<fs::path> vImportFiles)
|
||||
fReindex = false;
|
||||
LogPrintf("Reindexing finished\n");
|
||||
// To avoid ending up in a situation without genesis block, re-try initializing (no-op if reindexing worked):
|
||||
InitBlockIndex(chainparams);
|
||||
LoadGenesisBlock(chainparams);
|
||||
}
|
||||
|
||||
// hardcoded $DATADIR/bootstrap.dat
|
||||
@@ -1399,23 +1401,19 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
delete pblocktree;
|
||||
|
||||
pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex);
|
||||
pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex || fReindexChainState);
|
||||
pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview);
|
||||
|
||||
if (fReindex) {
|
||||
pblocktree->WriteReindexing(true);
|
||||
//If we're reindexing in prune mode, wipe away unusable block files and all undo data files
|
||||
if (fPruneMode)
|
||||
CleanupBlockRevFiles();
|
||||
} else {
|
||||
// If necessary, upgrade from older database format.
|
||||
if (!pcoinsdbview->Upgrade()) {
|
||||
strLoadError = _("Error upgrading chainstate database");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fRequestShutdown) break;
|
||||
|
||||
// LoadBlockIndex will load fTxIndex from the db, or set it if
|
||||
// we're reindexing. It will also load fHavePruned if we've
|
||||
// ever removed a block file from disk.
|
||||
if (!LoadBlockIndex(chainparams)) {
|
||||
strLoadError = _("Error loading block database");
|
||||
break;
|
||||
@@ -1426,12 +1424,6 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
if (!mapBlockIndex.empty() && mapBlockIndex.count(chainparams.GetConsensus().hashGenesisBlock) == 0)
|
||||
return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?"));
|
||||
|
||||
// Initialize the block index (no-op if non-empty database was already loaded)
|
||||
if (!InitBlockIndex(chainparams)) {
|
||||
strLoadError = _("Error initializing block database");
|
||||
break;
|
||||
}
|
||||
|
||||
// Check for changed -txindex state
|
||||
if (fTxIndex != GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
|
||||
strLoadError = _("You need to rebuild the database using -reindex-chainstate to change -txindex");
|
||||
@@ -1445,14 +1437,49 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
break;
|
||||
}
|
||||
|
||||
// At this point blocktree args are consistent with what's on disk.
|
||||
// If we're not mid-reindex (based on disk + args), add a genesis block on disk.
|
||||
// This is called again in ThreadImport in the reindex completes.
|
||||
if (!fReindex && !LoadGenesisBlock(chainparams)) {
|
||||
strLoadError = _("Error initializing block database");
|
||||
break;
|
||||
}
|
||||
|
||||
// At this point we're either in reindex or we've loaded a useful
|
||||
// block tree into mapBlockIndex!
|
||||
|
||||
pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex || fReindexChainState);
|
||||
pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview);
|
||||
|
||||
// If necessary, upgrade from older database format.
|
||||
// This is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
|
||||
if (!pcoinsdbview->Upgrade()) {
|
||||
strLoadError = _("Error upgrading chainstate database");
|
||||
break;
|
||||
}
|
||||
|
||||
// ReplayBlocks is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
|
||||
if (!ReplayBlocks(chainparams, pcoinsdbview)) {
|
||||
strLoadError = _("Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.");
|
||||
break;
|
||||
}
|
||||
pcoinsTip = new CCoinsViewCache(pcoinscatcher);
|
||||
LoadChainTip(chainparams);
|
||||
|
||||
if (!fReindex && chainActive.Tip() != NULL) {
|
||||
// The on-disk coinsdb is now in a good state, create the cache
|
||||
pcoinsTip = new CCoinsViewCache(pcoinscatcher);
|
||||
|
||||
if (!fReindex && !fReindexChainState) {
|
||||
// LoadChainTip sets chainActive based on pcoinsTip's best block
|
||||
if (!LoadChainTip(chainparams)) {
|
||||
strLoadError = _("Error initializing block database");
|
||||
break;
|
||||
}
|
||||
assert(chainActive.Tip() != NULL);
|
||||
}
|
||||
|
||||
if (!fReindex) {
|
||||
// Note that RewindBlockIndex MUST run even if we're about to -reindex-chainstate.
|
||||
// It both disconnects blocks based on chainActive, and drops block data in
|
||||
// mapBlockIndex based on lack of available witness data.
|
||||
uiInterface.InitMessage(_("Rewinding blocks..."));
|
||||
if (!RewindBlockIndex(chainparams)) {
|
||||
strLoadError = _("Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain");
|
||||
@@ -1460,29 +1487,31 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
}
|
||||
}
|
||||
|
||||
uiInterface.InitMessage(_("Verifying blocks..."));
|
||||
if (fHavePruned && GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) {
|
||||
LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks",
|
||||
MIN_BLOCKS_TO_KEEP);
|
||||
}
|
||||
if (!fReindex && !fReindexChainState) {
|
||||
uiInterface.InitMessage(_("Verifying blocks..."));
|
||||
if (fHavePruned && GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) {
|
||||
LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks",
|
||||
MIN_BLOCKS_TO_KEEP);
|
||||
}
|
||||
|
||||
{
|
||||
LOCK(cs_main);
|
||||
CBlockIndex* tip = chainActive.Tip();
|
||||
RPCNotifyBlockChange(true, tip);
|
||||
if (tip && tip->nTime > GetAdjustedTime() + 2 * 60 * 60) {
|
||||
strLoadError = _("The block database contains a block which appears to be from the future. "
|
||||
"This may be due to your computer's date and time being set incorrectly. "
|
||||
"Only rebuild the block database if you are sure that your computer's date and time are correct");
|
||||
{
|
||||
LOCK(cs_main);
|
||||
CBlockIndex* tip = chainActive.Tip();
|
||||
RPCNotifyBlockChange(true, tip);
|
||||
if (tip && tip->nTime > GetAdjustedTime() + 2 * 60 * 60) {
|
||||
strLoadError = _("The block database contains a block which appears to be from the future. "
|
||||
"This may be due to your computer's date and time being set incorrectly. "
|
||||
"Only rebuild the block database if you are sure that your computer's date and time are correct");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!CVerifyDB().VerifyDB(chainparams, pcoinsdbview, GetArg("-checklevel", DEFAULT_CHECKLEVEL),
|
||||
GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
|
||||
strLoadError = _("Corrupted block database detected");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!CVerifyDB().VerifyDB(chainparams, pcoinsdbview, GetArg("-checklevel", DEFAULT_CHECKLEVEL),
|
||||
GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
|
||||
strLoadError = _("Corrupted block database detected");
|
||||
break;
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
LogPrintf("%s\n", e.what());
|
||||
strLoadError = _("Error opening block database");
|
||||
|
||||
Reference in New Issue
Block a user