diff options
-rw-r--r-- | src/init.cpp | 103 | ||||
-rw-r--r-- | src/test/test_bitcoin.cpp | 2 | ||||
-rw-r--r-- | src/validation.cpp | 101 | ||||
-rw-r--r-- | src/validation.h | 9 |
4 files changed, 139 insertions, 76 deletions
diff --git a/src/init.cpp b/src/init.cpp index 0d32df4e26..0ee828ce92 100644 --- a/src/init.cpp +++ b/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; } + + // The on-disk coinsdb is now in a good state, create the cache pcoinsTip = new CCoinsViewCache(pcoinscatcher); - LoadChainTip(chainparams); - if (!fReindex && chainActive.Tip() != NULL) { + 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,28 +1487,30 @@ 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"); - break; + { + 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()); diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 0dd51a4908..545e56983c 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -74,7 +74,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha pblocktree = new CBlockTreeDB(1 << 20, true); pcoinsdbview = new CCoinsViewDB(1 << 23, true); pcoinsTip = new CCoinsViewCache(pcoinsdbview); - if (!InitBlockIndex(chainparams)) { + if (!LoadGenesisBlock(chainparams)) { throw std::runtime_error("InitBlockIndex failed."); } { diff --git a/src/validation.cpp b/src/validation.cpp index d223715c46..babf6f1522 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -3539,14 +3539,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(); @@ -3555,6 +3565,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() @@ -3751,6 +3762,8 @@ bool RewindBlockIndex(const CChainParams& params) { LOCK(cs_main); + // Note that during -reindex-chainstate we are called with an empty chainActive! + int nHeight = 1; while (nHeight <= chainActive.Height()) { if (IsWitnessEnabled(chainActive[nHeight - 1], params.GetConsensus()) && !(chainActive[nHeight]->nStatus & BLOCK_OPT_WITNESS)) { @@ -3820,12 +3833,19 @@ bool RewindBlockIndex(const CChainParams& params) } } - PruneBlockIndexCandidates(); + if (chainActive.Tip() != NULL) { + // We can't prune block index candidates based on our tip if we have + // no tip due to chainActive being empty! + PruneBlockIndexCandidates(); - CheckBlockIndex(params.GetConsensus()); + CheckBlockIndex(params.GetConsensus()); - if (!FlushStateToDisk(params, state, FLUSH_STATE_ALWAYS)) { - return false; + // FlushStateToDisk can possibly read chainActive. Be conservative + // and skip it here, we're about to -reindex-chainstate anyway, so + // it'll get called a bunch real soon. + if (!FlushStateToDisk(params, state, FLUSH_STATE_ALWAYS)) { + return false; + } } return true; @@ -3863,42 +3883,55 @@ 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; } -bool InitBlockIndex(const CChainParams& chainparams) +bool LoadGenesisBlock(const CChainParams& chainparams) { LOCK(cs_main); - // Check whether we're already initialized - if (chainActive.Genesis() != NULL) + // 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 = GetBoolArg("-txindex", DEFAULT_TXINDEX); - pblocktree->WriteFlag("txindex", fTxIndex); - 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 { - 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("LoadBlockIndex(): FindBlockPos failed"); - if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart())) - return error("LoadBlockIndex(): writing genesis block to disk failed"); - CBlockIndex *pindex = AddToBlockIndex(block); - if (!ReceivedBlockTransactions(block, state, pindex, blockPos, chainparams.GetConsensus())) - return error("LoadBlockIndex(): genesis block not accepted"); - } catch (const std::runtime_error& e) { - return error("LoadBlockIndex(): failed to initialize block database: %s", 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; diff --git a/src/validation.h b/src/validation.h index 95c8e5b93e..b44b72d2d4 100644 --- a/src/validation.h +++ b/src/validation.h @@ -256,12 +256,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 = NULL); -/** 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 */ |