Skip to content

Commit 4ae29f5

Browse files
committed
use ChainstateManager to initialize chainstate
This allows us to easily initialize multiple chainstates on startup in future commits. It retires the g_chainstate global in lieu of g_chainman.
1 parent 5b690f0 commit 4ae29f5

File tree

5 files changed

+165
-122
lines changed

5 files changed

+165
-122
lines changed

src/init.cpp

Lines changed: 117 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -233,13 +233,12 @@ void Shutdown(NodeContext& node)
233233
}
234234

235235
// 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.
239236
{
240237
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+
}
243242
}
244243
}
245244

@@ -263,9 +262,11 @@ void Shutdown(NodeContext& node)
263262

264263
{
265264
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+
}
269270
}
270271
pblocktree.reset();
271272
}
@@ -1502,17 +1503,18 @@ bool AppInitMain(NodeContext& node)
15021503
bool fLoaded = false;
15031504
while (!fLoaded && !ShutdownRequested()) {
15041505
bool fReset = fReindex;
1506+
auto is_coinsview_empty = [&](CChainState* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
1507+
return fReset || fReindexChainState || chainstate->CoinsTip().GetBestBlock().IsNull();
1508+
};
15051509
std::string strLoadError;
15061510

15071511
uiInterface.InitMessage(_("Loading block index...").translated);
15081512

15091513
do {
15101514
const int64_t load_block_index_start_time = GetTimeMillis();
1511-
bool is_coinsview_empty;
15121515
try {
15131516
LOCK(cs_main);
1514-
// This statement makes ::ChainstateActive() usable.
1515-
g_chainstate = MakeUnique<CChainState>();
1517+
g_chainman.InitializeChainstate();
15161518
UnloadBlockIndex();
15171519

15181520
// new CBlockTreeDB tries to delete the existing file, which
@@ -1565,93 +1567,129 @@ bool AppInitMain(NodeContext& node)
15651567
// At this point we're either in reindex or we've loaded a useful
15661568
// block tree into BlockIndex()!
15671569

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+
}
15951592

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;
16021597
break;
16031598
}
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
16051617
}
16061618
} catch (const std::exception& e) {
16071619
LogPrintf("%s\n", e.what());
16081620
strLoadError = _("Error opening block database").translated;
16091621
break;
16101622
}
16111623

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+
}
16201639
}
16211640
}
16221641

1642+
if (failed_rewind) {
1643+
break; // out of the chainstate activation do-while
1644+
}
1645+
1646+
bool failed_verification = false;
1647+
16231648
try {
16241649
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-
}
16311650

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+
}
16451680
}
16461681
}
16471682
} catch (const std::exception& e) {
16481683
LogPrintf("%s\n", e.what());
16491684
strLoadError = _("Error opening block database").translated;
1685+
failed_verification = true;
16501686
break;
16511687
}
16521688

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+
}
16551693
} while(false);
16561694

16571695
if (!fLoaded && !ShutdownRequested()) {
@@ -1715,8 +1753,11 @@ bool AppInitMain(NodeContext& node)
17151753
LogPrintf("Unsetting NODE_NETWORK on prune mode\n");
17161754
nLocalServices = ServiceFlags(nLocalServices & ~NODE_NETWORK);
17171755
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+
}
17201761
}
17211762
}
17221763

src/rpc/blockchain.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1319,7 +1319,7 @@ static UniValue getchaintips(const JSONRPCRequest& request)
13191319
/*
13201320
* Idea: the set of chain tips is ::ChainActive().tip, plus orphan blocks which do not have another orphan building off of them.
13211321
* Algorithm:
1322-
* - Make one pass through g_blockman.m_block_index, picking out the orphan blocks, and also storing a set of the orphan block's pprev pointers.
1322+
* - Make one pass through BlockIndex(), picking out the orphan blocks, and also storing a set of the orphan block's pprev pointers.
13231323
* - Iterate through the orphan blocks. If the block isn't pointed to by another orphan, it is a chain tip.
13241324
* - add ::ChainActive().Tip()
13251325
*/

src/test/util/setup_common.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
111111
GetMainSignals().RegisterBackgroundSignalScheduler(*g_rpc_node->scheduler);
112112

113113
pblocktree.reset(new CBlockTreeDB(1 << 20, true));
114-
g_chainstate = MakeUnique<CChainState>();
114+
g_chainman.InitializeChainstate();
115115
::ChainstateActive().InitCoinsDB(
116116
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
117117
assert(!::ChainstateActive().CanFlushToDisk());
@@ -153,7 +153,7 @@ TestingSetup::~TestingSetup()
153153
m_node.mempool = nullptr;
154154
m_node.scheduler.reset();
155155
UnloadBlockIndex();
156-
g_chainstate.reset();
156+
g_chainman.Reset();
157157
pblocktree.reset();
158158
}
159159

0 commit comments

Comments
 (0)