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.
This commit is contained in:
James O'Beirne 2019-12-12 10:48:28 -05:00 committed by James O'Beirne
parent 5b690f0aae
commit 4ae29f5f0c
5 changed files with 162 additions and 119 deletions

View File

@ -233,13 +233,12 @@ void Shutdown(NodeContext& node)
} }
// FlushStateToDisk generates a ChainStateFlushed callback, which we should avoid missing // FlushStateToDisk generates a ChainStateFlushed callback, which we should 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();
}
} }
} }
@ -263,9 +262,11 @@ void Shutdown(NodeContext& node)
{ {
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();
} }
@ -1502,17 +1503,18 @@ bool AppInitMain(NodeContext& node)
bool fLoaded = false; bool fLoaded = false;
while (!fLoaded && !ShutdownRequested()) { while (!fLoaded && !ShutdownRequested()) {
bool fReset = fReindex; bool fReset = fReindex;
auto is_coinsview_empty = [&](CChainState* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
return fReset || fReindexChainState || chainstate->CoinsTip().GetBestBlock().IsNull();
};
std::string strLoadError; std::string strLoadError;
uiInterface.InitMessage(_("Loading block index...").translated); uiInterface.InitMessage(_("Loading block index...").translated);
do { do {
const int64_t load_block_index_start_time = GetTimeMillis(); const int64_t load_block_index_start_time = GetTimeMillis();
bool is_coinsview_empty;
try { try {
LOCK(cs_main); LOCK(cs_main);
// This statement makes ::ChainstateActive() usable. g_chainman.InitializeChainstate();
g_chainstate = MakeUnique<CChainState>();
UnloadBlockIndex(); UnloadBlockIndex();
// new CBlockTreeDB tries to delete the existing file, which // new CBlockTreeDB tries to delete the existing file, which
@ -1565,43 +1567,53 @@ bool AppInitMain(NodeContext& node)
// At this point we're either in reindex or we've loaded a useful // At this point we're either in reindex or we've loaded a useful
// block tree into BlockIndex()! // block tree into BlockIndex()!
::ChainstateActive().InitCoinsDB( bool failed_chainstate_init = false;
/* cache_size_bytes */ nCoinDBCache,
/* in_memory */ false,
/* should_wipe */ fReset || fReindexChainState);
::ChainstateActive().CoinsErrorCatcher().AddReadErrCallback([]() { for (CChainState* chainstate : g_chainman.GetAll()) {
uiInterface.ThreadSafeMessageBox( LogPrintf("Initializing chainstate %s\n", chainstate->ToString());
_("Error reading from database, shutting down.").translated, chainstate->InitCoinsDB(
"", CClientUIInterface::MSG_ERROR); /* cache_size_bytes */ nCoinDBCache,
}); /* in_memory */ false,
/* should_wipe */ fReset || fReindexChainState);
// If necessary, upgrade from older database format. chainstate->CoinsErrorCatcher().AddReadErrCallback([]() {
// This is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate uiInterface.ThreadSafeMessageBox(
if (!::ChainstateActive().CoinsDB().Upgrade()) { _("Error reading from database, shutting down.").translated,
strLoadError = _("Error upgrading chainstate database").translated; "", CClientUIInterface::MSG_ERROR);
break; });
}
// ReplayBlocks is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate // If necessary, upgrade from older database format.
if (!::ChainstateActive().ReplayBlocks(chainparams)) { // This is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
strLoadError = _("Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.").translated; if (!chainstate->CoinsDB().Upgrade()) {
break; strLoadError = _("Error upgrading chainstate database").translated;
} failed_chainstate_init = true;
// The on-disk coinsdb is now in a good state, create the cache
::ChainstateActive().InitCoinsCache();
assert(::ChainstateActive().CanFlushToDisk());
is_coinsview_empty = fReset || fReindexChainState ||
::ChainstateActive().CoinsTip().GetBestBlock().IsNull();
if (!is_coinsview_empty) {
// LoadChainTip initializes the chain based on CoinsTip()'s best block
if (!::ChainstateActive().LoadChainTip(chainparams)) {
strLoadError = _("Error initializing block database").translated;
break; break;
} }
assert(::ChainActive().Tip() != nullptr);
// ReplayBlocks is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
if (!chainstate->ReplayBlocks(chainparams)) {
strLoadError = _("Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.").translated;
failed_chainstate_init = true;
break;
}
// The on-disk coinsdb is now in a good state, create the cache
chainstate->InitCoinsCache();
assert(chainstate->CanFlushToDisk());
if (!is_coinsview_empty(chainstate)) {
// LoadChainTip initializes the chain based on CoinsTip()'s best block
if (!chainstate->LoadChainTip(chainparams)) {
strLoadError = _("Error initializing block database").translated;
failed_chainstate_init = true;
break; // out of the per-chainstate loop
}
assert(chainstate->m_chain.Tip() != nullptr);
}
}
if (failed_chainstate_init) {
break; // out of the chainstate activation do-while
} }
} catch (const std::exception& e) { } catch (const std::exception& e) {
LogPrintf("%s\n", e.what()); LogPrintf("%s\n", e.what());
@ -1609,49 +1621,75 @@ bool AppInitMain(NodeContext& node)
break; break;
} }
if (!fReset) { bool failed_rewind{false};
// Note that RewindBlockIndex MUST run even if we're about to -reindex-chainstate.
// It both disconnects blocks based on ::ChainActive(), and drops block data in for (CChainState* chainstate : g_chainman.GetAll()) {
// BlockIndex() based on lack of available witness data. if (!fReset) {
uiInterface.InitMessage(_("Rewinding blocks...").translated); // Note that RewindBlockIndex MUST run even if we're about to -reindex-chainstate.
if (!::ChainstateActive().RewindBlockIndex(chainparams)) { // It both disconnects blocks based on the chainstate, and drops block data in
strLoadError = _("Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain").translated; // BlockIndex() based on lack of available witness data.
break; uiInterface.InitMessage(_("Rewinding blocks...").translated);
if (!chainstate->RewindBlockIndex(chainparams)) {
strLoadError = _(
"Unable to rewind the database to a pre-fork state. "
"You will need to redownload the blockchain").translated;
failed_rewind = true;
break; // out of the per-chainstate loop
}
} }
} }
if (failed_rewind) {
break; // out of the chainstate activation do-while
}
bool failed_verification = false;
try { try {
LOCK(cs_main); LOCK(cs_main);
if (!is_coinsview_empty) {
uiInterface.InitMessage(_("Verifying blocks...").translated);
if (fHavePruned && gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) {
LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks\n",
MIN_BLOCKS_TO_KEEP);
}
CBlockIndex* tip = ::ChainActive().Tip(); for (CChainState* chainstate : g_chainman.GetAll()) {
RPCNotifyBlockChange(true, tip); if (!is_coinsview_empty(chainstate)) {
if (tip && tip->nTime > GetAdjustedTime() + 2 * 60 * 60) { uiInterface.InitMessage(_("Verifying blocks...").translated);
strLoadError = _("The block database contains a block which appears to be from the future. " if (fHavePruned && gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) {
"This may be due to your computer's date and time being set incorrectly. " LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks\n",
"Only rebuild the block database if you are sure that your computer's date and time are correct").translated; MIN_BLOCKS_TO_KEEP);
break; }
}
if (!CVerifyDB().VerifyDB(chainparams, &::ChainstateActive().CoinsDB(), gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL), const CBlockIndex* tip = chainstate->m_chain.Tip();
gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) { RPCNotifyBlockChange(true, tip);
strLoadError = _("Corrupted block database detected").translated; if (tip && tip->nTime > GetAdjustedTime() + 2 * 60 * 60) {
break; strLoadError = _("The block database contains a block which appears to be from the future. "
"This may be due to your computer's date and time being set incorrectly. "
"Only rebuild the block database if you are sure that your computer's date and time are correct").translated;
failed_verification = true;
break;
}
// Only verify the DB of the active chainstate. This is fixed in later
// work when we allow VerifyDB to be parameterized by chainstate.
if (&::ChainstateActive() == chainstate &&
!CVerifyDB().VerifyDB(
chainparams, &chainstate->CoinsDB(),
gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL),
gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
strLoadError = _("Corrupted block database detected").translated;
failed_verification = true;
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").translated; strLoadError = _("Error opening block database").translated;
failed_verification = true;
break; break;
} }
fLoaded = true; if (!failed_verification) {
LogPrintf(" block index %15dms\n", GetTimeMillis() - load_block_index_start_time); fLoaded = true;
LogPrintf(" block index %15dms\n", GetTimeMillis() - load_block_index_start_time);
}
} while(false); } while(false);
if (!fLoaded && !ShutdownRequested()) { if (!fLoaded && !ShutdownRequested()) {
@ -1715,8 +1753,11 @@ bool AppInitMain(NodeContext& node)
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) {
uiInterface.InitMessage(_("Pruning blockstore...").translated); LOCK(cs_main);
::ChainstateActive().PruneAndFlush(); for (CChainState* chainstate : g_chainman.GetAll()) {
uiInterface.InitMessage(_("Pruning blockstore...").translated);
chainstate->PruneAndFlush();
}
} }
} }

