mirror of
https://github.com/dashpay/dash.git
synced 2024-12-27 04:52:59 +01:00
Merge #10758: Fix some chainstate-init-order bugs.
c0025d0
Fix segfault when shutting down before fully loading (Matt Corallo)1385697
Order chainstate init more logically. (Matt Corallo)ff3a219
Call RewindBlockIndex even if we're about to run -reindex-chainstate (Matt Corallo)b0f3249
More user-friendly error message if UTXO DB runs ahead of block DB (Matt Corallo)eda888e
Fix 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 ind6af06d68a
where 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:
parent
0631cd95da
commit
dbd4993084
104
src/init.cpp
104
src/init.cpp
@ -281,7 +281,9 @@ void PrepareShutdown()
|
||||
}
|
||||
|
||||
// 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...
|
||||
@ -782,7 +784,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
|
||||
@ -1752,8 +1754,6 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
evoDb = new CEvoDB(nEvoDbCache, false, fReindex || fReindexChainState);
|
||||
deterministicMNManager = new CDeterministicMNManager(*evoDb);
|
||||
pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex);
|
||||
pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex || fReindexChainState);
|
||||
pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview);
|
||||
llmq::InitLLMQSystem(*evoDb, &scheduler, false, fReindex || fReindexChainState);
|
||||
|
||||
if (fReindex) {
|
||||
@ -1761,15 +1761,13 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
//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;
|
||||
@ -1783,12 +1781,6 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
if (!chainparams.GetConsensus().hashDevnetGenesisBlock.IsNull() && !mapBlockIndex.empty() && mapBlockIndex.count(chainparams.GetConsensus().hashDevnetGenesisBlock) == 0)
|
||||
return InitError(_("Incorrect or no devnet genesis block found. Wrong datadir for devnet specified?"));
|
||||
|
||||
// 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 != gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
|
||||
strLoadError = _("You need to rebuild the database using -reindex-chainstate to change -txindex");
|
||||
@ -1802,37 +1794,81 @@ 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;
|
||||
}
|
||||
|
||||
// The on-disk coinsdb is now in a good state, create the cache
|
||||
pcoinsTip = new CCoinsViewCache(pcoinscatcher);
|
||||
LoadChainTip(chainparams);
|
||||
|
||||
deterministicMNManager->UpgradeDBIfNeeded();
|
||||
|
||||
uiInterface.InitMessage(_("Verifying blocks..."));
|
||||
if (fHavePruned && gArgs.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) {
|
||||
// LoadChainTip sets chainActive based on pcoinsTip's best block
|
||||
if (!LoadChainTip(chainparams)) {
|
||||
strLoadError = _("Error initializing block database");
|
||||
break;
|
||||
}
|
||||
assert(chainActive.Tip() != NULL);
|
||||
}
|
||||
|
||||
{
|
||||
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");
|
||||
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");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!CVerifyDB().VerifyDB(chainparams, pcoinsdbview, gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL),
|
||||
gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
|
||||
strLoadError = _("Corrupted block database detected");
|
||||
break;
|
||||
if (!fReindex && !fReindexChainState) {
|
||||
uiInterface.InitMessage(_("Verifying blocks..."));
|
||||
if (fHavePruned && gArgs.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");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!CVerifyDB().VerifyDB(chainparams, pcoinsdbview, gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL),
|
||||
gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
|
||||
strLoadError = _("Corrupted block database detected");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
LogPrintf("%s\n", e.what());
|
||||
|
@ -86,7 +86,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
|
||||
pcoinsdbview = new CCoinsViewDB(1 << 23, true);
|
||||
llmq::InitLLMQSystem(*evoDb, nullptr, true);
|
||||
pcoinsTip = new CCoinsViewCache(pcoinsdbview);
|
||||
if (!InitBlockIndex(chainparams)) {
|
||||
if (!LoadGenesisBlock(chainparams)) {
|
||||
throw std::runtime_error("InitBlockIndex failed.");
|
||||
}
|
||||
{
|
||||
|
@ -3908,14 +3908,24 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams)
|
||||
return true;
|
||||
}
|
||||
|
||||
void LoadChainTip(const CChainParams& chainparams)
|
||||
bool LoadChainTip(const CChainParams& chainparams)
|
||||
{
|
||||
if (chainActive.Tip() && chainActive.Tip()->GetBlockHash() == pcoinsTip->GetBestBlock()) return;
|
||||
if (chainActive.Tip() && chainActive.Tip()->GetBlockHash() == pcoinsTip->GetBestBlock()) return true;
|
||||
|
||||
if (pcoinsTip->GetBestBlock().IsNull() && mapBlockIndex.size() == 1) {
|
||||
// In case we just added the genesis block, connect it now, so
|
||||
// that we always have a chainActive.Tip() when we return.
|
||||
LogPrintf("%s: Connecting genesis block...\n", __func__);
|
||||
CValidationState state;
|
||||
if (!ActivateBestChain(state, chainparams)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Load pointer to end of best chain
|
||||
BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock());
|
||||
if (it == mapBlockIndex.end())
|
||||
return;
|
||||
return false;
|
||||
chainActive.SetTip(it->second);
|
||||
|
||||
PruneBlockIndexCandidates();
|
||||
@ -3924,6 +3934,7 @@ void LoadChainTip(const CChainParams& chainparams)
|
||||
chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(),
|
||||
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()),
|
||||
GuessVerificationProgress(chainparams.TxData(), chainActive.Tip()));
|
||||
return true;
|
||||
}
|
||||
|
||||
CVerifyDB::CVerifyDB()
|
||||
@ -4152,8 +4163,25 @@ void UnloadBlockIndex()
|
||||
bool LoadBlockIndex(const CChainParams& chainparams)
|
||||
{
|
||||
// Load block index from databases
|
||||
if (!fReindex && !LoadBlockIndexDB(chainparams))
|
||||
return false;
|
||||
bool needs_init = fReindex;
|
||||
if (!fReindex) {
|
||||
bool ret = LoadBlockIndexDB(chainparams);
|
||||
if (!ret) return false;
|
||||
needs_init = mapBlockIndex.empty();
|
||||
}
|
||||
|
||||
if (needs_init) {
|
||||
// Everything here is for *new* reindex/DBs. Thus, though
|
||||
// LoadBlockIndexDB may have set fReindex if we shut down
|
||||
// mid-reindex previously, we don't check fReindex and
|
||||
// instead only check it prior to LoadBlockIndexDB to set
|
||||
// needs_init.
|
||||
|
||||
LogPrintf("Initializing databases...\n");
|
||||
// Use the provided setting for -txindex in the new database
|
||||
fTxIndex = GetBoolArg("-txindex", DEFAULT_TXINDEX);
|
||||
pblocktree->WriteFlag("txindex", fTxIndex);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -4172,18 +4200,17 @@ static bool AddGenesisBlock(const CChainParams& chainparams, const CBlock& block
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InitBlockIndex(const CChainParams& chainparams)
|
||||
bool LoadGenesisBlock(const CChainParams& chainparams)
|
||||
{
|
||||
LOCK(cs_main);
|
||||
|
||||
// Check whether we're already initialized
|
||||
if (chainActive.Genesis() != nullptr)
|
||||
// Check whether we're already initialized by checking for genesis in
|
||||
// mapBlockIndex. Note that we can't use chainActive here, since it is
|
||||
// set based on the coins db, not the block index db, which is the only
|
||||
// thing loaded at this point.
|
||||
if (mapBlockIndex.count(chainparams.GenesisBlock().GetHash()))
|
||||
return true;
|
||||
|
||||
// Use the provided setting for -txindex in the new database
|
||||
fTxIndex = gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX);
|
||||
pblocktree->WriteFlag("txindex", fTxIndex);
|
||||
|
||||
// Use the provided setting for -addressindex in the new database
|
||||
fAddressIndex = gArgs.GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX);
|
||||
pblocktree->WriteFlag("addressindex", fAddressIndex);
|
||||
@ -4198,24 +4225,21 @@ bool InitBlockIndex(const CChainParams& chainparams)
|
||||
LogPrintf("Initializing databases...\n");
|
||||
|
||||
// Only add the genesis block if not reindexing (in which case we reuse the one already on disk)
|
||||
if (!fReindex) {
|
||||
try {
|
||||
CValidationState state;
|
||||
|
||||
if (!AddGenesisBlock(chainparams, chainparams.GenesisBlock(), state))
|
||||
return false;
|
||||
|
||||
if (chainparams.NetworkIDString() == CBaseChainParams::DEVNET) {
|
||||
// We can't continue if devnet genesis block is invalid
|
||||
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(chainparams.DevNetGenesisBlock());
|
||||
bool fCheckBlock = CheckBlock(*shared_pblock, state, chainparams.GetConsensus());
|
||||
assert(fCheckBlock);
|
||||
if (!AcceptBlock(shared_pblock, state, chainparams, nullptr, true, nullptr, nullptr))
|
||||
return false;
|
||||
}
|
||||
} catch (const std::runtime_error& e) {
|
||||
return error("%s: failed to initialize block database: %s", __func__, e.what());
|
||||
}
|
||||
try {
|
||||
CBlock &block = const_cast<CBlock&>(chainparams.GenesisBlock());
|
||||
// Start new block file
|
||||
unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION);
|
||||
CDiskBlockPos blockPos;
|
||||
CValidationState state;
|
||||
if (!FindBlockPos(state, blockPos, nBlockSize+8, 0, block.GetBlockTime()))
|
||||
return error("%s: FindBlockPos failed", __func__);
|
||||
if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart()))
|
||||
return error("%s: writing genesis block to disk failed", __func__);
|
||||
CBlockIndex *pindex = AddToBlockIndex(block);
|
||||
if (!ReceivedBlockTransactions(block, state, pindex, blockPos, chainparams.GetConsensus()))
|
||||
return error("%s: genesis block not accepted", __func__);
|
||||
} catch (const std::runtime_error& e) {
|
||||
return error("%s: failed to write genesis block: %s", __func__, e.what());
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -262,12 +262,13 @@ FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly = false);
|
||||
fs::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix);
|
||||
/** Import blocks from an external file */
|
||||
bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskBlockPos *dbp = nullptr);
|
||||
/** Initialize a new block tree database + block data on disk */
|
||||
bool InitBlockIndex(const CChainParams& chainparams);
|
||||
/** Load the block tree and coins database from disk */
|
||||
/** Ensures we have a genesis block in the block tree, possibly writing one to disk. */
|
||||
bool LoadGenesisBlock(const CChainParams& chainparams);
|
||||
/** Load the block tree and coins database from disk,
|
||||
* initializing state if we're running with -reindex. */
|
||||
bool LoadBlockIndex(const CChainParams& chainparams);
|
||||
/** Update the chain tip based on database information. */
|
||||
void LoadChainTip(const CChainParams& chainparams);
|
||||
bool LoadChainTip(const CChainParams& chainparams);
|
||||
/** Unload database information */
|
||||
void UnloadBlockIndex();
|
||||
/** Run an instance of the script checking thread */
|
||||
|
Loading…
Reference in New Issue
Block a user