@@ -233,13 +233,12 @@ void Shutdown(NodeContext& node)
233
233
}
234
234
235
235
// FlushStateToDisk generates a ChainStateFlushed callback, which we should avoid missing
236
- //
237
- // g_chainstate is referenced here directly (instead of ::ChainstateActive()) because it
238
- // may not have been initialized yet.
239
236
{
240
237
LOCK (cs_main);
241
- if (g_chainstate && g_chainstate->CanFlushToDisk ()) {
242
- g_chainstate->ForceFlushStateToDisk ();
238
+ for (CChainState* chainstate : g_chainman.GetAll ()) {
239
+ if (chainstate->CanFlushToDisk ()) {
240
+ chainstate->ForceFlushStateToDisk ();
241
+ }
243
242
}
244
243
}
245
244
@@ -263,9 +262,11 @@ void Shutdown(NodeContext& node)
263
262
264
263
{
265
264
LOCK (cs_main);
266
- if (g_chainstate && g_chainstate->CanFlushToDisk ()) {
267
- g_chainstate->ForceFlushStateToDisk ();
268
- g_chainstate->ResetCoinsViews ();
265
+ for (CChainState* chainstate : g_chainman.GetAll ()) {
266
+ if (chainstate->CanFlushToDisk ()) {
267
+ chainstate->ForceFlushStateToDisk ();
268
+ chainstate->ResetCoinsViews ();
269
+ }
269
270
}
270
271
pblocktree.reset ();
271
272
}
@@ -1502,17 +1503,18 @@ bool AppInitMain(NodeContext& node)
1502
1503
bool fLoaded = false ;
1503
1504
while (!fLoaded && !ShutdownRequested ()) {
1504
1505
bool fReset = fReindex ;
1506
+ auto is_coinsview_empty = [&](CChainState* chainstate) EXCLUSIVE_LOCKS_REQUIRED (::cs_main) {
1507
+ return fReset || fReindexChainState || chainstate->CoinsTip ().GetBestBlock ().IsNull ();
1508
+ };
1505
1509
std::string strLoadError;
1506
1510
1507
1511
uiInterface.InitMessage (_ (" Loading block index..." ).translated );
1508
1512
1509
1513
do {
1510
1514
const int64_t load_block_index_start_time = GetTimeMillis ();
1511
- bool is_coinsview_empty;
1512
1515
try {
1513
1516
LOCK (cs_main);
1514
- // This statement makes ::ChainstateActive() usable.
1515
- g_chainstate = MakeUnique<CChainState>();
1517
+ g_chainman.InitializeChainstate ();
1516
1518
UnloadBlockIndex ();
1517
1519
1518
1520
// new CBlockTreeDB tries to delete the existing file, which
@@ -1565,93 +1567,129 @@ bool AppInitMain(NodeContext& node)
1565
1567
// At this point we're either in reindex or we've loaded a useful
1566
1568
// block tree into BlockIndex()!
1567
1569
1568
- ::ChainstateActive ().InitCoinsDB(
1569
- /* cache_size_bytes */ nCoinDBCache,
1570
- /* in_memory */ false ,
1571
- /* should_wipe */ fReset || fReindexChainState );
1572
-
1573
- ::ChainstateActive ().CoinsErrorCatcher().AddReadErrCallback([]() {
1574
- uiInterface.ThreadSafeMessageBox (
1575
- _ (" Error reading from database, shutting down." ).translated ,
1576
- " " , CClientUIInterface::MSG_ERROR);
1577
- });
1578
-
1579
- // If necessary, upgrade from older database format.
1580
- // This is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
1581
- if (!::ChainstateActive ().CoinsDB ().Upgrade ()) {
1582
- strLoadError = _ (" Error upgrading chainstate database" ).translated ;
1583
- break ;
1584
- }
1585
-
1586
- // ReplayBlocks is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
1587
- if (!::ChainstateActive ().ReplayBlocks (chainparams)) {
1588
- strLoadError = _ (" Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate." ).translated ;
1589
- break ;
1590
- }
1591
-
1592
- // The on-disk coinsdb is now in a good state, create the cache
1593
- ::ChainstateActive ().InitCoinsCache();
1594
- assert (::ChainstateActive ().CanFlushToDisk ());
1570
+ bool failed_chainstate_init = false ;
1571
+
1572
+ for (CChainState* chainstate : g_chainman.GetAll ()) {
1573
+ LogPrintf (" Initializing chainstate %s\n " , chainstate->ToString ());
1574
+ chainstate->InitCoinsDB (
1575
+ /* cache_size_bytes */ nCoinDBCache,
1576
+ /* in_memory */ false ,
1577
+ /* should_wipe */ fReset || fReindexChainState );
1578
+
1579
+ chainstate->CoinsErrorCatcher ().AddReadErrCallback ([]() {
1580
+ uiInterface.ThreadSafeMessageBox (
1581
+ _ (" Error reading from database, shutting down." ).translated ,
1582
+ " " , CClientUIInterface::MSG_ERROR);
1583
+ });
1584
+
1585
+ // If necessary, upgrade from older database format.
1586
+ // This is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
1587
+ if (!chainstate->CoinsDB ().Upgrade ()) {
1588
+ strLoadError = _ (" Error upgrading chainstate database" ).translated ;
1589
+ failed_chainstate_init = true ;
1590
+ break ;
1591
+ }
1595
1592
1596
- is_coinsview_empty = fReset || fReindexChainState ||
1597
- ::ChainstateActive ().CoinsTip().GetBestBlock().IsNull();
1598
- if (!is_coinsview_empty) {
1599
- // LoadChainTip initializes the chain based on CoinsTip()'s best block
1600
- if (!::ChainstateActive ().LoadChainTip (chainparams)) {
1601
- strLoadError = _ (" Error initializing block database" ).translated ;
1593
+ // ReplayBlocks is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
1594
+ if (!chainstate->ReplayBlocks (chainparams)) {
1595
+ strLoadError = _ (" Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate." ).translated ;
1596
+ failed_chainstate_init = true ;
1602
1597
break ;
1603
1598
}
1604
- assert (::ChainActive ().Tip () != nullptr );
1599
+
1600
+ // The on-disk coinsdb is now in a good state, create the cache
1601
+ chainstate->InitCoinsCache ();
1602
+ assert (chainstate->CanFlushToDisk ());
1603
+
1604
+ if (!is_coinsview_empty (chainstate)) {
1605
+ // LoadChainTip initializes the chain based on CoinsTip()'s best block
1606
+ if (!chainstate->LoadChainTip (chainparams)) {
1607
+ strLoadError = _ (" Error initializing block database" ).translated ;
1608
+ failed_chainstate_init = true ;
1609
+ break ; // out of the per-chainstate loop
1610
+ }
1611
+ assert (chainstate->m_chain .Tip () != nullptr );
1612
+ }
1613
+ }
1614
+
1615
+ if (failed_chainstate_init) {
1616
+ break ; // out of the chainstate activation do-while
1605
1617
}
1606
1618
} catch (const std::exception& e) {
1607
1619
LogPrintf (" %s\n " , e.what ());
1608
1620
strLoadError = _ (" Error opening block database" ).translated ;
1609
1621
break ;
1610
1622
}
1611
1623
1612
- if (!fReset ) {
1613
- // Note that RewindBlockIndex MUST run even if we're about to -reindex-chainstate.
1614
- // It both disconnects blocks based on ::ChainActive(), and drops block data in
1615
- // BlockIndex() based on lack of available witness data.
1616
- uiInterface.InitMessage (_ (" Rewinding blocks..." ).translated );
1617
- if (!::ChainstateActive ().RewindBlockIndex (chainparams)) {
1618
- strLoadError = _ (" Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain" ).translated ;
1619
- break ;
1624
+ bool failed_rewind{false };
1625
+
1626
+ for (CChainState* chainstate : g_chainman.GetAll ()) {
1627
+ if (!fReset ) {
1628
+ // Note that RewindBlockIndex MUST run even if we're about to -reindex-chainstate.
1629
+ // It both disconnects blocks based on the chainstate, and drops block data in
1630
+ // BlockIndex() based on lack of available witness data.
1631
+ uiInterface.InitMessage (_ (" Rewinding blocks..." ).translated );
1632
+ if (!chainstate->RewindBlockIndex (chainparams)) {
1633
+ strLoadError = _ (
1634
+ " Unable to rewind the database to a pre-fork state. "
1635
+ " You will need to redownload the blockchain" ).translated ;
1636
+ failed_rewind = true ;
1637
+ break ; // out of the per-chainstate loop
1638
+ }
1620
1639
}
1621
1640
}
1622
1641
1642
+ if (failed_rewind) {
1643
+ break ; // out of the chainstate activation do-while
1644
+ }
1645
+
1646
+ bool failed_verification = false ;
1647
+
1623
1648
try {
1624
1649
LOCK (cs_main);
1625
- if (!is_coinsview_empty) {
1626
- uiInterface.InitMessage (_ (" Verifying blocks..." ).translated );
1627
- if (fHavePruned && gArgs .GetArg (" -checkblocks" , DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) {
1628
- LogPrintf (" Prune: pruned datadir may not have more than %d blocks; only checking available blocks\n " ,
1629
- MIN_BLOCKS_TO_KEEP);
1630
- }
1631
1650
1632
- CBlockIndex* tip = ::ChainActive ().Tip ();
1633
- RPCNotifyBlockChange (true , tip);
1634
- if (tip && tip->nTime > GetAdjustedTime () + 2 * 60 * 60 ) {
1635
- strLoadError = _ (" The block database contains a block which appears to be from the future. "
1636
- " This may be due to your computer's date and time being set incorrectly. "
1637
- " Only rebuild the block database if you are sure that your computer's date and time are correct" ).translated ;
1638
- break ;
1639
- }
1640
-
1641
- if (!CVerifyDB ().VerifyDB (chainparams, &::ChainstateActive ().CoinsDB (), gArgs .GetArg (" -checklevel" , DEFAULT_CHECKLEVEL),
1642
- gArgs .GetArg (" -checkblocks" , DEFAULT_CHECKBLOCKS))) {
1643
- strLoadError = _ (" Corrupted block database detected" ).translated ;
1644
- break ;
1651
+ for (CChainState* chainstate : g_chainman.GetAll ()) {
1652
+ if (!is_coinsview_empty (chainstate)) {
1653
+ uiInterface.InitMessage (_ (" Verifying blocks..." ).translated );
1654
+ if (fHavePruned && gArgs .GetArg (" -checkblocks" , DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) {
1655
+ LogPrintf (" Prune: pruned datadir may not have more than %d blocks; only checking available blocks\n " ,
1656
+ MIN_BLOCKS_TO_KEEP);
1657
+ }
1658
+
1659
+ const CBlockIndex* tip = chainstate->m_chain .Tip ();
1660
+ RPCNotifyBlockChange (true , tip);
1661
+ if (tip && tip->nTime > GetAdjustedTime () + 2 * 60 * 60 ) {
1662
+ strLoadError = _ (" The block database contains a block which appears to be from the future. "
1663
+ " This may be due to your computer's date and time being set incorrectly. "
1664
+ " Only rebuild the block database if you are sure that your computer's date and time are correct" ).translated ;
1665
+ failed_verification = true ;
1666
+ break ;
1667
+ }
1668
+
1669
+ // Only verify the DB of the active chainstate. This is fixed in later
1670
+ // work when we allow VerifyDB to be parameterized by chainstate.
1671
+ if (&::ChainstateActive () == chainstate &&
1672
+ !CVerifyDB ().VerifyDB (
1673
+ chainparams, &chainstate->CoinsDB (),
1674
+ gArgs .GetArg (" -checklevel" , DEFAULT_CHECKLEVEL),
1675
+ gArgs .GetArg (" -checkblocks" , DEFAULT_CHECKBLOCKS))) {
1676
+ strLoadError = _ (" Corrupted block database detected" ).translated ;
1677
+ failed_verification = true ;
1678
+ break ;
1679
+ }
1645
1680
}
1646
1681
}
1647
1682
} catch (const std::exception& e) {
1648
1683
LogPrintf (" %s\n " , e.what ());
1649
1684
strLoadError = _ (" Error opening block database" ).translated ;
1685
+ failed_verification = true ;
1650
1686
break ;
1651
1687
}
1652
1688
1653
- fLoaded = true ;
1654
- LogPrintf (" block index %15dms\n " , GetTimeMillis () - load_block_index_start_time);
1689
+ if (!failed_verification) {
1690
+ fLoaded = true ;
1691
+ LogPrintf (" block index %15dms\n " , GetTimeMillis () - load_block_index_start_time);
1692
+ }
1655
1693
} while (false );
1656
1694
1657
1695
if (!fLoaded && !ShutdownRequested ()) {
@@ -1715,8 +1753,11 @@ bool AppInitMain(NodeContext& node)
1715
1753
LogPrintf (" Unsetting NODE_NETWORK on prune mode\n " );
1716
1754
nLocalServices = ServiceFlags (nLocalServices & ~NODE_NETWORK);
1717
1755
if (!fReindex ) {
1718
- uiInterface.InitMessage (_ (" Pruning blockstore..." ).translated );
1719
- ::ChainstateActive ().PruneAndFlush();
1756
+ LOCK (cs_main);
1757
+ for (CChainState* chainstate : g_chainman.GetAll ()) {
1758
+ uiInterface.InitMessage (_ (" Pruning blockstore..." ).translated );
1759
+ chainstate->PruneAndFlush ();
1760
+ }
1720
1761
}
1721
1762
}
1722
1763
0 commit comments