refactor: Delete ChainstateManager::GetAll() method

Just use m_chainstates array instead.
This commit is contained in:
Ryan Ofsky committed 2025-12-12 06:49:59 -04:00
1 parent 6a572dbda9
commit ae85c495f1
8 files changed
+95 -80

No files matched your search

+3 -3
View File
@@ -345,7 +345,7 @@ void Shutdown(NodeContext& node)
// FlushStateToDisk generates a ChainStateFlushed callback, which we should avoid missing // FlushStateToDisk generates a ChainStateFlushed callback, which we should avoid missing
if (node.chainman) { if (node.chainman) {
LOCK(cs_main); LOCK(cs_main);
for (Chainstate* chainstate : node.chainman->GetAll()) { for (const auto& chainstate : node.chainman->m_chainstates) {
if (chainstate->CanFlushToDisk()) { if (chainstate->CanFlushToDisk()) {
chainstate->ForceFlushStateToDisk(); chainstate->ForceFlushStateToDisk();
} }
@@ -371,7 +371,7 @@ void Shutdown(NodeContext& node)
if (node.chainman) { if (node.chainman) {
LOCK(cs_main); LOCK(cs_main);
for (Chainstate* chainstate : node.chainman->GetAll()) { for (const auto& chainstate : node.chainman->m_chainstates) {
if (chainstate->CanFlushToDisk()) { if (chainstate->CanFlushToDisk()) {
chainstate->ForceFlushStateToDisk(); chainstate->ForceFlushStateToDisk();
chainstate->ResetCoinsViews(); chainstate->ResetCoinsViews();
@@ -1873,7 +1873,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
if (chainman.m_blockman.IsPruneMode()) { if (chainman.m_blockman.IsPruneMode()) {
if (chainman.m_blockman.m_blockfiles_indexed) { if (chainman.m_blockman.m_blockfiles_indexed) {
LOCK(cs_main); LOCK(cs_main);
for (Chainstate* chainstate : chainman.GetAll()) { for (const auto& chainstate : chainman.m_chainstates) {
uiInterface.InitMessage(_("Pruning blockstore…")); uiInterface.InitMessage(_("Pruning blockstore…"));
chainstate->PruneAndFlush(); chainstate->PruneAndFlush();
} }
+1 -1
View File
@@ -1009,7 +1009,7 @@ void btck_chainstate_manager_destroy(btck_ChainstateManager* chainman)
{ {
{ {
LOCK(btck_ChainstateManager::get(chainman).m_chainman->GetMutex()); LOCK(btck_ChainstateManager::get(chainman).m_chainman->GetMutex());
for (Chainstate* chainstate : btck_ChainstateManager::get(chainman).m_chainman->GetAll()) { for (const auto& chainstate : btck_ChainstateManager::get(chainman).m_chainman->m_chainstates) {
if (chainstate->CanFlushToDisk()) { if (chainstate->CanFlushToDisk()) {
chainstate->ForceFlushStateToDisk(); chainstate->ForceFlushStateToDisk();
chainstate->ResetCoinsViews(); chainstate->ResetCoinsViews();
+9 -2
View File
@@ -318,9 +318,16 @@ void BlockManager::FindFilesToPrune(
ChainstateManager& chainman) ChainstateManager& chainman)
{ {
LOCK2(cs_main, cs_LastBlockFile); LOCK2(cs_main, cs_LastBlockFile);
// Distribute our -prune budget over all chainstates. // Compute `target` value with maximum size (in bytes) of blocks below the
// `last_prune` height which should be preserved and not pruned. The
// `target` value will be derived from the -prune preference provided by the
// user. If there is a historical chainstate being used to populate indexes
// and validate the snapshot, the target is divided by two so half of the
// block storage will be reserved for the historical chainstate, and the
// other half will be reserved for the most-work chainstate.
const int num_chainstates{chainman.HistoricalChainstate() ? 2 : 1};
const auto target = std::max( const auto target = std::max(
MIN_DISK_SPACE_FOR_BLOCK_FILES, GetPruneTarget() / chainman.GetAll().size()); MIN_DISK_SPACE_FOR_BLOCK_FILES, GetPruneTarget() / num_chainstates);
const uint64_t target_sync_height = chainman.m_best_header->nHeight; const uint64_t target_sync_height = chainman.m_best_header->nHeight;
if (chain.m_chain.Height() < 0 || target == 0) { if (chain.m_chain.Height() < 0 || target == 0) {
+12 -12
View File
@@ -66,8 +66,8 @@ static ChainstateLoadResult CompleteChainstateInitialization(
return {ChainstateLoadStatus::FAILURE, _("Error initializing block database")}; return {ChainstateLoadStatus::FAILURE, _("Error initializing block database")};
} }
auto is_coinsview_empty = [&](Chainstate* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { auto is_coinsview_empty = [&](Chainstate& chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
return options.wipe_chainstate_db || chainstate->CoinsTip().GetBestBlock().IsNull(); return options.wipe_chainstate_db || chainstate.CoinsTip().GetBestBlock().IsNull();
}; };
assert(chainman.m_total_coinstip_cache > 0); assert(chainman.m_total_coinstip_cache > 0);
@@ -78,12 +78,12 @@ static ChainstateLoadResult CompleteChainstateInitialization(
// recalculated by `chainman.MaybeRebalanceCaches()`. The discount factor // recalculated by `chainman.MaybeRebalanceCaches()`. The discount factor
// is conservatively chosen such that the sum of the caches does not exceed // is conservatively chosen such that the sum of the caches does not exceed
// the allowable amount during this temporary initialization state. // the allowable amount during this temporary initialization state.
double init_cache_fraction = chainman.GetAll().size() > 1 ? 0.2 : 1.0; double init_cache_fraction = chainman.HistoricalChainstate() ? 0.2 : 1.0;
// 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()!
for (Chainstate* chainstate : chainman.GetAll()) { for (const auto& chainstate : chainman.m_chainstates) {
LogInfo("Initializing chainstate %s", chainstate->ToString()); LogInfo("Initializing chainstate %s", chainstate->ToString());
try { try {
@@ -117,7 +117,7 @@ static ChainstateLoadResult CompleteChainstateInitialization(
chainstate->InitCoinsCache(chainman.m_total_coinstip_cache * init_cache_fraction); chainstate->InitCoinsCache(chainman.m_total_coinstip_cache * init_cache_fraction);
assert(chainstate->CanFlushToDisk()); assert(chainstate->CanFlushToDisk());
if (!is_coinsview_empty(chainstate)) { if (!is_coinsview_empty(*chainstate)) {
// LoadChainTip initializes the chain based on CoinsTip()'s best block // LoadChainTip initializes the chain based on CoinsTip()'s best block
if (!chainstate->LoadChainTip()) { if (!chainstate->LoadChainTip()) {
return {ChainstateLoadStatus::FAILURE, _("Error initializing block database")}; return {ChainstateLoadStatus::FAILURE, _("Error initializing block database")};
@@ -126,9 +126,9 @@ static ChainstateLoadResult CompleteChainstateInitialization(
} }
} }
auto chainstates{chainman.GetAll()}; const auto& chainstates{chainman.m_chainstates};
if (std::any_of(chainstates.begin(), chainstates.end(), if (std::any_of(chainstates.begin(), chainstates.end(),
[](const Chainstate* cs) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return cs->NeedsRedownload(); })) { [](const auto& cs) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return cs->NeedsRedownload(); })) {
return {ChainstateLoadStatus::FAILURE, strprintf(_("Witness data for blocks after height %d requires validation. Please restart with -reindex."), return {ChainstateLoadStatus::FAILURE, strprintf(_("Witness data for blocks after height %d requires validation. Please restart with -reindex."),
chainman.GetConsensus().SegwitHeight)}; chainman.GetConsensus().SegwitHeight)};
}; };
@@ -209,7 +209,7 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize
// Because ValidatedSnapshotCleanup() has torn down chainstates with // Because ValidatedSnapshotCleanup() has torn down chainstates with
// ChainstateManager::ResetChainstates(), reinitialize them here without // ChainstateManager::ResetChainstates(), reinitialize them here without
// duplicating the blockindex work above. // duplicating the blockindex work above.
assert(chainman.GetAll().empty()); assert(chainman.m_chainstates.empty());
chainman.InitializeChainstate(options.mempool); chainman.InitializeChainstate(options.mempool);
@@ -232,14 +232,14 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize
ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager& chainman, const ChainstateLoadOptions& options) ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager& chainman, const ChainstateLoadOptions& options)
{ {
auto is_coinsview_empty = [&](Chainstate* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { auto is_coinsview_empty = [&](Chainstate& chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
return options.wipe_chainstate_db || chainstate->CoinsTip().GetBestBlock().IsNull(); return options.wipe_chainstate_db || chainstate.CoinsTip().GetBestBlock().IsNull();
}; };
LOCK(cs_main); LOCK(cs_main);
for (Chainstate* chainstate : chainman.GetAll()) { for (auto& chainstate : chainman.m_chainstates) {
if (!is_coinsview_empty(chainstate)) { if (!is_coinsview_empty(*chainstate)) {
const CBlockIndex* tip = chainstate->m_chain.Tip(); const CBlockIndex* tip = chainstate->m_chain.Tip();
if (tip && tip->nTime > GetTime() + MAX_FUTURE_BLOCK_TIME) { if (tip && tip->nTime > GetTime() + MAX_FUTURE_BLOCK_TIME) {
return {ChainstateLoadStatus::FAILURE, _("The block database contains a block which appears to be from the future. " return {ChainstateLoadStatus::FAILURE, _("The block database contains a block which appears to be from the future. "
+3 -2
View File
@@ -16,8 +16,9 @@ void TestChainstateManager::DisableNextWrite()
struct TestChainstate : public Chainstate { struct TestChainstate : public Chainstate {
void ResetNextWrite() { m_next_write = NodeClock::time_point::max() - 1s; } void ResetNextWrite() { m_next_write = NodeClock::time_point::max() - 1s; }
}; };
for (auto* cs : GetAll()) { LOCK(::cs_main);
static_cast<TestChainstate*>(cs)->ResetNextWrite(); for (const auto& cs : m_chainstates) {
static_cast<TestChainstate&>(*cs).ResetNextWrite();
} }
} }
+44 -39
View File
@@ -40,18 +40,19 @@ BOOST_FIXTURE_TEST_SUITE(validation_chainstatemanager_tests, TestingSetup)
BOOST_FIXTURE_TEST_CASE(chainstatemanager, TestChain100Setup) BOOST_FIXTURE_TEST_CASE(chainstatemanager, TestChain100Setup)
{ {
ChainstateManager& manager = *m_node.chainman; ChainstateManager& manager = *m_node.chainman;
std::vector<Chainstate*> chainstates;
BOOST_CHECK(WITH_LOCK(::cs_main, return !manager.CurrentChainstate().m_from_snapshot_blockhash)); BOOST_CHECK(WITH_LOCK(::cs_main, return !manager.CurrentChainstate().m_from_snapshot_blockhash));
// Create a legacy (IBD) chainstate. // Create a legacy (IBD) chainstate.
// //
Chainstate& c1 = manager.ActiveChainstate(); Chainstate& c1 = manager.ActiveChainstate();
chainstates.push_back(&c1);
BOOST_CHECK(WITH_LOCK(::cs_main, return !manager.CurrentChainstate().m_from_snapshot_blockhash)); BOOST_CHECK(WITH_LOCK(::cs_main, return !manager.CurrentChainstate().m_from_snapshot_blockhash));
auto all = manager.GetAll(); {
BOOST_CHECK_EQUAL_COLLECTIONS(all.begin(), all.end(), chainstates.begin(), chainstates.end()); LOCK(manager.GetMutex());
BOOST_CHECK_EQUAL(manager.m_chainstates.size(), 1);
BOOST_CHECK_EQUAL(manager.m_chainstates[0].get(), &c1);
}
auto& active_chain = WITH_LOCK(manager.GetMutex(), return manager.ActiveChain()); auto& active_chain = WITH_LOCK(manager.GetMutex(), return manager.ActiveChain());
BOOST_CHECK_EQUAL(&active_chain, &c1.m_chain); BOOST_CHECK_EQUAL(&active_chain, &c1.m_chain);
@@ -69,7 +70,6 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager, TestChain100Setup)
// //
const uint256 snapshot_blockhash = active_tip->GetBlockHash(); const uint256 snapshot_blockhash = active_tip->GetBlockHash();
Chainstate& c2{WITH_LOCK(::cs_main, return manager.AddChainstate(std::make_unique<Chainstate>(nullptr, manager.m_blockman, manager, snapshot_blockhash)))}; Chainstate& c2{WITH_LOCK(::cs_main, return manager.AddChainstate(std::make_unique<Chainstate>(nullptr, manager.m_blockman, manager, snapshot_blockhash)))};
chainstates.push_back(&c2);
c2.InitCoinsDB( c2.InitCoinsDB(
/*cache_size_bytes=*/1 << 23, /*in_memory=*/true, /*should_wipe=*/false); /*cache_size_bytes=*/1 << 23, /*in_memory=*/true, /*should_wipe=*/false);
{ {
@@ -86,8 +86,12 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager, TestChain100Setup)
BOOST_CHECK(WITH_LOCK(::cs_main, return manager.CurrentChainstate().m_assumeutxo == Assumeutxo::UNVALIDATED)); BOOST_CHECK(WITH_LOCK(::cs_main, return manager.CurrentChainstate().m_assumeutxo == Assumeutxo::UNVALIDATED));
BOOST_CHECK_EQUAL(&c2, &manager.ActiveChainstate()); BOOST_CHECK_EQUAL(&c2, &manager.ActiveChainstate());
BOOST_CHECK(&c1 != &manager.ActiveChainstate()); BOOST_CHECK(&c1 != &manager.ActiveChainstate());
auto all2 = manager.GetAll(); {
BOOST_CHECK_EQUAL_COLLECTIONS(all2.begin(), all2.end(), chainstates.begin(), chainstates.end()); LOCK(manager.GetMutex());
BOOST_CHECK_EQUAL(manager.m_chainstates.size(), 2);
BOOST_CHECK_EQUAL(manager.m_chainstates[0].get(), &c1);
BOOST_CHECK_EQUAL(manager.m_chainstates[1].get(), &c2);
}
auto& active_chain2 = WITH_LOCK(manager.GetMutex(), return manager.ActiveChain()); auto& active_chain2 = WITH_LOCK(manager.GetMutex(), return manager.ActiveChain());
BOOST_CHECK_EQUAL(&active_chain2, &c2.m_chain); BOOST_CHECK_EQUAL(&active_chain2, &c2.m_chain);
@@ -292,7 +296,7 @@ struct SnapshotTestSetup : TestChain100Setup {
LOCK(::cs_main); LOCK(::cs_main);
int chains_tested{0}; int chains_tested{0};
for (Chainstate* chainstate : chainman.GetAll()) { for (const auto& chainstate : chainman.m_chainstates) {
BOOST_TEST_MESSAGE("Checking coins in " << chainstate->ToString()); BOOST_TEST_MESSAGE("Checking coins in " << chainstate->ToString());
CCoinsViewCache& coinscache = chainstate->CoinsTip(); CCoinsViewCache& coinscache = chainstate->CoinsTip();
@@ -325,10 +329,10 @@ struct SnapshotTestSetup : TestChain100Setup {
size_t coins_in_background{0}; size_t coins_in_background{0};
size_t coins_missing_from_background{0}; size_t coins_missing_from_background{0};
for (Chainstate* chainstate : chainman.GetAll()) { for (const auto& chainstate : chainman.m_chainstates) {
BOOST_TEST_MESSAGE("Checking coins in " << chainstate->ToString()); BOOST_TEST_MESSAGE("Checking coins in " << chainstate->ToString());
CCoinsViewCache& coinscache = chainstate->CoinsTip(); CCoinsViewCache& coinscache = chainstate->CoinsTip();
bool is_background = chainstate != &chainman.ActiveChainstate(); bool is_background = chainstate.get() != &chainman.ActiveChainstate();
for (CTransactionRef& txn : m_coinbase_txns) { for (CTransactionRef& txn : m_coinbase_txns) {
COutPoint op{txn->GetHash(), 0}; COutPoint op{txn->GetHash(), 0};
@@ -365,15 +369,17 @@ struct SnapshotTestSetup : TestChain100Setup {
BOOST_TEST_MESSAGE("Simulating node restart"); BOOST_TEST_MESSAGE("Simulating node restart");
{ {
for (Chainstate* cs : chainman.GetAll()) { LOCK(chainman.GetMutex());
LOCK(::cs_main); for (const auto& cs : chainman.m_chainstates) {
cs->ForceFlushStateToDisk(); if (cs->CanFlushToDisk()) cs->ForceFlushStateToDisk();
} }
}
{
// Process all callbacks referring to the old manager before wiping it. // Process all callbacks referring to the old manager before wiping it.
m_node.validation_signals->SyncWithValidationInterfaceQueue(); m_node.validation_signals->SyncWithValidationInterfaceQueue();
LOCK(::cs_main); LOCK(::cs_main);
chainman.ResetChainstates(); chainman.ResetChainstates();
BOOST_CHECK_EQUAL(chainman.GetAll().size(), 0); BOOST_CHECK_EQUAL(chainman.m_chainstates.size(), 0);
m_node.notifications = std::make_unique<KernelNotifications>(Assert(m_node.shutdown_request), m_node.exit_status, *Assert(m_node.warnings)); m_node.notifications = std::make_unique<KernelNotifications>(Assert(m_node.shutdown_request), m_node.exit_status, *Assert(m_node.warnings));
const ChainstateManager::Options chainman_opts{ const ChainstateManager::Options chainman_opts{
.chainparams = ::Params(), .chainparams = ::Params(),
@@ -438,17 +444,16 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_loadblockindex, TestChain100Setup)
BOOST_CHECK_EQUAL(assumed_tip->nHeight, 120); BOOST_CHECK_EQUAL(assumed_tip->nHeight, 120);
auto reload_all_block_indexes = [&]() { auto reload_all_block_indexes = [&]() {
LOCK(chainman.GetMutex());
// For completeness, we also reset the block sequence counters to // For completeness, we also reset the block sequence counters to
// ensure that no state which affects the ranking of tip-candidates is // ensure that no state which affects the ranking of tip-candidates is
// retained (even though this isn't strictly necessary). // retained (even though this isn't strictly necessary).
WITH_LOCK(::cs_main, return chainman.ResetBlockSequenceCounters()); chainman.ResetBlockSequenceCounters();
for (Chainstate* cs : chainman.GetAll()) { for (const auto& cs : chainman.m_chainstates) {
LOCK(::cs_main);
cs->ClearBlockIndexCandidates(); cs->ClearBlockIndexCandidates();
BOOST_CHECK(cs->setBlockIndexCandidates.empty()); BOOST_CHECK(cs->setBlockIndexCandidates.empty());
} }
chainman.LoadBlockIndex();
WITH_LOCK(::cs_main, chainman.LoadBlockIndex());
}; };
// Ensure that without any assumed-valid BlockIndex entries, only the current tip is // Ensure that without any assumed-valid BlockIndex entries, only the current tip is
@@ -565,8 +570,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_init, SnapshotTestSetup)
const uint256 snapshot_tip_hash = WITH_LOCK(chainman.GetMutex(), const uint256 snapshot_tip_hash = WITH_LOCK(chainman.GetMutex(),
return chainman.ActiveTip()->GetBlockHash()); return chainman.ActiveTip()->GetBlockHash());
auto all_chainstates = chainman.GetAll(); BOOST_CHECK_EQUAL(WITH_LOCK(chainman.GetMutex(), return chainman.m_chainstates.size()), 2);
BOOST_CHECK_EQUAL(all_chainstates.size(), 2);
// "Rewind" the background chainstate so that its tip is not at the // "Rewind" the background chainstate so that its tip is not at the
// base block of the snapshot - this is so after simulating a node restart, // base block of the snapshot - this is so after simulating a node restart,
@@ -591,23 +595,22 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_init, SnapshotTestSetup)
// This call reinitializes the chainstates. // This call reinitializes the chainstates.
this->LoadVerifyActivateChainstate(); this->LoadVerifyActivateChainstate();
std::vector<Chainstate*> chainstates;
{ {
LOCK(chainman_restarted.GetMutex()); LOCK(chainman_restarted.GetMutex());
chainstates = chainman_restarted.GetAll(); BOOST_CHECK_EQUAL(chainman_restarted.m_chainstates.size(), 2);
BOOST_CHECK_EQUAL(chainstates.size(), 2);
// Background chainstate has height of 109 not 110 here due to a quirk // Background chainstate has height of 109 not 110 here due to a quirk
// of the LoadVerifyActivate only calling ActivateBestChain on one // of the LoadVerifyActivate only calling ActivateBestChain on one
// chainstate. The height would be 110 after a real restart, but it's // chainstate. The height would be 110 after a real restart, but it's
// fine for this test which is focused on the snapshot chainstate. // fine for this test which is focused on the snapshot chainstate.
BOOST_CHECK_EQUAL(chainstates[0]->m_chain.Height(), 109); BOOST_CHECK_EQUAL(chainman_restarted.m_chainstates[0]->m_chain.Height(), 109);
BOOST_CHECK_EQUAL(chainstates[1]->m_chain.Height(), 210); BOOST_CHECK_EQUAL(chainman_restarted.m_chainstates[1]->m_chain.Height(), 210);
BOOST_CHECK(chainman_restarted.CurrentChainstate().m_from_snapshot_blockhash); BOOST_CHECK(chainman_restarted.CurrentChainstate().m_from_snapshot_blockhash);
BOOST_CHECK(chainman_restarted.CurrentChainstate().m_assumeutxo == Assumeutxo::UNVALIDATED); BOOST_CHECK(chainman_restarted.CurrentChainstate().m_assumeutxo == Assumeutxo::UNVALIDATED);
BOOST_CHECK_EQUAL(chainman_restarted.ActiveTip()->GetBlockHash(), snapshot_tip_hash); BOOST_CHECK_EQUAL(chainman_restarted.ActiveTip()->GetBlockHash(), snapshot_tip_hash);
BOOST_CHECK_EQUAL(chainman_restarted.ActiveHeight(), 210); BOOST_CHECK_EQUAL(chainman_restarted.ActiveHeight(), 210);
BOOST_CHECK_EQUAL(chainman_restarted.HistoricalChainstate()->m_chain.Height(), 109);
} }
BOOST_TEST_MESSAGE( BOOST_TEST_MESSAGE(
@@ -619,9 +622,10 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_init, SnapshotTestSetup)
// Background chainstate should be unaware of new blocks on the snapshot // Background chainstate should be unaware of new blocks on the snapshot
// chainstate, but the block disconnected above is now reattached. // chainstate, but the block disconnected above is now reattached.
BOOST_CHECK_EQUAL(chainstates[0]->m_chain.Height(), 110); BOOST_CHECK_EQUAL(chainman_restarted.m_chainstates.size(), 2);
BOOST_CHECK_EQUAL(chainstates[1]->m_chain.Height(), 220); BOOST_CHECK_EQUAL(chainman_restarted.m_chainstates[0]->m_chain.Height(), 110);
BOOST_CHECK_EQUAL(chainman_restarted.GetAll().size(), 1); BOOST_CHECK_EQUAL(chainman_restarted.m_chainstates[1]->m_chain.Height(), 220);
BOOST_CHECK_EQUAL(chainman_restarted.HistoricalChainstate(), nullptr);
} }
} }
@@ -651,16 +655,13 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_completion, SnapshotTestSetup
BOOST_CHECK(WITH_LOCK(::cs_main, return chainman.CurrentChainstate().m_assumeutxo == Assumeutxo::VALIDATED)); BOOST_CHECK(WITH_LOCK(::cs_main, return chainman.CurrentChainstate().m_assumeutxo == Assumeutxo::VALIDATED));
BOOST_CHECK(WITH_LOCK(::cs_main, return chainman.CurrentChainstate().m_from_snapshot_blockhash)); BOOST_CHECK(WITH_LOCK(::cs_main, return chainman.CurrentChainstate().m_from_snapshot_blockhash));
BOOST_CHECK_EQUAL(WITH_LOCK(chainman.GetMutex(), return chainman.HistoricalChainstate()), nullptr);
// Cache should have been rebalanced and reallocated to the "only" remaining // Cache should have been rebalanced and reallocated to the "only" remaining
// chainstate. // chainstate.
BOOST_CHECK(active_cs.m_coinstip_cache_size_bytes > tip_cache_before_complete); BOOST_CHECK(active_cs.m_coinstip_cache_size_bytes > tip_cache_before_complete);
BOOST_CHECK(active_cs.m_coinsdb_cache_size_bytes > db_cache_before_complete); BOOST_CHECK(active_cs.m_coinsdb_cache_size_bytes > db_cache_before_complete);
auto all_chainstates = chainman.GetAll();
BOOST_CHECK_EQUAL(all_chainstates.size(), 1);
BOOST_CHECK_EQUAL(all_chainstates[0], &active_cs);
// Trying completion again should return false. // Trying completion again should return false.
res = WITH_LOCK(::cs_main, return chainman.MaybeValidateSnapshot(validated_cs, active_cs)); res = WITH_LOCK(::cs_main, return chainman.MaybeValidateSnapshot(validated_cs, active_cs));
BOOST_CHECK_EQUAL(res, SnapshotCompletionResult::SKIPPED); BOOST_CHECK_EQUAL(res, SnapshotCompletionResult::SKIPPED);
@@ -690,7 +691,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_completion, SnapshotTestSetup
{ {
LOCK(chainman_restarted.GetMutex()); LOCK(chainman_restarted.GetMutex());
BOOST_CHECK_EQUAL(chainman_restarted.GetAll().size(), 1); BOOST_CHECK_EQUAL(chainman_restarted.m_chainstates.size(), 1);
BOOST_CHECK(!chainman_restarted.CurrentChainstate().m_from_snapshot_blockhash); BOOST_CHECK(!chainman_restarted.CurrentChainstate().m_from_snapshot_blockhash);
BOOST_CHECK(active_cs2.m_coinstip_cache_size_bytes > tip_cache_before_complete); BOOST_CHECK(active_cs2.m_coinstip_cache_size_bytes > tip_cache_before_complete);
BOOST_CHECK(active_cs2.m_coinsdb_cache_size_bytes > db_cache_before_complete); BOOST_CHECK(active_cs2.m_coinsdb_cache_size_bytes > db_cache_before_complete);
@@ -737,10 +738,14 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_completion_hash_mismatch, Sna
BOOST_CHECK_EQUAL(res, SnapshotCompletionResult::HASH_MISMATCH); BOOST_CHECK_EQUAL(res, SnapshotCompletionResult::HASH_MISMATCH);
} }
auto all_chainstates = chainman.GetAll(); {
BOOST_CHECK_EQUAL(all_chainstates.size(), 1); LOCK(chainman.GetMutex());
BOOST_CHECK_EQUAL(all_chainstates[0], &validation_chainstate); BOOST_CHECK_EQUAL(chainman.m_chainstates.size(), 2);
BOOST_CHECK_EQUAL(&chainman.ActiveChainstate(), &validation_chainstate); BOOST_CHECK(chainman.m_chainstates[0]->m_assumeutxo == Assumeutxo::VALIDATED);
BOOST_CHECK(!chainman.m_chainstates[0]->SnapshotBase());
BOOST_CHECK(chainman.m_chainstates[1]->m_assumeutxo == Assumeutxo::INVALID);
BOOST_CHECK(chainman.m_chainstates[1]->SnapshotBase());
}
fs::path snapshot_invalid_dir = gArgs.GetDataDirNet() / "chainstate_snapshot_INVALID"; fs::path snapshot_invalid_dir = gArgs.GetDataDirNet() / "chainstate_snapshot_INVALID";
BOOST_CHECK(fs::exists(snapshot_invalid_dir)); BOOST_CHECK(fs::exists(snapshot_invalid_dir));
@@ -761,7 +766,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_completion_hash_mismatch, Sna
{ {
LOCK(::cs_main); LOCK(::cs_main);
BOOST_CHECK_EQUAL(chainman_restarted.GetAll().size(), 1); BOOST_CHECK_EQUAL(chainman_restarted.m_chainstates.size(), 1);
BOOST_CHECK(!chainman_restarted.CurrentChainstate().m_from_snapshot_blockhash); BOOST_CHECK(!chainman_restarted.CurrentChainstate().m_from_snapshot_blockhash);
BOOST_CHECK_EQUAL(chainman_restarted.ActiveHeight(), 210); BOOST_CHECK_EQUAL(chainman_restarted.ActiveHeight(), 210);
} }
+23 -18
View File
@@ -3811,6 +3811,15 @@ void Chainstate::ResetBlockFailureFlags(CBlockIndex *pindex) {
void Chainstate::TryAddBlockIndexCandidate(CBlockIndex* pindex) void Chainstate::TryAddBlockIndexCandidate(CBlockIndex* pindex)
{ {
AssertLockHeld(cs_main); AssertLockHeld(cs_main);
// Do not continue building a chainstate that is based on an invalid
// snapshot. This is a belt-and-suspenders type of check because if an
// invalid snapshot is loaded, the node will shut down to force a manual
// intervention. But it is good to handle this case correctly regardless.
if (m_assumeutxo == Assumeutxo::INVALID) {
return;
}
// The block only is a candidate for the most-work-chain if it has the same // The block only is a candidate for the most-work-chain if it has the same
// or more work than our current tip. // or more work than our current tip.
if (m_chain.Tip() != nullptr && setBlockIndexCandidates.value_comp()(pindex, m_chain.Tip())) { if (m_chain.Tip() != nullptr && setBlockIndexCandidates.value_comp()(pindex, m_chain.Tip())) {
@@ -3877,7 +3886,7 @@ void ChainstateManager::ReceivedBlockTransactions(const CBlock& block, CBlockInd
} }
pindex->m_chain_tx_count = prev_tx_sum(*pindex); pindex->m_chain_tx_count = prev_tx_sum(*pindex);
pindex->nSequenceId = nBlockSequenceId++; pindex->nSequenceId = nBlockSequenceId++;
for (Chainstate *c : GetAll()) { for (const auto& c : m_chainstates) {
c->TryAddBlockIndexCandidate(pindex); c->TryAddBlockIndexCandidate(pindex);
} }
std::pair<std::multimap<CBlockIndex*, CBlockIndex*>::iterator, std::multimap<CBlockIndex*, CBlockIndex*>::iterator> range = m_blockman.m_blocks_unlinked.equal_range(pindex); std::pair<std::multimap<CBlockIndex*, CBlockIndex*>::iterator, std::multimap<CBlockIndex*, CBlockIndex*>::iterator> range = m_blockman.m_blocks_unlinked.equal_range(pindex);
@@ -4980,7 +4989,7 @@ bool ChainstateManager::LoadBlockIndex()
(pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) &&
(pindex->HaveNumChainTxs() || pindex->pprev == nullptr))) { (pindex->HaveNumChainTxs() || pindex->pprev == nullptr))) {
for (Chainstate* chainstate : GetAll()) { for (const auto& chainstate : m_chainstates) {
chainstate->TryAddBlockIndexCandidate(pindex); chainstate->TryAddBlockIndexCandidate(pindex);
} }
} }
@@ -5595,18 +5604,6 @@ double ChainstateManager::GuessVerificationProgress(const CBlockIndex* pindex) c
return std::min<double>(pindex->m_chain_tx_count / fTxTotal, 1.0); return std::min<double>(pindex->m_chain_tx_count / fTxTotal, 1.0);
} }
std::vector<Chainstate*> ChainstateManager::GetAll()
{
LOCK(::cs_main);
std::vector<Chainstate*> out;
for (const auto& cs : m_chainstates) {
if (cs && cs->m_assumeutxo != Assumeutxo::INVALID && !cs->m_target_utxohash) out.push_back(cs.get());
}
return out;
}
Chainstate& ChainstateManager::InitializeChainstate(CTxMemPool* mempool) Chainstate& ChainstateManager::InitializeChainstate(CTxMemPool* mempool)
{ {
AssertLockHeld(::cs_main); AssertLockHeld(::cs_main);
@@ -6354,9 +6351,7 @@ bool ChainstateManager::ValidatedSnapshotCleanup(Chainstate& validated_cs, Chain
// The caller of this method will be responsible for reinitializing chainstates // The caller of this method will be responsible for reinitializing chainstates
// if they want to continue operation. // if they want to continue operation.
this->ResetChainstates(); this->ResetChainstates();
assert(this->m_chainstates.size() == 0);
// No chainstates should be considered usable.
assert(this->GetAll().size() == 0);
LogInfo("[snapshot] deleting background chainstate directory (now unnecessary) (%s)", LogInfo("[snapshot] deleting background chainstate directory (now unnecessary) (%s)",
fs::PathToString(validated_path)); fs::PathToString(validated_path));
@@ -6445,7 +6440,17 @@ util::Result<void> ChainstateManager::ActivateBestChains()
// the chainman unique_ptrs since ABC requires us not to be holding cs_main, so retrieve // the chainman unique_ptrs since ABC requires us not to be holding cs_main, so retrieve
// the relevant pointers before the ABC call. // the relevant pointers before the ABC call.
AssertLockNotHeld(cs_main); AssertLockNotHeld(cs_main);
for (Chainstate* chainstate : GetAll()) { std::vector<Chainstate*> chainstates;
{
LOCK(GetMutex());
chainstates.reserve(m_chainstates.size());
for (const auto& chainstate : m_chainstates) {
if (chainstate && chainstate->m_assumeutxo != Assumeutxo::INVALID && !chainstate->m_target_utxohash) {
chainstates.push_back(chainstate.get());
}
}
}
for (Chainstate* chainstate : chainstates) {
BlockValidationState state; BlockValidationState state;
if (!chainstate->ActivateBestChain(state, nullptr)) { if (!chainstate->ActivateBestChain(state, nullptr)) {
LOCK(GetMutex()); LOCK(GetMutex());
-3
View File
@@ -1081,9 +1081,6 @@ public:
// constructor // constructor
Chainstate& InitializeChainstate(CTxMemPool* mempool) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); Chainstate& InitializeChainstate(CTxMemPool* mempool) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
//! Get all chainstates currently being used.
std::vector<Chainstate*> GetAll();
//! Construct and activate a Chainstate on the basis of UTXO snapshot data. //! Construct and activate a Chainstate on the basis of UTXO snapshot data.
//! //!
//! Steps: //! Steps: