mirror of
https://github.com/dashpay/dash.git
synced 2024-12-26 12:32:48 +01:00
merge bitcoin#17737: Add ChainstateManager, remove BlockManager global
Co-authored-by: "UdjinM6 <UdjinM6@users.noreply.github.com>"
This commit is contained in:
parent
f2300bc9cf
commit
8b9e784a64
@ -229,6 +229,7 @@ BITCOIN_TESTS =\
|
||||
test/uint256_tests.cpp \
|
||||
test/util_tests.cpp \
|
||||
test/validation_block_tests.cpp \
|
||||
test/validation_chainstatemanager_tests.cpp \
|
||||
test/validation_flush_tests.cpp \
|
||||
test/versionbits_tests.cpp
|
||||
|
||||
|
204
src/init.cpp
204
src/init.cpp
@ -293,13 +293,12 @@ void PrepareShutdown(NodeContext& node)
|
||||
}
|
||||
|
||||
// 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);
|
||||
if (g_chainstate && g_chainstate->CanFlushToDisk()) {
|
||||
g_chainstate->ForceFlushStateToDisk();
|
||||
for (CChainState* chainstate : g_chainman.GetAll()) {
|
||||
if (chainstate->CanFlushToDisk()) {
|
||||
chainstate->ForceFlushStateToDisk();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -323,9 +322,11 @@ void PrepareShutdown(NodeContext& node)
|
||||
|
||||
{
|
||||
LOCK(cs_main);
|
||||
if (g_chainstate && g_chainstate->CanFlushToDisk()) {
|
||||
g_chainstate->ForceFlushStateToDisk();
|
||||
g_chainstate->ResetCoinsViews();
|
||||
for (CChainState* chainstate : g_chainman.GetAll()) {
|
||||
if (chainstate->CanFlushToDisk()) {
|
||||
chainstate->ForceFlushStateToDisk();
|
||||
chainstate->ResetCoinsViews();
|
||||
}
|
||||
}
|
||||
pblocktree.reset();
|
||||
llmq::DestroyLLMQSystem();
|
||||
@ -931,11 +932,17 @@ static void ThreadImport(std::vector<fs::path> vImportFiles)
|
||||
}
|
||||
|
||||
// scan for better chains in the block chain database, that are not yet connected in the active best chain
|
||||
CValidationState state;
|
||||
if (!ActivateBestChain(state, chainparams)) {
|
||||
LogPrintf("Failed to connect best block (%s)\n", FormatStateMessage(state));
|
||||
StartShutdown();
|
||||
return;
|
||||
|
||||
// We can't hold cs_main during ActivateBestChain even though we're accessing
|
||||
// the g_chainman unique_ptrs since ABC requires us not to be holding cs_main, so retrieve
|
||||
// the relevant pointers before the ABC call.
|
||||
for (CChainState* chainstate : WITH_LOCK(::cs_main, return g_chainman.GetAll())) {
|
||||
CValidationState state;
|
||||
if (!chainstate->ActivateBestChain(state, chainparams, nullptr)) {
|
||||
LogPrintf("Failed to connect best block (%s)\n", FormatStateMessage(state));
|
||||
StartShutdown();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (gArgs.GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) {
|
||||
@ -1957,17 +1964,20 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
|
||||
|
||||
while (!fLoaded && !ShutdownRequested()) {
|
||||
bool fReset = fReindex;
|
||||
auto is_coinsview_empty = [&](CChainState* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
|
||||
return fReset || fReindexChainState || chainstate->CoinsTip().GetBestBlock().IsNull();
|
||||
};
|
||||
bilingual_str strLoadError;
|
||||
|
||||
uiInterface.InitMessage(_("Loading block index...").translated);
|
||||
|
||||
do {
|
||||
bool failed_verification = false;
|
||||
const int64_t load_block_index_start_time = GetTimeMillis();
|
||||
bool is_coinsview_empty;
|
||||
|
||||
try {
|
||||
LOCK(cs_main);
|
||||
// This statement makes ::ChainstateActive() usable.
|
||||
g_chainstate = MakeUnique<CChainState>();
|
||||
g_chainman.InitializeChainstate();
|
||||
UnloadBlockIndex();
|
||||
|
||||
// new CBlockTreeDB tries to delete the existing file, which
|
||||
@ -2056,55 +2066,62 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
|
||||
// At this point we're either in reindex or we've loaded a useful
|
||||
// block tree into BlockIndex()!
|
||||
|
||||
::ChainstateActive().InitCoinsDB(
|
||||
/* cache_size_bytes */ nCoinDBCache,
|
||||
/* in_memory */ false,
|
||||
/* should_wipe */ fReset || fReindexChainState);
|
||||
bool failed_chainstate_init = false;
|
||||
for (CChainState* chainstate : g_chainman.GetAll()) {
|
||||
LogPrintf("Initializing chainstate %s\n", chainstate->ToString());
|
||||
chainstate->InitCoinsDB(
|
||||
/* cache_size_bytes */ nCoinDBCache,
|
||||
/* in_memory */ false,
|
||||
/* should_wipe */ fReset || fReindexChainState);
|
||||
|
||||
::ChainstateActive().CoinsErrorCatcher().AddReadErrCallback([]() {
|
||||
uiInterface.ThreadSafeMessageBox(
|
||||
_("Error reading from database, shutting down."),
|
||||
"", CClientUIInterface::MSG_ERROR);
|
||||
});
|
||||
chainstate->CoinsErrorCatcher().AddReadErrCallback([]() {
|
||||
uiInterface.ThreadSafeMessageBox(
|
||||
_("Error reading from database, shutting down."),
|
||||
"", CClientUIInterface::MSG_ERROR);
|
||||
});
|
||||
|
||||
// If necessary, upgrade from older database format.
|
||||
// This is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
|
||||
if (!::ChainstateActive().CoinsDB().Upgrade()) {
|
||||
strLoadError = _("Error upgrading chainstate database");
|
||||
break;
|
||||
}
|
||||
|
||||
// ReplayBlocks is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
|
||||
if (!::ChainstateActive().ReplayBlocks(chainparams)) {
|
||||
strLoadError = _("Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.");
|
||||
break;
|
||||
}
|
||||
|
||||
// The on-disk coinsdb is now in a good state, create the cache
|
||||
::ChainstateActive().InitCoinsCache();
|
||||
assert(::ChainstateActive().CanFlushToDisk());
|
||||
|
||||
// flush evodb
|
||||
if (!evoDb->CommitRootTransaction()) {
|
||||
strLoadError = _("Failed to commit EvoDB");
|
||||
break;
|
||||
}
|
||||
|
||||
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");
|
||||
// If necessary, upgrade from older database format.
|
||||
// This is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
|
||||
if (!chainstate->CoinsDB().Upgrade()) {
|
||||
strLoadError = _("Error upgrading chainstate database");
|
||||
failed_chainstate_init = true;
|
||||
break;
|
||||
}
|
||||
assert(::ChainActive().Tip() != NULL);
|
||||
|
||||
// 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.");
|
||||
failed_chainstate_init = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// The on-disk coinsdb is now in a good state, create the cache
|
||||
chainstate->InitCoinsCache();
|
||||
assert(chainstate->CanFlushToDisk());
|
||||
|
||||
// flush evodb
|
||||
// TODO: CEvoDB instance should probably be a part of CChainState
|
||||
// (for multiple chainstates to actually work in parallel)
|
||||
// and not a global
|
||||
if (&::ChainstateActive() == chainstate && !evoDb->CommitRootTransaction()) {
|
||||
strLoadError = _("Failed to commit EvoDB");
|
||||
failed_chainstate_init = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!is_coinsview_empty(chainstate)) {
|
||||
// LoadChainTip initializes the chain based on CoinsTip()'s best block
|
||||
if (!chainstate->LoadChainTip(chainparams)) {
|
||||
strLoadError = _("Error initializing block database");
|
||||
failed_chainstate_init = true;
|
||||
break; // out of the per-chainstate loop
|
||||
}
|
||||
assert(chainstate->m_chain.Tip() != nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_coinsview_empty && !evoDb->IsEmpty()) {
|
||||
// EvoDB processed some blocks earlier but we have no blocks anymore, something is wrong
|
||||
strLoadError = _("Error initializing block database");
|
||||
break;
|
||||
if (failed_chainstate_init) {
|
||||
break; // out of the chainstate activation do-while
|
||||
}
|
||||
|
||||
if (!deterministicMNManager->UpgradeDBIfNeeded() || !llmq::quorumBlockProcessor->UpgradeDB()) {
|
||||
@ -2112,26 +2129,45 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
|
||||
break;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
for (CChainState* chainstate : g_chainman.GetAll()) {
|
||||
if (!is_coinsview_empty(chainstate)) {
|
||||
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();
|
||||
RPCNotifyBlockChange(true, tip);
|
||||
if (tip && tip->nTime > GetAdjustedTime() + 2 * 60 * 60) {
|
||||
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");
|
||||
break;
|
||||
}
|
||||
CBlockIndex* tip = chainstate->m_chain.Tip();
|
||||
RPCNotifyBlockChange(true, tip);
|
||||
if (tip && tip->nTime > GetAdjustedTime() + 2 * 60 * 60) {
|
||||
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");
|
||||
failed_verification = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!CVerifyDB().VerifyDB(chainparams, &::ChainstateActive().CoinsDB(), gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL),
|
||||
gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
|
||||
strLoadError = _("Corrupted block database detected");
|
||||
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");
|
||||
failed_verification = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// TODO: CEvoDB instance should probably be a part of CChainState
|
||||
// (for multiple chainstates to actually work in parallel)
|
||||
// and not a global
|
||||
if (&::ChainstateActive() == chainstate && !evoDb->IsEmpty()) {
|
||||
// EvoDB processed some blocks earlier but we have no blocks anymore, something is wrong
|
||||
strLoadError = _("Error initializing block database");
|
||||
failed_verification = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL) >= 3) {
|
||||
@ -2141,11 +2177,14 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
|
||||
} catch (const std::exception& e) {
|
||||
LogPrintf("%s\n", e.what());
|
||||
strLoadError = _("Error opening block database");
|
||||
failed_verification = true;
|
||||
break;
|
||||
}
|
||||
|
||||
fLoaded = true;
|
||||
LogPrintf(" block index %15dms\n", GetTimeMillis() - load_block_index_start_time);
|
||||
if (!failed_verification) {
|
||||
fLoaded = true;
|
||||
LogPrintf(" block index %15dms\n", GetTimeMillis() - load_block_index_start_time);
|
||||
}
|
||||
} while(false);
|
||||
|
||||
if (!fLoaded && !ShutdownRequested()) {
|
||||
@ -2217,8 +2256,11 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
|
||||
LogPrintf("Unsetting NODE_NETWORK on prune mode\n");
|
||||
nLocalServices = ServiceFlags(nLocalServices & ~NODE_NETWORK);
|
||||
if (!fReindex) {
|
||||
uiInterface.InitMessage(_("Pruning blockstore...").translated);
|
||||
::ChainstateActive().PruneAndFlush();
|
||||
LOCK(cs_main);
|
||||
for (CChainState* chainstate : g_chainman.GetAll()) {
|
||||
uiInterface.InitMessage(_("Pruning blockstore...").translated);
|
||||
chainstate->PruneAndFlush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,6 +88,7 @@ void AppTests::appTests()
|
||||
LogInstance().DisconnectTestLogger();
|
||||
AbortShutdown();
|
||||
UnloadBlockIndex();
|
||||
WITH_LOCK(::cs_main, g_chainman.Reset());
|
||||
}
|
||||
|
||||
//! Entry point for BitcoinGUI tests.
|
||||
|
@ -1613,7 +1613,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.
|
||||
* 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.
|
||||
* - add ::ChainActive().Tip()
|
||||
*/
|
||||
|
@ -134,7 +134,8 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
|
||||
m_node.connman = MakeUnique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests.
|
||||
m_node.peer_logic = MakeUnique<PeerLogicValidation>(m_node.connman.get(), m_node.banman.get(), *m_node.scheduler, *m_node.mempool, false);
|
||||
pblocktree.reset(new CBlockTreeDB(1 << 20, true));
|
||||
g_chainstate = MakeUnique<CChainState>();
|
||||
|
||||
g_chainman.InitializeChainstate();
|
||||
::ChainstateActive().InitCoinsDB(
|
||||
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
|
||||
assert(!::ChainstateActive().CanFlushToDisk());
|
||||
@ -178,8 +179,8 @@ TestingSetup::~TestingSetup()
|
||||
m_node.mempool = nullptr;
|
||||
m_node.scheduler.reset();
|
||||
UnloadBlockIndex();
|
||||
g_chainstate.reset();
|
||||
llmq::DestroyLLMQSystem();
|
||||
m_node.chainman->Reset();
|
||||
pblocktree.reset();
|
||||
}
|
||||
|
||||
|
109
src/test/validation_chainstatemanager_tests.cpp
Normal file
109
src/test/validation_chainstatemanager_tests.cpp
Normal file
@ -0,0 +1,109 @@
|
||||
// Copyright (c) 2019 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
//
|
||||
#include <chainparams.h>
|
||||
#include <random.h>
|
||||
#include <index/txindex.h>
|
||||
#include <uint256.h>
|
||||
#include <consensus/validation.h>
|
||||
#include <sync.h>
|
||||
#include <test/util/setup_common.h>
|
||||
#include <validation.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(validation_chainstatemanager_tests, TestingSetup)
|
||||
|
||||
//! Basic tests for ChainstateManager.
|
||||
//!
|
||||
//! First create a legacy (IBD) chainstate, then create a snapshot chainstate.
|
||||
BOOST_AUTO_TEST_CASE(chainstatemanager)
|
||||
{
|
||||
// TODO: this is a hack to avoid txindex crash on BlockConnected trigered by ActivateBestChain
|
||||
g_txindex->Interrupt();
|
||||
g_txindex->Stop();
|
||||
|
||||
ChainstateManager manager;
|
||||
std::vector<CChainState*> chainstates;
|
||||
const CChainParams& chainparams = Params();
|
||||
|
||||
// Create a legacy (IBD) chainstate.
|
||||
//
|
||||
ENTER_CRITICAL_SECTION(cs_main);
|
||||
CChainState& c1 = manager.InitializeChainstate();
|
||||
LEAVE_CRITICAL_SECTION(cs_main);
|
||||
chainstates.push_back(&c1);
|
||||
c1.InitCoinsDB(
|
||||
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
|
||||
WITH_LOCK(::cs_main, c1.InitCoinsCache());
|
||||
|
||||
BOOST_CHECK(!manager.IsSnapshotActive());
|
||||
BOOST_CHECK(!manager.IsSnapshotValidated());
|
||||
BOOST_CHECK(!manager.IsBackgroundIBD(&c1));
|
||||
auto all = manager.GetAll();
|
||||
BOOST_CHECK_EQUAL_COLLECTIONS(all.begin(), all.end(), chainstates.begin(), chainstates.end());
|
||||
|
||||
auto& active_chain = manager.ActiveChain();
|
||||
BOOST_CHECK_EQUAL(&active_chain, &c1.m_chain);
|
||||
|
||||
BOOST_CHECK_EQUAL(manager.ActiveHeight(), -1);
|
||||
|
||||
auto active_tip = manager.ActiveTip();
|
||||
auto exp_tip = c1.m_chain.Tip();
|
||||
BOOST_CHECK_EQUAL(active_tip, exp_tip);
|
||||
|
||||
auto& validated_cs = manager.ValidatedChainstate();
|
||||
BOOST_CHECK_EQUAL(&validated_cs, &c1);
|
||||
|
||||
// Create a snapshot-based chainstate.
|
||||
//
|
||||
ENTER_CRITICAL_SECTION(cs_main);
|
||||
CChainState& c2 = manager.InitializeChainstate(GetRandHash());
|
||||
LEAVE_CRITICAL_SECTION(cs_main);
|
||||
chainstates.push_back(&c2);
|
||||
c2.InitCoinsDB(
|
||||
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
|
||||
WITH_LOCK(::cs_main, c2.InitCoinsCache());
|
||||
// Unlike c1, which doesn't have any blocks. Gets us different tip, height.
|
||||
c2.LoadGenesisBlock(chainparams);
|
||||
CValidationState _;
|
||||
BOOST_CHECK(c2.ActivateBestChain(_, chainparams, nullptr));
|
||||
|
||||
BOOST_CHECK(manager.IsSnapshotActive());
|
||||
BOOST_CHECK(!manager.IsSnapshotValidated());
|
||||
BOOST_CHECK(manager.IsBackgroundIBD(&c1));
|
||||
BOOST_CHECK(!manager.IsBackgroundIBD(&c2));
|
||||
auto all2 = manager.GetAll();
|
||||
BOOST_CHECK_EQUAL_COLLECTIONS(all2.begin(), all2.end(), chainstates.begin(), chainstates.end());
|
||||
|
||||
auto& active_chain2 = manager.ActiveChain();
|
||||
BOOST_CHECK_EQUAL(&active_chain2, &c2.m_chain);
|
||||
|
||||
BOOST_CHECK_EQUAL(manager.ActiveHeight(), 0);
|
||||
|
||||
auto active_tip2 = manager.ActiveTip();
|
||||
auto exp_tip2 = c2.m_chain.Tip();
|
||||
BOOST_CHECK_EQUAL(active_tip2, exp_tip2);
|
||||
|
||||
// Ensure that these pointers actually correspond to different
|
||||
// CCoinsViewCache instances.
|
||||
BOOST_CHECK(exp_tip != exp_tip2);
|
||||
|
||||
auto& validated_cs2 = manager.ValidatedChainstate();
|
||||
BOOST_CHECK_EQUAL(&validated_cs2, &c1);
|
||||
|
||||
auto& validated_chain = manager.ValidatedChain();
|
||||
BOOST_CHECK_EQUAL(&validated_chain, &c1.m_chain);
|
||||
|
||||
auto validated_tip = manager.ValidatedTip();
|
||||
exp_tip = c1.m_chain.Tip();
|
||||
BOOST_CHECK_EQUAL(validated_tip, exp_tip);
|
||||
|
||||
// Avoid triggering the address sanitizer.
|
||||
WITH_LOCK(::cs_main, manager.Unload());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
@ -21,6 +21,7 @@
|
||||
#include <index/txindex.h>
|
||||
#include <logging.h>
|
||||
#include <logging/timer.h>
|
||||
#include <optional.h>
|
||||
#include <policy/fees.h>
|
||||
#include <policy/policy.h>
|
||||
#include <policy/settings.h>
|
||||
@ -98,20 +99,19 @@ bool CBlockIndexWorkComparator::operator()(const CBlockIndex* pa, const CBlockIn
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace {
|
||||
BlockManager g_blockman;
|
||||
} // anon namespace
|
||||
ChainstateManager g_chainman;
|
||||
|
||||
std::unique_ptr<CChainState> g_chainstate;
|
||||
|
||||
CChainState& ChainstateActive() {
|
||||
assert(g_chainstate);
|
||||
return *g_chainstate;
|
||||
CChainState& ChainstateActive()
|
||||
{
|
||||
LOCK(::cs_main);
|
||||
assert(g_chainman.m_active_chainstate);
|
||||
return *g_chainman.m_active_chainstate;
|
||||
}
|
||||
|
||||
CChain& ChainActive() {
|
||||
assert(g_chainstate);
|
||||
return g_chainstate->m_chain;
|
||||
CChain& ChainActive()
|
||||
{
|
||||
LOCK(::cs_main);
|
||||
return ::ChainstateActive().m_chain;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -178,8 +178,8 @@ namespace {
|
||||
CBlockIndex* LookupBlockIndex(const uint256& hash)
|
||||
{
|
||||
AssertLockHeld(cs_main);
|
||||
BlockMap::const_iterator it = g_blockman.m_block_index.find(hash);
|
||||
return it == g_blockman.m_block_index.end() ? nullptr : it->second;
|
||||
BlockMap::const_iterator it = g_chainman.BlockIndex().find(hash);
|
||||
return it == g_chainman.BlockIndex().end() ? nullptr : it->second;
|
||||
}
|
||||
|
||||
CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator)
|
||||
@ -1145,10 +1145,9 @@ void CoinsViews::InitCache()
|
||||
m_cacheview = MakeUnique<CCoinsViewCache>(&m_catcherview);
|
||||
}
|
||||
|
||||
// NOTE: for now m_blockman is set to a global, but this will be changed
|
||||
// in a future commit.
|
||||
CChainState::CChainState() : m_blockman(g_blockman) {}
|
||||
|
||||
CChainState::CChainState(BlockManager& blockman, uint256 from_snapshot_blockhash)
|
||||
: m_blockman(blockman),
|
||||
m_from_snapshot_blockhash(from_snapshot_blockhash) {}
|
||||
|
||||
void CChainState::InitCoinsDB(
|
||||
size_t cache_size_bytes,
|
||||
@ -1156,6 +1155,10 @@ void CChainState::InitCoinsDB(
|
||||
bool should_wipe,
|
||||
std::string leveldb_name)
|
||||
{
|
||||
if (!m_from_snapshot_blockhash.IsNull()) {
|
||||
leveldb_name += "_" + m_from_snapshot_blockhash.ToString();
|
||||
}
|
||||
|
||||
m_coins_views = MakeUnique<CoinsViews>(
|
||||
leveldb_name, cache_size_bytes, in_memory, should_wipe);
|
||||
}
|
||||
@ -1197,12 +1200,14 @@ static CBlockIndex *pindexBestForkTip = nullptr, *pindexBestForkBase = nullptr;
|
||||
|
||||
BlockMap& BlockIndex()
|
||||
{
|
||||
return g_blockman.m_block_index;
|
||||
LOCK(::cs_main);
|
||||
return g_chainman.m_blockman.m_block_index;
|
||||
}
|
||||
|
||||
PrevBlockMap& PrevBlockIndex()
|
||||
{
|
||||
return g_blockman.m_prev_block_index;
|
||||
LOCK(::cs_main);
|
||||
return g_chainman.m_blockman.m_prev_block_index;
|
||||
}
|
||||
|
||||
static void AlertNotify(const std::string& strMessage)
|
||||
@ -3337,8 +3342,8 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c
|
||||
// it up here, this should be an essentially unobservable error.
|
||||
// Loop back over all block index entries and add any missing entries
|
||||
// to setBlockIndexCandidates.
|
||||
BlockMap::iterator it = g_blockman.m_block_index.begin();
|
||||
while (it != g_blockman.m_block_index.end()) {
|
||||
BlockMap::iterator it = g_chainman.BlockIndex().begin();
|
||||
while (it != g_chainman.BlockIndex().end()) {
|
||||
if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && !(it->second->nStatus & BLOCK_CONFLICT_CHAINLOCK) && it->second->HaveTxsDownloaded() && !setBlockIndexCandidates.value_comp()(it->second, m_chain.Tip())) {
|
||||
setBlockIndexCandidates.insert(it->second);
|
||||
}
|
||||
@ -3447,8 +3452,8 @@ bool CChainState::MarkConflictingBlock(CValidationState& state, const CChainPara
|
||||
|
||||
// The resulting new best tip may not be in setBlockIndexCandidates anymore, so
|
||||
// add it again.
|
||||
BlockMap::iterator it = g_blockman.m_block_index.begin();
|
||||
while (it != g_blockman.m_block_index.end()) {
|
||||
BlockMap::iterator it = g_chainman.BlockIndex().begin();
|
||||
while (it != g_chainman.BlockIndex().end()) {
|
||||
if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && !(it->second->nStatus & BLOCK_CONFLICT_CHAINLOCK) && it->second->HaveTxsDownloaded() && !setBlockIndexCandidates.value_comp()(it->second, m_chain.Tip())) {
|
||||
setBlockIndexCandidates.insert(it->second);
|
||||
}
|
||||
@ -3836,7 +3841,7 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationSta
|
||||
if (fCheckpointsEnabled) {
|
||||
// Don't accept any forks from the main chain prior to last checkpoint.
|
||||
// GetLastCheckpoint finds the last checkpoint in MapCheckpoints that's in our
|
||||
// g_blockman.m_block_index.
|
||||
// BlockIndex().
|
||||
CBlockIndex* pcheckpoint = GetLastCheckpoint(params.Checkpoints());
|
||||
if (pcheckpoint && nHeight < pcheckpoint->nHeight)
|
||||
return state.DoS(100, error("%s: forked chain older than last checkpoint (height %d)", __func__, nHeight), REJECT_CHECKPOINT, "bad-fork-prior-to-checkpoint");
|
||||
@ -4036,7 +4041,8 @@ bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidatio
|
||||
LOCK(cs_main);
|
||||
for (const CBlockHeader& header : headers) {
|
||||
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());
|
||||
|
||||
if (!accepted) {
|
||||
@ -4153,8 +4159,8 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CVali
|
||||
return AbortNode(state, std::string("System error: ") + e.what());
|
||||
}
|
||||
|
||||
if (g_chainstate && g_chainstate->CanFlushToDisk()) {
|
||||
g_chainstate->FlushStateToDisk(chainparams, state, FlushStateMode::NONE);
|
||||
if (::ChainstateActive().CanFlushToDisk()) {
|
||||
::ChainstateActive().FlushStateToDisk(chainparams, state, FlushStateMode::NONE);
|
||||
}
|
||||
|
||||
CheckBlockIndex(chainparams.GetConsensus());
|
||||
@ -4257,7 +4263,7 @@ void PruneOneBlockFile(const int fileNumber)
|
||||
{
|
||||
LOCK(cs_LastBlockFile);
|
||||
|
||||
for (const auto& entry : g_blockman.m_block_index) {
|
||||
for (const auto& entry : g_chainman.BlockIndex()) {
|
||||
CBlockIndex* pindex = entry.second;
|
||||
if (pindex->nFile == fileNumber) {
|
||||
pindex->nStatus &= ~BLOCK_HAVE_DATA;
|
||||
@ -4271,12 +4277,12 @@ void PruneOneBlockFile(const int fileNumber)
|
||||
// to be downloaded again in order to consider its chain, at which
|
||||
// point it would be considered as a candidate for
|
||||
// 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) {
|
||||
std::multimap<CBlockIndex *, CBlockIndex *>::iterator _it = range.first;
|
||||
range.first++;
|
||||
if (_it->second == pindex) {
|
||||
g_blockman.m_blocks_unlinked.erase(_it);
|
||||
g_chainman.m_blockman.m_blocks_unlinked.erase(_it);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4519,9 +4525,11 @@ void BlockManager::Unload() {
|
||||
|
||||
bool static LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
||||
{
|
||||
if (!g_blockman.LoadBlockIndex(
|
||||
chainparams.GetConsensus(), *pblocktree, ::ChainstateActive().setBlockIndexCandidates))
|
||||
if (!g_chainman.m_blockman.LoadBlockIndex(
|
||||
chainparams.GetConsensus(), *pblocktree,
|
||||
::ChainstateActive().setBlockIndexCandidates)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load block file info
|
||||
pblocktree->ReadLastBlockFile(nLastBlockFile);
|
||||
@ -4543,7 +4551,7 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_RE
|
||||
// Check presence of blk files
|
||||
LogPrintf("Checking all blk files are present...\n");
|
||||
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;
|
||||
if (pindex->nStatus & BLOCK_HAVE_DATA) {
|
||||
@ -4839,8 +4847,7 @@ void CChainState::UnloadBlockIndex() {
|
||||
void UnloadBlockIndex()
|
||||
{
|
||||
LOCK(cs_main);
|
||||
::ChainActive().SetTip(nullptr);
|
||||
g_blockman.Unload();
|
||||
g_chainman.Unload();
|
||||
pindexBestInvalid = nullptr;
|
||||
pindexBestHeader = nullptr;
|
||||
mempool.clear();
|
||||
@ -4853,8 +4860,6 @@ void UnloadBlockIndex()
|
||||
warningcache[b].clear();
|
||||
}
|
||||
fHavePruned = false;
|
||||
|
||||
::ChainstateActive().UnloadBlockIndex();
|
||||
}
|
||||
|
||||
bool LoadBlockIndex(const CChainParams& chainparams)
|
||||
@ -4864,7 +4869,7 @@ bool LoadBlockIndex(const CChainParams& chainparams)
|
||||
if (!fReindex) {
|
||||
bool ret = LoadBlockIndexDB(chainparams);
|
||||
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) {
|
||||
@ -5015,7 +5020,7 @@ void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi
|
||||
// Activate the genesis block so normal node progress can continue
|
||||
if (hash == chainparams.GetConsensus().hashGenesisBlock) {
|
||||
CValidationState state;
|
||||
if (!ActivateBestChain(state, chainparams)) {
|
||||
if (!ActivateBestChain(state, chainparams, nullptr)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -5250,6 +5255,14 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
|
||||
assert(nNodes == forward.size());
|
||||
}
|
||||
|
||||
std::string CChainState::ToString()
|
||||
{
|
||||
CBlockIndex* tip = m_chain.Tip();
|
||||
return strprintf("Chainstate [%s] @ height %d (%s)",
|
||||
m_from_snapshot_blockhash.IsNull() ? "ibd" : "snapshot",
|
||||
tip ? tip->nHeight : -1, tip ? tip->GetBlockHash().ToString() : "null");
|
||||
}
|
||||
|
||||
std::string CBlockFileInfo::ToString() const {
|
||||
return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, FormatISO8601Date(nTimeFirst), FormatISO8601Date(nTimeLast));
|
||||
}
|
||||
@ -5454,10 +5467,99 @@ public:
|
||||
CMainCleanup() {}
|
||||
~CMainCleanup() {
|
||||
// block headers
|
||||
BlockMap::iterator it1 = g_blockman.m_block_index.begin();
|
||||
for (; it1 != g_blockman.m_block_index.end(); it1++)
|
||||
BlockMap::iterator it1 = g_chainman.BlockIndex().begin();
|
||||
for (; it1 != g_chainman.BlockIndex().end(); it1++)
|
||||
delete (*it1).second;
|
||||
g_blockman.m_block_index.clear();
|
||||
g_chainman.BlockIndex().clear();
|
||||
}
|
||||
};
|
||||
static CMainCleanup instance_of_cmaincleanup;
|
||||
|
||||
Optional<uint256> ChainstateManager::SnapshotBlockhash() const {
|
||||
if (m_active_chainstate != nullptr) {
|
||||
// If a snapshot chainstate exists, it will always be our active.
|
||||
return m_active_chainstate->m_from_snapshot_blockhash;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<CChainState*> ChainstateManager::GetAll()
|
||||
{
|
||||
std::vector<CChainState*> out;
|
||||
|
||||
if (!IsSnapshotValidated() && m_ibd_chainstate) {
|
||||
out.push_back(m_ibd_chainstate.get());
|
||||
}
|
||||
|
||||
if (m_snapshot_chainstate) {
|
||||
out.push_back(m_snapshot_chainstate.get());
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
CChainState& ChainstateManager::InitializeChainstate(const uint256& snapshot_blockhash)
|
||||
{
|
||||
bool is_snapshot = !snapshot_blockhash.IsNull();
|
||||
std::unique_ptr<CChainState>& to_modify =
|
||||
is_snapshot ? m_snapshot_chainstate : m_ibd_chainstate;
|
||||
|
||||
if (to_modify) {
|
||||
throw std::logic_error("should not be overwriting a chainstate");
|
||||
}
|
||||
|
||||
to_modify.reset(new CChainState(m_blockman, snapshot_blockhash));
|
||||
|
||||
// Snapshot chainstates and initial IBD chaintates always become active.
|
||||
if (is_snapshot || (!is_snapshot && !m_active_chainstate)) {
|
||||
LogPrintf("Switching active chainstate to %s\n", to_modify->ToString());
|
||||
m_active_chainstate = to_modify.get();
|
||||
} else {
|
||||
throw std::logic_error("unexpected chainstate activation");
|
||||
}
|
||||
|
||||
return *to_modify;
|
||||
}
|
||||
|
||||
CChain& ChainstateManager::ActiveChain() const
|
||||
{
|
||||
assert(m_active_chainstate);
|
||||
return m_active_chainstate->m_chain;
|
||||
}
|
||||
|
||||
bool ChainstateManager::IsSnapshotActive() const
|
||||
{
|
||||
return m_snapshot_chainstate && m_active_chainstate == m_snapshot_chainstate.get();
|
||||
}
|
||||
|
||||
CChainState& ChainstateManager::ValidatedChainstate() const
|
||||
{
|
||||
if (m_snapshot_chainstate && IsSnapshotValidated()) {
|
||||
return *m_snapshot_chainstate.get();
|
||||
}
|
||||
assert(m_ibd_chainstate);
|
||||
return *m_ibd_chainstate.get();
|
||||
}
|
||||
|
||||
bool ChainstateManager::IsBackgroundIBD(CChainState* chainstate) const
|
||||
{
|
||||
return (m_snapshot_chainstate && chainstate == m_ibd_chainstate.get());
|
||||
}
|
||||
|
||||
void ChainstateManager::Unload()
|
||||
{
|
||||
for (CChainState* chainstate : this->GetAll()) {
|
||||
chainstate->m_chain.SetTip(nullptr);
|
||||
chainstate->UnloadBlockIndex();
|
||||
}
|
||||
|
||||
m_blockman.Unload();
|
||||
}
|
||||
|
||||
void ChainstateManager::Reset()
|
||||
{
|
||||
m_ibd_chainstate.reset();
|
||||
m_snapshot_chainstate.reset();
|
||||
m_active_chainstate = nullptr;
|
||||
m_snapshot_validated = false;
|
||||
}
|
||||
|
167
src/validation.h
167
src/validation.h
@ -15,6 +15,7 @@
|
||||
#include <coins.h>
|
||||
#include <crypto/common.h> // for ReadLE64
|
||||
#include <fs.h>
|
||||
#include <optional.h>
|
||||
#include <policy/feerate.h>
|
||||
#include <protocol.h> // For CMessageHeader::MessageStartChars
|
||||
#include <script/script_error.h>
|
||||
@ -510,6 +511,9 @@ enum class CoinsCacheSizeState
|
||||
OK = 0
|
||||
};
|
||||
|
||||
// Defined below, but needed for `friend` usage in CChainState.
|
||||
class ChainstateManager;
|
||||
|
||||
/**
|
||||
* CChainState stores and provides an API to update our local knowledge of the
|
||||
* current best chain.
|
||||
@ -563,8 +567,7 @@ private:
|
||||
std::unique_ptr<CoinsViews> m_coins_views;
|
||||
|
||||
public:
|
||||
CChainState(BlockManager& blockman) : m_blockman(blockman) {}
|
||||
CChainState();
|
||||
explicit CChainState(BlockManager& blockman, uint256 from_snapshot_blockhash = uint256());
|
||||
|
||||
/**
|
||||
* Initialize the CoinsViews UTXO set database management data structures. The in-memory
|
||||
@ -592,6 +595,13 @@ public:
|
||||
//! @see CChain, CBlockIndex.
|
||||
CChain m_chain;
|
||||
|
||||
/**
|
||||
* The blockhash which is the base of the snapshot this chainstate was created from.
|
||||
*
|
||||
* IsNull() if this chainstate was not created from a snapshot.
|
||||
*/
|
||||
const uint256 m_from_snapshot_blockhash{};
|
||||
|
||||
/**
|
||||
* The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself and all ancestors) and
|
||||
* as good as our current tip or better. Entries may be failed, though, and pruning nodes may be
|
||||
@ -713,6 +723,8 @@ public:
|
||||
size_t max_coins_cache_size_bytes,
|
||||
size_t max_mempool_size_bytes) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||
|
||||
std::string ToString() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||
|
||||
private:
|
||||
bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs);
|
||||
bool ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs);
|
||||
@ -729,6 +741,8 @@ private:
|
||||
|
||||
//! Mark a block as not having block data
|
||||
void EraseBlockData(CBlockIndex* index) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
|
||||
friend ChainstateManager;
|
||||
};
|
||||
|
||||
/** Mark a block as precious and reorganize.
|
||||
@ -747,6 +761,150 @@ void EnforceBlock(CValidationState& state, const CChainParams& chainparams, cons
|
||||
/** Remove invalidity status from a block and its descendants. */
|
||||
void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
|
||||
/**
|
||||
* Provides an interface for creating and interacting with one or two
|
||||
* chainstates: an IBD chainstate generated by downloading blocks, and
|
||||
* an optional snapshot chainstate loaded from a UTXO snapshot. Managed
|
||||
* chainstates can be maintained at different heights simultaneously.
|
||||
*
|
||||
* This class provides abstractions that allow the retrieval of the current
|
||||
* most-work chainstate ("Active") as well as chainstates which may be in
|
||||
* background use to validate UTXO snapshots.
|
||||
*
|
||||
* Definitions:
|
||||
*
|
||||
* *IBD chainstate*: a chainstate whose current state has been "fully"
|
||||
* validated by the initial block download process.
|
||||
*
|
||||
* *Snapshot chainstate*: a chainstate populated by loading in an
|
||||
* assumeutxo UTXO snapshot.
|
||||
*
|
||||
* *Active chainstate*: the chainstate containing the current most-work
|
||||
* chain. Consulted by most parts of the system (net_processing,
|
||||
* wallet) as a reflection of the current chain and UTXO set.
|
||||
* This may either be an IBD chainstate or a snapshot chainstate.
|
||||
*
|
||||
* *Background IBD chainstate*: an IBD chainstate for which the
|
||||
* IBD process is happening in the background while use of the
|
||||
* active (snapshot) chainstate allows the rest of the system to function.
|
||||
*
|
||||
* *Validated chainstate*: the most-work chainstate which has been validated
|
||||
* locally via initial block download. This will be the snapshot chainstate
|
||||
* if a snapshot was loaded and all blocks up to the snapshot starting point
|
||||
* have been downloaded and validated (via background validation), otherwise
|
||||
* it will be the IBD chainstate.
|
||||
*/
|
||||
class ChainstateManager
|
||||
{
|
||||
private:
|
||||
//! The chainstate used under normal operation (i.e. "regular" IBD) or, if
|
||||
//! a snapshot is in use, for background validation.
|
||||
//!
|
||||
//! Its contents (including on-disk data) will be deleted *upon shutdown*
|
||||
//! after background validation of the snapshot has completed. We do not
|
||||
//! free the chainstate contents immediately after it finishes validation
|
||||
//! to cautiously avoid a case where some other part of the system is still
|
||||
//! using this pointer (e.g. net_processing).
|
||||
//!
|
||||
//! Once this pointer is set to a corresponding chainstate, it will not
|
||||
//! be reset until init.cpp:Shutdown(). This means it is safe to acquire
|
||||
//! the contents of this pointer with ::cs_main held, release the lock,
|
||||
//! and then use the reference without concern of it being deconstructed.
|
||||
//!
|
||||
//! This is especially important when, e.g., calling ActivateBestChain()
|
||||
//! on all chainstates because we are not able to hold ::cs_main going into
|
||||
//! that call.
|
||||
std::unique_ptr<CChainState> m_ibd_chainstate;
|
||||
|
||||
//! A chainstate initialized on the basis of a UTXO snapshot. If this is
|
||||
//! non-null, it is always our active chainstate.
|
||||
//!
|
||||
//! Once this pointer is set to a corresponding chainstate, it will not
|
||||
//! be reset until init.cpp:Shutdown(). This means it is safe to acquire
|
||||
//! the contents of this pointer with ::cs_main held, release the lock,
|
||||
//! and then use the reference without concern of it being deconstructed.
|
||||
//!
|
||||
//! This is especially important when, e.g., calling ActivateBestChain()
|
||||
//! on all chainstates because we are not able to hold ::cs_main going into
|
||||
//! that call.
|
||||
std::unique_ptr<CChainState> m_snapshot_chainstate;
|
||||
|
||||
//! Points to either the ibd or snapshot chainstate; indicates our
|
||||
//! most-work chain.
|
||||
//!
|
||||
//! Once this pointer is set to a corresponding chainstate, it will not
|
||||
//! be reset until init.cpp:Shutdown(). This means it is safe to acquire
|
||||
//! the contents of this pointer with ::cs_main held, release the lock,
|
||||
//! and then use the reference without concern of it being deconstructed.
|
||||
//!
|
||||
//! This is especially important when, e.g., calling ActivateBestChain()
|
||||
//! on all chainstates because we are not able to hold ::cs_main going into
|
||||
//! that call.
|
||||
CChainState* m_active_chainstate{nullptr};
|
||||
|
||||
//! If true, the assumed-valid chainstate has been fully validated
|
||||
//! by the background validation chainstate.
|
||||
bool m_snapshot_validated{false};
|
||||
|
||||
// For access to m_active_chainstate.
|
||||
friend CChainState& ChainstateActive();
|
||||
friend CChain& ChainActive();
|
||||
|
||||
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
|
||||
//! from a snapshot.
|
||||
//!
|
||||
//! @param[in] snapshot_blockhash If given, signify that this chainstate
|
||||
//! is based on a snapshot.
|
||||
CChainState& InitializeChainstate(const uint256& snapshot_blockhash = uint256())
|
||||
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||
|
||||
//! Get all chainstates currently being used.
|
||||
std::vector<CChainState*> GetAll();
|
||||
|
||||
//! The most-work chain.
|
||||
CChain& ActiveChain() const;
|
||||
int ActiveHeight() const { return ActiveChain().Height(); }
|
||||
CBlockIndex* ActiveTip() const { return ActiveChain().Tip(); }
|
||||
|
||||
BlockMap& BlockIndex() EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
|
||||
{
|
||||
return m_blockman.m_block_index;
|
||||
}
|
||||
|
||||
bool IsSnapshotActive() const;
|
||||
|
||||
Optional<uint256> SnapshotBlockhash() const;
|
||||
|
||||
//! Is there a snapshot in use and has it been fully validated?
|
||||
bool IsSnapshotValidated() const { return m_snapshot_validated; }
|
||||
|
||||
//! @returns true if this chainstate is being used to validate an active
|
||||
//! snapshot in the background.
|
||||
bool IsBackgroundIBD(CChainState* chainstate) const;
|
||||
|
||||
//! Return the most-work chainstate that has been fully validated.
|
||||
//!
|
||||
//! During background validation of a snapshot, this is the IBD chain. After
|
||||
//! background validation has completed, this is the snapshot chain.
|
||||
CChainState& ValidatedChainstate() const;
|
||||
|
||||
CChain& ValidatedChain() const { return ValidatedChainstate().m_chain; }
|
||||
CBlockIndex* ValidatedTip() const { return ValidatedChain().Tip(); }
|
||||
|
||||
//! Unload block index and chain data before shutdown.
|
||||
void Unload() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||
|
||||
//! Clear (deconstruct) chainstate data.
|
||||
void Reset();
|
||||
};
|
||||
|
||||
extern ChainstateManager g_chainman GUARDED_BY(::cs_main);
|
||||
|
||||
/** @returns the most-work chain. */
|
||||
CChain& ChainActive();
|
||||
|
||||
@ -759,11 +917,6 @@ BlockMap& BlockIndex();
|
||||
/** @returns the global previous block index map. */
|
||||
PrevBlockMap& PrevBlockIndex();
|
||||
|
||||
// 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) */
|
||||
extern std::unique_ptr<CBlockTreeDB> pblocktree;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user