@@ -243,13 +243,12 @@ void Shutdown(NodeContext& node)
243
243
}
244
244
245
245
// FlushStateToDisk generates a ChainStateFlushed callback, which we should avoid missing
246
- //
247
- // g_chainstate is referenced here directly (instead of ::ChainstateActive()) because it
248
- // may not have been initialized yet.
249
246
{
250
247
LOCK (cs_main);
251
- if (g_chainstate && g_chainstate->CanFlushToDisk ()) {
252
- g_chainstate->ForceFlushStateToDisk ();
248
+ for (CChainState* chainstate : g_chainman.GetAll ()) {
249
+ if (chainstate->CanFlushToDisk ()) {
250
+ chainstate->ForceFlushStateToDisk ();
251
+ }
253
252
}
254
253
}
255
254
@@ -273,9 +272,11 @@ void Shutdown(NodeContext& node)
273
272
274
273
{
275
274
LOCK (cs_main);
276
- if (g_chainstate && g_chainstate->CanFlushToDisk ()) {
277
- g_chainstate->ForceFlushStateToDisk ();
278
- g_chainstate->ResetCoinsViews ();
275
+ for (CChainState* chainstate : g_chainman.GetAll ()) {
276
+ if (chainstate->CanFlushToDisk ()) {
277
+ chainstate->ForceFlushStateToDisk ();
278
+ chainstate->ResetCoinsViews ();
279
+ }
279
280
}
280
281
pblocktree.reset ();
281
282
}
@@ -719,11 +720,17 @@ static void ThreadImport(std::vector<fs::path> vImportFiles)
719
720
}
720
721
721
722
// scan for better chains in the block chain database, that are not yet connected in the active best chain
722
- BlockValidationState state;
723
- if (!ActivateBestChain (state, chainparams)) {
724
- LogPrintf (" Failed to connect best block (%s)\n " , state.ToString ());
725
- StartShutdown ();
726
- return ;
723
+
724
+ // We can't hold cs_main during ActivateBestChain even though we're accessing
725
+ // the g_chainman unique_ptrs since ABC requires us not to be holding cs_main, so retrieve
726
+ // the relevant pointers before the ABC call.
727
+ for (CChainState* chainstate : WITH_LOCK (::cs_main, return g_chainman.GetAll ())) {
728
+ BlockValidationState state;
729
+ if (!chainstate->ActivateBestChain (state, chainparams, nullptr )) {
730
+ LogPrintf (" Failed to connect best block (%s)\n " , state.ToString ());
731
+ StartShutdown ();
732
+ return ;
733
+ }
727
734
}
728
735
729
736
if (gArgs .GetBoolArg (" -stopafterblockimport" , DEFAULT_STOPAFTERBLOCKIMPORT)) {
@@ -1513,17 +1520,18 @@ bool AppInitMain(NodeContext& node)
1513
1520
bool fLoaded = false ;
1514
1521
while (!fLoaded && !ShutdownRequested ()) {
1515
1522
bool fReset = fReindex ;
1523
+ auto is_coinsview_empty = [&](CChainState* chainstate) EXCLUSIVE_LOCKS_REQUIRED (::cs_main) {
1524
+ return fReset || fReindexChainState || chainstate->CoinsTip ().GetBestBlock ().IsNull ();
1525
+ };
1516
1526
std::string strLoadError;
1517
1527
1518
1528
uiInterface.InitMessage (_ (" Loading block index..." ).translated );
1519
1529
1520
1530
do {
1521
1531
const int64_t load_block_index_start_time = GetTimeMillis ();
1522
- bool is_coinsview_empty;
1523
1532
try {
1524
1533
LOCK (cs_main);
1525
- // This statement makes ::ChainstateActive() usable.
1526
- g_chainstate = MakeUnique<CChainState>();
1534
+ g_chainman.InitializeChainstate ();
1527
1535
UnloadBlockIndex ();
1528
1536
1529
1537
// new CBlockTreeDB tries to delete the existing file, which
@@ -1576,93 +1584,130 @@ bool AppInitMain(NodeContext& node)
1576
1584
// At this point we're either in reindex or we've loaded a useful
1577
1585
// block tree into BlockIndex()!
1578
1586
1579
- ::ChainstateActive ().InitCoinsDB(
1580
- /* cache_size_bytes */ nCoinDBCache,
1581
- /* in_memory */ false ,
1582
- /* should_wipe */ fReset || fReindexChainState );
1583
-
1584
- ::ChainstateActive ().CoinsErrorCatcher().AddReadErrCallback([]() {
1585
- uiInterface.ThreadSafeMessageBox (
1586
- _ (" Error reading from database, shutting down." ).translated ,
1587
- " " , CClientUIInterface::MSG_ERROR);
1588
- });
1589
-
1590
- // If necessary, upgrade from older database format.
1591
- // This is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
1592
- if (!::ChainstateActive ().CoinsDB ().Upgrade ()) {
1593
- strLoadError = _ (" Error upgrading chainstate database" ).translated ;
1594
- break ;
1595
- }
1596
-
1597
- // ReplayBlocks is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
1598
- if (!::ChainstateActive ().ReplayBlocks (chainparams)) {
1599
- strLoadError = _ (" Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate." ).translated ;
1600
- break ;
1601
- }
1602
-
1603
- // The on-disk coinsdb is now in a good state, create the cache
1604
- ::ChainstateActive ().InitCoinsCache();
1605
- assert (::ChainstateActive ().CanFlushToDisk ());
1587
+ bool failed_chainstate_init = false ;
1588
+
1589
+ for (CChainState* chainstate : g_chainman.GetAll ()) {
1590
+ LogPrintf (" Initializing chainstate %s\n " , chainstate->ToString ());
1591
+ chainstate->InitCoinsDB (
1592
+ /* cache_size_bytes */ nCoinDBCache,
1593
+ /* in_memory */ false ,
1594
+ /* should_wipe */ fReset || fReindexChainState );
1595
+
1596
+ chainstate->CoinsErrorCatcher ().AddReadErrCallback ([]() {
1597
+ uiInterface.ThreadSafeMessageBox (
1598
+ _ (" Error reading from database, shutting down." ).translated ,
1599
+ " " , CClientUIInterface::MSG_ERROR);
1600
+ });
1601
+
1602
+ // If necessary, upgrade from older database format.
1603
+ // This is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
1604
+ if (!chainstate->CoinsDB ().Upgrade ()) {
1605
+ strLoadError = _ (" Error upgrading chainstate database" ).translated ;
1606
+ failed_chainstate_init = true ;
1607
+ break ;
1608
+ }
1606
1609
1607
- is_coinsview_empty = fReset || fReindexChainState ||
1608
- ::ChainstateActive ().CoinsTip().GetBestBlock().IsNull();
1609
- if (!is_coinsview_empty) {
1610
- // LoadChainTip initializes the chain based on CoinsTip()'s best block
1611
- if (!::ChainstateActive ().LoadChainTip (chainparams)) {
1612
- strLoadError = _ (" Error initializing block database" ).translated ;
1610
+ // ReplayBlocks is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
1611
+ if (!chainstate->ReplayBlocks (chainparams)) {
1612
+ strLoadError = _ (" Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate." ).translated ;
1613
+ failed_chainstate_init = true ;
1613
1614
break ;
1614
1615
}
1615
- assert (::ChainActive ().Tip () != nullptr );
1616
+
1617
+ // The on-disk coinsdb is now in a good state, create the cache
1618
+ chainstate->InitCoinsCache ();
1619
+ assert (chainstate->CanFlushToDisk ());
1620
+
1621
+ if (!is_coinsview_empty (chainstate)) {
1622
+ // LoadChainTip initializes the chain based on CoinsTip()'s best block
1623
+ if (!chainstate->LoadChainTip (chainparams)) {
1624
+ strLoadError = _ (" Error initializing block database" ).translated ;
1625
+ failed_chainstate_init = true ;
1626
+ break ; // out of the per-chainstate loop
1627
+ }
1628
+ assert (chainstate->m_chain .Tip () != nullptr );
1629
+ }
1630
+ }
1631
+
1632
+ if (failed_chainstate_init) {
1633
+ break ; // out of the chainstate activation do-while
1616
1634
}
1617
1635
} catch (const std::exception& e) {
1618
1636
LogPrintf (" %s\n " , e.what ());
1619
1637
strLoadError = _ (" Error opening block database" ).translated ;
1620
1638
break ;
1621
1639
}
1622
1640
1623
- if (!fReset ) {
1624
- // Note that RewindBlockIndex MUST run even if we're about to -reindex-chainstate.
1625
- // It both disconnects blocks based on ::ChainActive(), and drops block data in
1626
- // BlockIndex() based on lack of available witness data.
1627
- uiInterface.InitMessage (_ (" Rewinding blocks..." ).translated );
1628
- if (!RewindBlockIndex (chainparams)) {
1629
- strLoadError = _ (" Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain" ).translated ;
1630
- break ;
1641
+ bool failed_rewind{false };
1642
+ // Can't hold cs_main while calling RewindBlockIndex, so retrieve the relevant
1643
+ // chainstates beforehand.
1644
+ for (CChainState* chainstate : WITH_LOCK (::cs_main, return g_chainman.GetAll ())) {
1645
+ if (!fReset ) {
1646
+ // Note that RewindBlockIndex MUST run even if we're about to -reindex-chainstate.
1647
+ // It both disconnects blocks based on the chainstate, and drops block data in
1648
+ // BlockIndex() based on lack of available witness data.
1649
+ uiInterface.InitMessage (_ (" Rewinding blocks..." ).translated );
1650
+ if (!chainstate->RewindBlockIndex (chainparams)) {
1651
+ strLoadError = _ (
1652
+ " Unable to rewind the database to a pre-fork state. "
1653
+ " You will need to redownload the blockchain" ).translated ;
1654
+ failed_rewind = true ;
1655
+ break ; // out of the per-chainstate loop
1656
+ }
1631
1657
}
1632
1658
}
1633
1659
1660
+ if (failed_rewind) {
1661
+ break ; // out of the chainstate activation do-while
1662
+ }
1663
+
1664
+ bool failed_verification = false ;
1665
+
1634
1666
try {
1635
1667
LOCK (cs_main);
1636
- if (!is_coinsview_empty) {
1637
- uiInterface.InitMessage (_ (" Verifying blocks..." ).translated );
1638
- if (fHavePruned && gArgs .GetArg (" -checkblocks" , DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) {
1639
- LogPrintf (" Prune: pruned datadir may not have more than %d blocks; only checking available blocks\n " ,
1640
- MIN_BLOCKS_TO_KEEP);
1641
- }
1642
-
1643
- CBlockIndex* tip = ::ChainActive ().Tip ();
1644
- RPCNotifyBlockChange (true , tip);
1645
- if (tip && tip->nTime > GetAdjustedTime () + 2 * 60 * 60 ) {
1646
- strLoadError = _ (" The block database contains a block which appears to be from the future. "
1647
- " This may be due to your computer's date and time being set incorrectly. "
1648
- " Only rebuild the block database if you are sure that your computer's date and time are correct" ).translated ;
1649
- break ;
1650
- }
1651
1668
1652
- if (!CVerifyDB ().VerifyDB (chainparams, &::ChainstateActive ().CoinsDB (), gArgs .GetArg (" -checklevel" , DEFAULT_CHECKLEVEL),
1653
- gArgs .GetArg (" -checkblocks" , DEFAULT_CHECKBLOCKS))) {
1654
- strLoadError = _ (" Corrupted block database detected" ).translated ;
1655
- break ;
1669
+ for (CChainState* chainstate : g_chainman.GetAll ()) {
1670
+ if (!is_coinsview_empty (chainstate)) {
1671
+ uiInterface.InitMessage (_ (" Verifying blocks..." ).translated );
1672
+ if (fHavePruned && gArgs .GetArg (" -checkblocks" , DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) {
1673
+ LogPrintf (" Prune: pruned datadir may not have more than %d blocks; only checking available blocks\n " ,
1674
+ MIN_BLOCKS_TO_KEEP);
1675
+ }
1676
+
1677
+ const CBlockIndex* tip = chainstate->m_chain .Tip ();
1678
+ RPCNotifyBlockChange (true , tip);
1679
+ if (tip && tip->nTime > GetAdjustedTime () + 2 * 60 * 60 ) {
1680
+ strLoadError = _ (" The block database contains a block which appears to be from the future. "
1681
+ " This may be due to your computer's date and time being set incorrectly. "
1682
+ " Only rebuild the block database if you are sure that your computer's date and time are correct" ).translated ;
1683
+ failed_verification = true ;
1684
+ break ;
1685
+ }
1686
+
1687
+ // Only verify the DB of the active chainstate. This is fixed in later
1688
+ // work when we allow VerifyDB to be parameterized by chainstate.
1689
+ if (&::ChainstateActive () == chainstate &&
1690
+ !CVerifyDB ().VerifyDB (
1691
+ chainparams, &chainstate->CoinsDB (),
1692
+ gArgs .GetArg (" -checklevel" , DEFAULT_CHECKLEVEL),
1693
+ gArgs .GetArg (" -checkblocks" , DEFAULT_CHECKBLOCKS))) {
1694
+ strLoadError = _ (" Corrupted block database detected" ).translated ;
1695
+ failed_verification = true ;
1696
+ break ;
1697
+ }
1656
1698
}
1657
1699
}
1658
1700
} catch (const std::exception& e) {
1659
1701
LogPrintf (" %s\n " , e.what ());
1660
1702
strLoadError = _ (" Error opening block database" ).translated ;
1703
+ failed_verification = true ;
1661
1704
break ;
1662
1705
}
1663
1706
1664
- fLoaded = true ;
1665
- LogPrintf (" block index %15dms\n " , GetTimeMillis () - load_block_index_start_time);
1707
+ if (!failed_verification) {
1708
+ fLoaded = true ;
1709
+ LogPrintf (" block index %15dms\n " , GetTimeMillis () - load_block_index_start_time);
1710
+ }
1666
1711
} while (false );
1667
1712
1668
1713
if (!fLoaded && !ShutdownRequested ()) {
@@ -1726,8 +1771,11 @@ bool AppInitMain(NodeContext& node)
1726
1771
LogPrintf (" Unsetting NODE_NETWORK on prune mode\n " );
1727
1772
nLocalServices = ServiceFlags (nLocalServices & ~NODE_NETWORK);
1728
1773
if (!fReindex ) {
1729
- uiInterface.InitMessage (_ (" Pruning blockstore..." ).translated );
1730
- ::ChainstateActive ().PruneAndFlush();
1774
+ LOCK (cs_main);
1775
+ for (CChainState* chainstate : g_chainman.GetAll ()) {
1776
+ uiInterface.InitMessage (_ (" Pruning blockstore..." ).translated );
1777
+ chainstate->PruneAndFlush ();
1778
+ }
1731
1779
}
1732
1780
}
1733
1781
0 commit comments