@@ -293,13 +293,12 @@ void PrepareShutdown(NodeContext& node)
293293 }
294294
295295 // FlushStateToDisk generates a ChainStateFlushed callback, which we should avoid missing
296- //
297- // g_chainstate is referenced here directly (instead of ::ChainstateActive()) because it
298- // may not have been initialized yet.
299296 {
300297 LOCK (cs_main);
301- if (g_chainstate && g_chainstate->CanFlushToDisk ()) {
302- g_chainstate->ForceFlushStateToDisk ();
298+ for (CChainState* chainstate : g_chainman.GetAll ()) {
299+ if (chainstate->CanFlushToDisk ()) {
300+ chainstate->ForceFlushStateToDisk ();
301+ }
303302 }
304303 }
305304
@@ -323,9 +322,11 @@ void PrepareShutdown(NodeContext& node)
323322
324323 {
325324 LOCK (cs_main);
326- if (g_chainstate && g_chainstate->CanFlushToDisk ()) {
327- g_chainstate->ForceFlushStateToDisk ();
328- g_chainstate->ResetCoinsViews ();
325+ for (CChainState* chainstate : g_chainman.GetAll ()) {
326+ if (chainstate->CanFlushToDisk ()) {
327+ chainstate->ForceFlushStateToDisk ();
328+ chainstate->ResetCoinsViews ();
329+ }
329330 }
330331 pblocktree.reset ();
331332 llmq::DestroyLLMQSystem ();
@@ -931,11 +932,17 @@ static void ThreadImport(std::vector<fs::path> vImportFiles)
931932 }
932933
933934 // scan for better chains in the block chain database, that are not yet connected in the active best chain
934- CValidationState state;
935- if (!ActivateBestChain (state, chainparams)) {
936- LogPrintf (" Failed to connect best block (%s)\n " , FormatStateMessage (state));
937- StartShutdown ();
938- return ;
935+
936+ // We can't hold cs_main during ActivateBestChain even though we're accessing
937+ // the g_chainman unique_ptrs since ABC requires us not to be holding cs_main, so retrieve
938+ // the relevant pointers before the ABC call.
939+ for (CChainState* chainstate : WITH_LOCK (::cs_main, return g_chainman.GetAll ())) {
940+ CValidationState state;
941+ if (!chainstate->ActivateBestChain (state, chainparams, nullptr )) {
942+ LogPrintf (" Failed to connect best block (%s)\n " , FormatStateMessage (state));
943+ StartShutdown ();
944+ return ;
945+ }
939946 }
940947
941948 if (gArgs .GetBoolArg (" -stopafterblockimport" , DEFAULT_STOPAFTERBLOCKIMPORT)) {
@@ -1957,17 +1964,20 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
19571964
19581965 while (!fLoaded && !ShutdownRequested ()) {
19591966 bool fReset = fReindex ;
1967+ auto is_coinsview_empty = [&](CChainState* chainstate) EXCLUSIVE_LOCKS_REQUIRED (::cs_main) {
1968+ return fReset || fReindexChainState || chainstate->CoinsTip ().GetBestBlock ().IsNull ();
1969+ };
19601970 bilingual_str strLoadError;
19611971
19621972 uiInterface.InitMessage (_ (" Loading block index..." ).translated );
19631973
19641974 do {
1975+ bool failed_verification = false ;
19651976 const int64_t load_block_index_start_time = GetTimeMillis ();
1966- bool is_coinsview_empty;
1977+
19671978 try {
19681979 LOCK (cs_main);
1969- // This statement makes ::ChainstateActive() usable.
1970- g_chainstate = MakeUnique<CChainState>();
1980+ g_chainman.InitializeChainstate ();
19711981 UnloadBlockIndex ();
19721982
19731983 // new CBlockTreeDB tries to delete the existing file, which
@@ -2056,82 +2066,108 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
20562066 // At this point we're either in reindex or we've loaded a useful
20572067 // block tree into BlockIndex()!
20582068
2059- ::ChainstateActive ().InitCoinsDB(
2060- /* cache_size_bytes */ nCoinDBCache,
2061- /* in_memory */ false ,
2062- /* should_wipe */ fReset || fReindexChainState );
2063-
2064- ::ChainstateActive ().CoinsErrorCatcher().AddReadErrCallback([]() {
2065- uiInterface.ThreadSafeMessageBox (
2066- _ (" Error reading from database, shutting down." ),
2067- " " , CClientUIInterface::MSG_ERROR);
2068- });
2069-
2070- // If necessary, upgrade from older database format.
2071- // This is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
2072- if (!::ChainstateActive ().CoinsDB ().Upgrade ()) {
2073- strLoadError = _ (" Error upgrading chainstate database" );
2074- break ;
2075- }
2076-
2077- // ReplayBlocks is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
2078- if (!::ChainstateActive ().ReplayBlocks (chainparams)) {
2079- strLoadError = _ (" Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate." );
2080- break ;
2081- }
2082-
2083- // The on-disk coinsdb is now in a good state, create the cache
2084- ::ChainstateActive ().InitCoinsCache();
2085- assert (::ChainstateActive ().CanFlushToDisk ());
2069+ bool failed_chainstate_init = false ;
2070+ for (CChainState* chainstate : g_chainman.GetAll ()) {
2071+ LogPrintf (" Initializing chainstate %s\n " , chainstate->ToString ());
2072+ chainstate->InitCoinsDB (
2073+ /* cache_size_bytes */ nCoinDBCache,
2074+ /* in_memory */ false ,
2075+ /* should_wipe */ fReset || fReindexChainState );
2076+
2077+ chainstate->CoinsErrorCatcher ().AddReadErrCallback ([]() {
2078+ uiInterface.ThreadSafeMessageBox (
2079+ _ (" Error reading from database, shutting down." ),
2080+ " " , CClientUIInterface::MSG_ERROR);
2081+ });
2082+
2083+ // If necessary, upgrade from older database format.
2084+ // This is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
2085+ if (!chainstate->CoinsDB ().Upgrade ()) {
2086+ strLoadError = _ (" Error upgrading chainstate database" );
2087+ failed_chainstate_init = true ;
2088+ break ;
2089+ }
20862090
2087- // flush evodb
2088- if (!evoDb->CommitRootTransaction ()) {
2089- strLoadError = _ (" Failed to commit EvoDB" );
2090- break ;
2091- }
2091+ // ReplayBlocks is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
2092+ if (!chainstate->ReplayBlocks (chainparams)) {
2093+ strLoadError = _ (" Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate." );
2094+ failed_chainstate_init = true ;
2095+ break ;
2096+ }
20922097
2093- is_coinsview_empty = fReset || fReindexChainState ||
2094- ::ChainstateActive ().CoinsTip().GetBestBlock().IsNull();
2095- if (!is_coinsview_empty) {
2096- // LoadChainTip initializes the chain based on CoinsTip()'s best block
2097- if (!::ChainstateActive ().LoadChainTip (chainparams)) {
2098- strLoadError = _ (" Error initializing block database" );
2098+ // The on-disk coinsdb is now in a good state, create the cache
2099+ chainstate->InitCoinsCache ();
2100+ assert (chainstate->CanFlushToDisk ());
2101+
2102+ // flush evodb
2103+ // TODO: CEvoDB instance should probably be a part of CChainState
2104+ // (for multiple chainstates to actually work in parallel)
2105+ // and not a global
2106+ if (&::ChainstateActive () == chainstate && !evoDb->CommitRootTransaction ()) {
2107+ strLoadError = _ (" Failed to commit EvoDB" );
2108+ failed_chainstate_init = true ;
20992109 break ;
21002110 }
2101- assert (::ChainActive ().Tip () != NULL );
2111+
2112+ if (!is_coinsview_empty (chainstate)) {
2113+ // LoadChainTip initializes the chain based on CoinsTip()'s best block
2114+ if (!chainstate->LoadChainTip (chainparams)) {
2115+ strLoadError = _ (" Error initializing block database" );
2116+ failed_chainstate_init = true ;
2117+ break ; // out of the per-chainstate loop
2118+ }
2119+ assert (chainstate->m_chain .Tip () != nullptr );
2120+ }
21022121 }
21032122
2104- if (is_coinsview_empty && !evoDb->IsEmpty ()) {
2105- // EvoDB processed some blocks earlier but we have no blocks anymore, something is wrong
2106- strLoadError = _ (" Error initializing block database" );
2107- break ;
2123+ if (failed_chainstate_init) {
2124+ break ; // out of the chainstate activation do-while
21082125 }
21092126
21102127 if (!deterministicMNManager->UpgradeDBIfNeeded () || !llmq::quorumBlockProcessor->UpgradeDB ()) {
21112128 strLoadError = _ (" Error upgrading evo database" );
21122129 break ;
21132130 }
21142131
2115- if (!is_coinsview_empty) {
2116- uiInterface.InitMessage (_ (" Verifying blocks..." ).translated );
2117- if (fHavePruned && gArgs .GetArg (" -checkblocks" , DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) {
2118- LogPrintf (" Prune: pruned datadir may not have more than %d blocks; only checking available blocks\n " ,
2119- MIN_BLOCKS_TO_KEEP);
2120- }
2121-
2122- CBlockIndex* tip = ::ChainActive ().Tip ();
2123- RPCNotifyBlockChange (true , tip);
2124- if (tip && tip->nTime > GetAdjustedTime () + 2 * 60 * 60 ) {
2125- strLoadError = _ (" The block database contains a block which appears to be from the future. "
2126- " This may be due to your computer's date and time being set incorrectly. "
2127- " Only rebuild the block database if you are sure that your computer's date and time are correct" );
2128- break ;
2129- }
2130-
2131- if (!CVerifyDB ().VerifyDB (chainparams, &::ChainstateActive ().CoinsDB (), gArgs .GetArg (" -checklevel" , DEFAULT_CHECKLEVEL),
2132- gArgs .GetArg (" -checkblocks" , DEFAULT_CHECKBLOCKS))) {
2133- strLoadError = _ (" Corrupted block database detected" );
2134- break ;
2132+ for (CChainState* chainstate : g_chainman.GetAll ()) {
2133+ if (!is_coinsview_empty (chainstate)) {
2134+ uiInterface.InitMessage (_ (" Verifying blocks..." ).translated );
2135+ if (fHavePruned && gArgs .GetArg (" -checkblocks" , DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) {
2136+ LogPrintf (" Prune: pruned datadir may not have more than %d blocks; only checking available blocks\n " ,
2137+ MIN_BLOCKS_TO_KEEP);
2138+ }
2139+
2140+ CBlockIndex* tip = chainstate->m_chain .Tip ();
2141+ RPCNotifyBlockChange (true , tip);
2142+ if (tip && tip->nTime > GetAdjustedTime () + 2 * 60 * 60 ) {
2143+ strLoadError = _ (" The block database contains a block which appears to be from the future. "
2144+ " This may be due to your computer's date and time being set incorrectly. "
2145+ " Only rebuild the block database if you are sure that your computer's date and time are correct" );
2146+ failed_verification = true ;
2147+ break ;
2148+ }
2149+
2150+ // Only verify the DB of the active chainstate. This is fixed in later
2151+ // work when we allow VerifyDB to be parameterized by chainstate.
2152+ if (&::ChainstateActive () == chainstate &&
2153+ !CVerifyDB ().VerifyDB (
2154+ chainparams, &chainstate->CoinsDB (),
2155+ gArgs .GetArg (" -checklevel" , DEFAULT_CHECKLEVEL),
2156+ gArgs .GetArg (" -checkblocks" , DEFAULT_CHECKBLOCKS))) {
2157+ strLoadError = _ (" Corrupted block database detected" );
2158+ failed_verification = true ;
2159+ break ;
2160+ }
2161+ } else {
2162+ // TODO: CEvoDB instance should probably be a part of CChainState
2163+ // (for multiple chainstates to actually work in parallel)
2164+ // and not a global
2165+ if (&::ChainstateActive () == chainstate && !evoDb->IsEmpty ()) {
2166+ // EvoDB processed some blocks earlier but we have no blocks anymore, something is wrong
2167+ strLoadError = _ (" Error initializing block database" );
2168+ failed_verification = true ;
2169+ break ;
2170+ }
21352171 }
21362172
21372173 if (gArgs .GetArg (" -checklevel" , DEFAULT_CHECKLEVEL) >= 3 ) {
@@ -2141,11 +2177,14 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
21412177 } catch (const std::exception& e) {
21422178 LogPrintf (" %s\n " , e.what ());
21432179 strLoadError = _ (" Error opening block database" );
2180+ failed_verification = true ;
21442181 break ;
21452182 }
21462183
2147- fLoaded = true ;
2148- LogPrintf (" block index %15dms\n " , GetTimeMillis () - load_block_index_start_time);
2184+ if (!failed_verification) {
2185+ fLoaded = true ;
2186+ LogPrintf (" block index %15dms\n " , GetTimeMillis () - load_block_index_start_time);
2187+ }
21492188 } while (false );
21502189
21512190 if (!fLoaded && !ShutdownRequested ()) {
@@ -2217,8 +2256,11 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
22172256 LogPrintf (" Unsetting NODE_NETWORK on prune mode\n " );
22182257 nLocalServices = ServiceFlags (nLocalServices & ~NODE_NETWORK);
22192258 if (!fReindex ) {
2220- uiInterface.InitMessage (_ (" Pruning blockstore..." ).translated );
2221- ::ChainstateActive ().PruneAndFlush();
2259+ LOCK (cs_main);
2260+ for (CChainState* chainstate : g_chainman.GetAll ()) {
2261+ uiInterface.InitMessage (_ (" Pruning blockstore..." ).translated );
2262+ chainstate->PruneAndFlush ();
2263+ }
22222264 }
22232265 }
22242266
0 commit comments