Changeset View
Changeset View
Standalone View
Standalone View
src/init.cpp
Show First 20 Lines • Show All 254 Lines • ▼ Show 20 Lines | void Shutdown(NodeContext &node) { | ||||
if (::g_mempool.IsLoaded() && | if (::g_mempool.IsLoaded() && | ||||
gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) { | gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) { | ||||
DumpMempool(::g_mempool); | DumpMempool(::g_mempool); | ||||
} | } | ||||
// FlushStateToDisk generates a ChainStateFlushed callback, which we should | // FlushStateToDisk generates a ChainStateFlushed callback, which we should | ||||
// avoid missing | // avoid missing | ||||
// g_chainstate is referenced here directly (instead of | |||||
// ::ChainstateActive()) because it may not have been initialized yet. | |||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
if (g_chainstate && g_chainstate->CanFlushToDisk()) { | for (CChainState *chainstate : g_chainman.GetAll()) { | ||||
g_chainstate->ForceFlushStateToDisk(); | if (chainstate->CanFlushToDisk()) { | ||||
chainstate->ForceFlushStateToDisk(); | |||||
} | |||||
} | } | ||||
} | } | ||||
// After there are no more peers/RPC left to give us new data which may | // After there are no more peers/RPC left to give us new data which may | ||||
// generate CValidationInterface callbacks, flush them... | // generate CValidationInterface callbacks, flush them... | ||||
GetMainSignals().FlushBackgroundCallbacks(); | GetMainSignals().FlushBackgroundCallbacks(); | ||||
// Any future callbacks will be dropped. This should absolutely be safe - if | // Any future callbacks will be dropped. This should absolutely be safe - if | ||||
// missing a callback results in an unrecoverable situation, unclean | // missing a callback results in an unrecoverable situation, unclean | ||||
// shutdown would too. The only reason to do the above flushes is to let the | // shutdown would too. The only reason to do the above flushes is to let the | ||||
// wallet catch up with our current chain to avoid any strange pruning edge | // wallet catch up with our current chain to avoid any strange pruning edge | ||||
// cases and make next startup faster by avoiding rescan. | // cases and make next startup faster by avoiding rescan. | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
if (g_chainstate && g_chainstate->CanFlushToDisk()) { | for (CChainState *chainstate : g_chainman.GetAll()) { | ||||
g_chainstate->ForceFlushStateToDisk(); | if (chainstate->CanFlushToDisk()) { | ||||
g_chainstate->ResetCoinsViews(); | chainstate->ForceFlushStateToDisk(); | ||||
chainstate->ResetCoinsViews(); | |||||
} | |||||
} | } | ||||
pblocktree.reset(); | pblocktree.reset(); | ||||
} | } | ||||
for (const auto &client : node.chain_clients) { | for (const auto &client : node.chain_clients) { | ||||
client->stop(); | client->stop(); | ||||
} | } | ||||
#if ENABLE_ZMQ | #if ENABLE_ZMQ | ||||
▲ Show 20 Lines • Show All 2,123 Lines • ▼ Show 20 Lines | #endif | ||||
LogPrintf("* Using %.1f MiB for in-memory UTXO set (plus up to %.1f MiB of " | LogPrintf("* Using %.1f MiB for in-memory UTXO set (plus up to %.1f MiB of " | ||||
"unused mempool space)\n", | "unused mempool space)\n", | ||||
nCoinCacheUsage * (1.0 / 1024 / 1024), | nCoinCacheUsage * (1.0 / 1024 / 1024), | ||||
nMempoolSizeMax * (1.0 / 1024 / 1024)); | nMempoolSizeMax * (1.0 / 1024 / 1024)); | ||||
bool fLoaded = false; | bool fLoaded = false; | ||||
while (!fLoaded && !ShutdownRequested()) { | while (!fLoaded && !ShutdownRequested()) { | ||||
const bool fReset = fReindex; | const bool fReset = fReindex; | ||||
auto is_coinsview_empty = | |||||
[&](CChainState *chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { | |||||
return fReset || fReindexChainState || | |||||
chainstate->CoinsTip().GetBestBlock().IsNull(); | |||||
}; | |||||
bilingual_str strLoadError; | bilingual_str strLoadError; | ||||
uiInterface.InitMessage(_("Loading block index...").translated); | uiInterface.InitMessage(_("Loading block index...").translated); | ||||
do { | do { | ||||
bool failed_verification = false; | |||||
const int64_t load_block_index_start_time = GetTimeMillis(); | const int64_t load_block_index_start_time = GetTimeMillis(); | ||||
try { | try { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
// This statement makes ::ChainstateActive() usable. | g_chainman.InitializeChainstate(); | ||||
g_chainstate = std::make_unique<CChainState>(); | |||||
UnloadBlockIndex(); | UnloadBlockIndex(); | ||||
// new CBlockTreeDB tries to delete the existing file, which | // new CBlockTreeDB tries to delete the existing file, which | ||||
// fails if it's still open from the previous loop. Close it | // fails if it's still open from the previous loop. Close it | ||||
// first: | // first: | ||||
pblocktree.reset(); | pblocktree.reset(); | ||||
pblocktree.reset( | pblocktree.reset( | ||||
new CBlockTreeDB(nBlockTreeDBCache, false, fReset)); | new CBlockTreeDB(nBlockTreeDBCache, false, fReset)); | ||||
▲ Show 20 Lines • Show All 63 Lines • ▼ Show 20 Lines | while (!fLoaded && !ShutdownRequested()) { | ||||
if (!fReindex && !LoadGenesisBlock(chainparams)) { | if (!fReindex && !LoadGenesisBlock(chainparams)) { | ||||
strLoadError = _("Error initializing block database"); | strLoadError = _("Error initializing block database"); | ||||
break; | break; | ||||
} | } | ||||
// At this point we're either in reindex or we've loaded a | // At this point we're either in reindex or we've loaded a | ||||
// useful block tree into BlockIndex()! | // useful block tree into BlockIndex()! | ||||
::ChainstateActive().InitCoinsDB( | bool failed_chainstate_init = false; | ||||
for (CChainState *chainstate : g_chainman.GetAll()) { | |||||
LogPrintf("Initializing chainstate %s\n", | |||||
chainstate->ToString()); | |||||
chainstate->InitCoinsDB( | |||||
/* cache_size_bytes */ nCoinDBCache, | /* cache_size_bytes */ nCoinDBCache, | ||||
/* in_memory */ false, | /* in_memory */ false, | ||||
/* should_wipe */ fReset || fReindexChainState); | /* should_wipe */ fReset || fReindexChainState); | ||||
::ChainstateActive().CoinsErrorCatcher().AddReadErrCallback( | chainstate->CoinsErrorCatcher().AddReadErrCallback([]() { | ||||
[]() { | |||||
uiInterface.ThreadSafeMessageBox( | uiInterface.ThreadSafeMessageBox( | ||||
_("Error reading from database, shutting down."), | _("Error reading from database, shutting down."), | ||||
"", CClientUIInterface::MSG_ERROR); | "", CClientUIInterface::MSG_ERROR); | ||||
}); | }); | ||||
// If necessary, upgrade from older database format. | // If necessary, upgrade from older database format. | ||||
// This is a no-op if we cleared the coinsviewdb with -reindex | // This is a no-op if we cleared the coinsviewdb with | ||||
// or -reindex-chainstate | // -reindex or -reindex-chainstate | ||||
if (!::ChainstateActive().CoinsDB().Upgrade()) { | if (!chainstate->CoinsDB().Upgrade()) { | ||||
strLoadError = _("Error upgrading chainstate database"); | strLoadError = _("Error upgrading chainstate database"); | ||||
failed_chainstate_init = true; | |||||
break; | break; | ||||
} | } | ||||
// ReplayBlocks is a no-op if we cleared the coinsviewdb with | // ReplayBlocks is a no-op if we cleared the coinsviewdb | ||||
// -reindex or -reindex-chainstate | // with -reindex or -reindex-chainstate | ||||
if (!::ChainstateActive().ReplayBlocks(params)) { | if (!chainstate->ReplayBlocks(params)) { | ||||
strLoadError = | strLoadError = _( | ||||
_("Unable to replay blocks. You will need to rebuild " | "Unable to replay blocks. You will need to rebuild " | ||||
"the database using -reindex-chainstate."); | "the database using -reindex-chainstate."); | ||||
failed_chainstate_init = true; | |||||
break; | break; | ||||
} | } | ||||
// The on-disk coinsdb is now in a good state, create the cache | // The on-disk coinsdb is now in a good state, create the | ||||
::ChainstateActive().InitCoinsCache(); | // cache | ||||
assert(::ChainstateActive().CanFlushToDisk()); | chainstate->InitCoinsCache(); | ||||
assert(chainstate->CanFlushToDisk()); | |||||
bool is_coinsview_empty = | |||||
fReset || fReindexChainState || | if (!is_coinsview_empty(chainstate)) { | ||||
::ChainstateActive().CoinsTip().GetBestBlock().IsNull(); | // LoadChainTip initializes the chain based on | ||||
if (!is_coinsview_empty) { | // CoinsTip()'s best block | ||||
// LoadChainTip initializes the chain based on CoinsTip()'s | if (!chainstate->LoadChainTip(chainparams)) { | ||||
// best block | strLoadError = | ||||
if (!::ChainstateActive().LoadChainTip(chainparams)) { | _("Error initializing block database"); | ||||
strLoadError = _("Error initializing block database"); | failed_chainstate_init = true; | ||||
// out of the per-chainstate loop | |||||
break; | |||||
} | |||||
assert(chainstate->m_chain.Tip() != nullptr); | |||||
} | |||||
} | |||||
if (failed_chainstate_init) { | |||||
// out of the chainstate activation do-while | |||||
break; | break; | ||||
} | } | ||||
assert(::ChainActive().Tip() != nullptr); | |||||
for (CChainState *chainstate : g_chainman.GetAll()) { | |||||
if (!is_coinsview_empty(chainstate)) { | |||||
uiInterface.InitMessage( | uiInterface.InitMessage( | ||||
_("Verifying blocks...").translated); | _("Verifying blocks...").translated); | ||||
if (fHavePruned && | if (fHavePruned && | ||||
gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > | gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > | ||||
MIN_BLOCKS_TO_KEEP) { | MIN_BLOCKS_TO_KEEP) { | ||||
LogPrintf( | LogPrintf( | ||||
"Prune: pruned datadir may not have more than %d " | "Prune: pruned datadir may not have more than " | ||||
"blocks; only checking available blocks\n", | "%d blocks; only checking available blocks\n", | ||||
MIN_BLOCKS_TO_KEEP); | MIN_BLOCKS_TO_KEEP); | ||||
} | } | ||||
CBlockIndex *tip = ::ChainActive().Tip(); | const CBlockIndex *tip = chainstate->m_chain.Tip(); | ||||
RPCNotifyBlockChange(tip); | RPCNotifyBlockChange(tip); | ||||
if (tip && tip->nTime > | if (tip && | ||||
GetAdjustedTime() + MAX_FUTURE_BLOCK_TIME) { | tip->nTime > GetAdjustedTime() + 2 * 60 * 60) { | ||||
strLoadError = | strLoadError = | ||||
_("The block database contains a block which " | _("The block database contains a block which " | ||||
"appears to be from the future. This may be due " | "appears to be from the future. " | ||||
"to your computer's date and time being set " | "This may be due to your computer's date and " | ||||
"incorrectly. Only rebuild the block database if " | "time being set incorrectly. " | ||||
"you are sure that your computer's date and time " | "Only rebuild the block database if you are " | ||||
"are correct"); | "sure that your computer's date and time are " | ||||
"correct"); | |||||
failed_verification = true; | |||||
break; | break; | ||||
} | } | ||||
if (!CVerifyDB().VerifyDB( | // Only verify the DB of the active chainstate. This is | ||||
config, &::ChainstateActive().CoinsDB(), | // fixed in later work when we allow VerifyDB to be | ||||
// parameterized by chainstate. | |||||
if (&::ChainstateActive() == chainstate && | |||||
!CVerifyDB().VerifyDB( | |||||
config, &chainstate->CoinsDB(), | |||||
gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL), | gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL), | ||||
gArgs.GetArg("-checkblocks", | gArgs.GetArg("-checkblocks", | ||||
DEFAULT_CHECKBLOCKS))) { | DEFAULT_CHECKBLOCKS))) { | ||||
strLoadError = _("Corrupted block database detected"); | strLoadError = | ||||
_("Corrupted block database detected"); | |||||
failed_verification = true; | |||||
break; | break; | ||||
} | } | ||||
} | } | ||||
} | |||||
} catch (const std::exception &e) { | } catch (const std::exception &e) { | ||||
LogPrintf("%s\n", e.what()); | LogPrintf("%s\n", e.what()); | ||||
strLoadError = _("Error opening block database"); | strLoadError = _("Error opening block database"); | ||||
failed_verification = true; | |||||
break; | break; | ||||
} | } | ||||
if (!failed_verification) { | |||||
fLoaded = true; | fLoaded = true; | ||||
LogPrintf(" block index %15dms\n", | LogPrintf(" block index %15dms\n", | ||||
GetTimeMillis() - load_block_index_start_time); | GetTimeMillis() - load_block_index_start_time); | ||||
} | |||||
} while (false); | } while (false); | ||||
if (!fLoaded && !ShutdownRequested()) { | if (!fLoaded && !ShutdownRequested()) { | ||||
// first suggest a reindex | // first suggest a reindex | ||||
if (!fReset) { | if (!fReset) { | ||||
bool fRet = uiInterface.ThreadSafeQuestion( | bool fRet = uiInterface.ThreadSafeQuestion( | ||||
strLoadError + Untranslated(".\n\n") + | strLoadError + Untranslated(".\n\n") + | ||||
_("Do you want to rebuild the block database now?"), | _("Do you want to rebuild the block database now?"), | ||||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | #endif | ||||
// Step 10: data directory maintenance | // Step 10: data directory maintenance | ||||
// if pruning, unset the service bit and perform the initial blockstore | // if pruning, unset the service bit and perform the initial blockstore | ||||
// prune after any wallet rescanning has taken place. | // prune after any wallet rescanning has taken place. | ||||
if (fPruneMode) { | if (fPruneMode) { | ||||
LogPrintf("Unsetting NODE_NETWORK on prune mode\n"); | LogPrintf("Unsetting NODE_NETWORK on prune mode\n"); | ||||
nLocalServices = ServiceFlags(nLocalServices & ~NODE_NETWORK); | nLocalServices = ServiceFlags(nLocalServices & ~NODE_NETWORK); | ||||
if (!fReindex) { | if (!fReindex) { | ||||
LOCK(cs_main); | |||||
for (CChainState *chainstate : g_chainman.GetAll()) { | |||||
uiInterface.InitMessage(_("Pruning blockstore...").translated); | uiInterface.InitMessage(_("Pruning blockstore...").translated); | ||||
::ChainstateActive().PruneAndFlush(); | chainstate->PruneAndFlush(); | ||||
} | |||||
} | } | ||||
} | } | ||||
// Step 11: import blocks | // Step 11: import blocks | ||||
if (!CheckDiskSpace(GetDataDir())) { | if (!CheckDiskSpace(GetDataDir())) { | ||||
InitError( | InitError( | ||||
strprintf(_("Error: Disk space is low for %s"), GetDataDir())); | strprintf(_("Error: Disk space is low for %s"), GetDataDir())); | ||||
return false; | return false; | ||||
▲ Show 20 Lines • Show All 158 Lines • Show Last 20 Lines |