View File

@ -1319,7 +1319,7 @@ static UniValue getchaintips(const JSONRPCRequest& request)
/* /*
* Idea: the set of chain tips is ::ChainActive().tip, plus orphan blocks which do not have another orphan building off of them. * Idea: the set of chain tips is ::ChainActive().tip, plus orphan blocks which do not have another orphan building off of them.
* Algorithm: * Algorithm:
* - 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. * - Make one pass through BlockIndex(), picking out the orphan blocks, and also storing a set of the orphan block's pprev pointers.
* - Iterate through the orphan blocks. If the block isn't pointed to by another orphan, it is a chain tip. * - Iterate through the orphan blocks. If the block isn't pointed to by another orphan, it is a chain tip.
* - add ::ChainActive().Tip() * - add ::ChainActive().Tip()
*/ */

View File

@ -111,7 +111,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
GetMainSignals().RegisterBackgroundSignalScheduler(*g_rpc_node->scheduler); GetMainSignals().RegisterBackgroundSignalScheduler(*g_rpc_node->scheduler);
pblocktree.reset(new CBlockTreeDB(1 << 20, true)); pblocktree.reset(new CBlockTreeDB(1 << 20, true));
g_chainstate = MakeUnique<CChainState>(); g_chainman.InitializeChainstate();
::ChainstateActive().InitCoinsDB( ::ChainstateActive().InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false); /* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
assert(!::ChainstateActive().CanFlushToDisk()); assert(!::ChainstateActive().CanFlushToDisk());
@ -153,7 +153,7 @@ TestingSetup::~TestingSetup()
m_node.mempool = nullptr; m_node.mempool = nullptr;
m_node.scheduler.reset(); m_node.scheduler.reset();
UnloadBlockIndex(); UnloadBlockIndex();
g_chainstate.reset(); g_chainman.Reset();
pblocktree.reset(); pblocktree.reset();
} }

View File

@ -77,20 +77,17 @@ bool CBlockIndexWorkComparator::operator()(const CBlockIndex *pa, const CBlockIn
return false; return false;
} }
namespace { ChainstateManager g_chainman;
BlockManager g_blockman;
} // anon namespace
std::unique_ptr<CChainState> g_chainstate; CChainState& ChainstateActive()
{
CChainState& ChainstateActive() { assert(g_chainman.m_active_chainstate);
assert(g_chainstate); return *g_chainman.m_active_chainstate;
return *g_chainstate;
} }
CChain& ChainActive() { CChain& ChainActive()
assert(g_chainstate); {
return g_chainstate->m_chain; return ::ChainstateActive().m_chain;
} }
/** /**
@ -152,8 +149,8 @@ namespace {
CBlockIndex* LookupBlockIndex(const uint256& hash) CBlockIndex* LookupBlockIndex(const uint256& hash)
{ {
AssertLockHeld(cs_main); AssertLockHeld(cs_main);
BlockMap::const_iterator it = g_blockman.m_block_index.find(hash); BlockMap::const_iterator it = g_chainman.BlockIndex().find(hash);
return it == g_blockman.m_block_index.end() ? nullptr : it->second; return it == g_chainman.BlockIndex().end() ? nullptr : it->second;
} }
CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator) CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator)
@ -1243,13 +1240,10 @@ void CoinsViews::InitCache()
m_cacheview = MakeUnique<CCoinsViewCache>(&m_catcherview); m_cacheview = MakeUnique<CCoinsViewCache>(&m_catcherview);
} }
// NOTE: for now m_blockman is set to a global, but this will be changed CChainState::CChainState(BlockManager& blockman, uint256 from_snapshot_blockhash)
// in a future commit. : m_blockman(blockman),
CChainState::CChainState(uint256 from_snapshot_blockhash)
: m_blockman(g_blockman),
m_from_snapshot_blockhash(from_snapshot_blockhash) {} m_from_snapshot_blockhash(from_snapshot_blockhash) {}
void CChainState::InitCoinsDB( void CChainState::InitCoinsDB(
size_t cache_size_bytes, size_t cache_size_bytes,
bool in_memory, bool in_memory,
@ -1301,7 +1295,7 @@ static CBlockIndex *pindexBestForkTip = nullptr, *pindexBestForkBase = nullptr;
BlockMap& BlockIndex() BlockMap& BlockIndex()
{ {
return g_blockman.m_block_index; return g_chainman.m_blockman.m_block_index;
} }
static void AlertNotify(const std::string& strMessage) static void AlertNotify(const std::string& strMessage)
@ -3471,7 +3465,7 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio
if (fCheckpointsEnabled) { if (fCheckpointsEnabled) {
// Don't accept any forks from the main chain prior to last checkpoint. // Don't accept any forks from the main chain prior to last checkpoint.
// GetLastCheckpoint finds the last checkpoint in MapCheckpoints that's in our // GetLastCheckpoint finds the last checkpoint in MapCheckpoints that's in our
// g_blockman.m_block_index. // BlockIndex().
CBlockIndex* pcheckpoint = GetLastCheckpoint(params.Checkpoints()); CBlockIndex* pcheckpoint = GetLastCheckpoint(params.Checkpoints());
if (pcheckpoint && nHeight < pcheckpoint->nHeight) { if (pcheckpoint && nHeight < pcheckpoint->nHeight) {
LogPrintf("ERROR: %s: forked chain older than last checkpoint (height %d)\n", __func__, nHeight); LogPrintf("ERROR: %s: forked chain older than last checkpoint (height %d)\n", __func__, nHeight);
@ -3679,7 +3673,8 @@ bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, BlockValid
LOCK(cs_main); LOCK(cs_main);
for (const CBlockHeader& header : headers) { for (const CBlockHeader& header : headers) {
CBlockIndex *pindex = nullptr; // Use a temp pindex instead of ppindex to avoid a const_cast CBlockIndex *pindex = nullptr; // Use a temp pindex instead of ppindex to avoid a const_cast
bool accepted = g_blockman.AcceptBlockHeader(header, state, chainparams, &pindex); bool accepted = g_chainman.m_blockman.AcceptBlockHeader(
header, state, chainparams, &pindex);
::ChainstateActive().CheckBlockIndex(chainparams.GetConsensus()); ::ChainstateActive().CheckBlockIndex(chainparams.GetConsensus());
if (!accepted) { if (!accepted) {
@ -3881,7 +3876,7 @@ void PruneOneBlockFile(const int fileNumber)
{ {
LOCK(cs_LastBlockFile); LOCK(cs_LastBlockFile);
for (const auto& entry : g_blockman.m_block_index) { for (const auto& entry : g_chainman.BlockIndex()) {
CBlockIndex* pindex = entry.second; CBlockIndex* pindex = entry.second;
if (pindex->nFile == fileNumber) { if (pindex->nFile == fileNumber) {
pindex->nStatus &= ~BLOCK_HAVE_DATA; pindex->nStatus &= ~BLOCK_HAVE_DATA;
@ -3895,12 +3890,12 @@ void PruneOneBlockFile(const int fileNumber)
// to be downloaded again in order to consider its chain, at which // to be downloaded again in order to consider its chain, at which
// point it would be considered as a candidate for // point it would be considered as a candidate for
// m_blocks_unlinked or setBlockIndexCandidates. // m_blocks_unlinked or setBlockIndexCandidates.
auto range = g_blockman.m_blocks_unlinked.equal_range(pindex->pprev); auto range = g_chainman.m_blockman.m_blocks_unlinked.equal_range(pindex->pprev);
while (range.first != range.second) { while (range.first != range.second) {
std::multimap<CBlockIndex *, CBlockIndex *>::iterator _it = range.first; std::multimap<CBlockIndex *, CBlockIndex *>::iterator _it = range.first;
range.first++; range.first++;
if (_it->second == pindex) { if (_it->second == pindex) {
g_blockman.m_blocks_unlinked.erase(_it); g_chainman.m_blockman.m_blocks_unlinked.erase(_it);
} }
} }
} }
@ -4137,9 +4132,11 @@ void BlockManager::Unload() {
bool static LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main) bool static LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{ {
if (!g_blockman.LoadBlockIndex( if (!g_chainman.m_blockman.LoadBlockIndex(
chainparams.GetConsensus(), *pblocktree, ::ChainstateActive().setBlockIndexCandidates)) chainparams.GetConsensus(), *pblocktree,
::ChainstateActive().setBlockIndexCandidates)) {
return false; return false;
}
// Load block file info // Load block file info
pblocktree->ReadLastBlockFile(nLastBlockFile); pblocktree->ReadLastBlockFile(nLastBlockFile);
@ -4161,7 +4158,7 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_RE
// Check presence of blk files // Check presence of blk files
LogPrintf("Checking all blk files are present...\n"); LogPrintf("Checking all blk files are present...\n");
std::set<int> setBlkDataFiles; std::set<int> setBlkDataFiles;
for (const std::pair<const uint256, CBlockIndex*>& item : g_blockman.m_block_index) for (const std::pair<const uint256, CBlockIndex*>& item : g_chainman.BlockIndex())
{ {
CBlockIndex* pindex = item.second; CBlockIndex* pindex = item.second;
if (pindex->nStatus & BLOCK_HAVE_DATA) { if (pindex->nStatus & BLOCK_HAVE_DATA) {
@ -4564,8 +4561,7 @@ void CChainState::UnloadBlockIndex() {
void UnloadBlockIndex() void UnloadBlockIndex()
{ {
LOCK(cs_main); LOCK(cs_main);
::ChainActive().SetTip(nullptr); g_chainman.Unload();
g_blockman.Unload();
pindexBestInvalid = nullptr; pindexBestInvalid = nullptr;
pindexBestHeader = nullptr; pindexBestHeader = nullptr;
mempool.clear(); mempool.clear();
@ -4578,8 +4574,6 @@ void UnloadBlockIndex()
warningcache[b].clear(); warningcache[b].clear();
} }
fHavePruned = false; fHavePruned = false;
::ChainstateActive().UnloadBlockIndex();
} }
bool LoadBlockIndex(const CChainParams& chainparams) bool LoadBlockIndex(const CChainParams& chainparams)
@ -4589,7 +4583,7 @@ bool LoadBlockIndex(const CChainParams& chainparams)
if (!fReindex) { if (!fReindex) {
bool ret = LoadBlockIndexDB(chainparams); bool ret = LoadBlockIndexDB(chainparams);
if (!ret) return false; if (!ret) return false;
needs_init = g_blockman.m_block_index.empty(); needs_init = g_chainman.m_blockman.m_block_index.empty();
} }
if (needs_init) { if (needs_init) {
@ -5135,10 +5129,10 @@ public:
CMainCleanup() {} CMainCleanup() {}
~CMainCleanup() { ~CMainCleanup() {
// block headers // block headers
BlockMap::iterator it1 = g_blockman.m_block_index.begin(); BlockMap::iterator it1 = g_chainman.BlockIndex().begin();
for (; it1 != g_blockman.m_block_index.end(); it1++) for (; it1 != g_chainman.BlockIndex().end(); it1++)
delete (*it1).second; delete (*it1).second;
g_blockman.m_block_index.clear(); g_chainman.BlockIndex().clear();
} }
}; };
static CMainCleanup instance_of_cmaincleanup; static CMainCleanup instance_of_cmaincleanup;
@ -5176,7 +5170,7 @@ CChainState& ChainstateManager::InitializeChainstate(const uint256& snapshot_blo
throw std::logic_error("should not be overwriting a chainstate"); throw std::logic_error("should not be overwriting a chainstate");
} }
to_modify.reset(new CChainState(snapshot_blockhash)); to_modify.reset(new CChainState(m_blockman, snapshot_blockhash));
// Snapshot chainstates and initial IBD chaintates always become active. // Snapshot chainstates and initial IBD chaintates always become active.
if (is_snapshot || (!is_snapshot && !m_active_chainstate)) { if (is_snapshot || (!is_snapshot && !m_active_chainstate)) {
@ -5220,6 +5214,8 @@ void ChainstateManager::Unload()
chainstate->m_chain.SetTip(nullptr); chainstate->m_chain.SetTip(nullptr);
chainstate->UnloadBlockIndex(); chainstate->UnloadBlockIndex();
} }
m_blockman.Unload();
} }
void ChainstateManager::Reset() void ChainstateManager::Reset()

View File

@ -592,8 +592,7 @@ private:
std::unique_ptr<CoinsViews> m_coins_views; std::unique_ptr<CoinsViews> m_coins_views;
public: public:
explicit CChainState(BlockManager& blockman) : m_blockman(blockman) {} explicit CChainState(BlockManager& blockman, uint256 from_snapshot_blockhash = uint256());
explicit CChainState(uint256 from_snapshot_blockhash = uint256());
/** /**
* Initialize the CoinsViews UTXO set database management data structures. The in-memory * Initialize the CoinsViews UTXO set database management data structures. The in-memory
@ -839,9 +838,14 @@ private:
bool m_snapshot_validated{false}; bool m_snapshot_validated{false};
// For access to m_active_chainstate. // For access to m_active_chainstate.
friend CChainState& ChainstateActive();
friend CChain& ChainActive(); friend CChain& ChainActive();
public: public:
//! A single BlockManager instance is shared across each constructed
//! chainstate to avoid duplicating block metadata.
BlockManager m_blockman GUARDED_BY(::cs_main);
//! Instantiate a new chainstate and assign it based upon whether it is //! Instantiate a new chainstate and assign it based upon whether it is
//! from a snapshot. //! from a snapshot.
//! //!
@ -858,6 +862,11 @@ public:
int ActiveHeight() const { return ActiveChain().Height(); } int ActiveHeight() const { return ActiveChain().Height(); }
CBlockIndex* ActiveTip() const { return ActiveChain().Tip(); } CBlockIndex* ActiveTip() const { return ActiveChain().Tip(); }
BlockMap& BlockIndex() EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
return m_blockman.m_block_index;
}
bool IsSnapshotActive() const; bool IsSnapshotActive() const;
Optional<uint256> SnapshotBlockhash() const; Optional<uint256> SnapshotBlockhash() const;
@ -885,6 +894,8 @@ public:
void Reset(); void Reset();
}; };
extern ChainstateManager g_chainman;
/** @returns the most-work valid chainstate. */ /** @returns the most-work valid chainstate. */
CChainState& ChainstateActive(); CChainState& ChainstateActive();
@ -894,11 +905,6 @@ CChain& ChainActive();
/** @returns the global block index map. */ /** @returns the global block index map. */
BlockMap& BlockIndex(); BlockMap& BlockIndex();
// Most often ::ChainstateActive() should be used instead of this, but some code
// may not be able to assume that this has been initialized yet and so must use it
// directly, e.g. init.cpp.
extern std::unique_ptr<CChainState> g_chainstate;
/** Global variable that points to the active block tree (protected by cs_main) */ /** Global variable that points to the active block tree (protected by cs_main) */
extern std::unique_ptr<CBlockTreeDB> pblocktree; extern std::unique_ptr<CBlockTreeDB> pblocktree;