mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-03-27 07:55:39 +01:00
Merge bitcoin/bitcoin#30214: refactor: Improve assumeutxo state representation
82be652e40doc: Improve ChainstateManager documentation, use consistent terms (Ryan Ofsky)af455dcb39refactor: Simplify pruning functions (TheCharlatan)ae85c495f1refactor: Delete ChainstateManager::GetAll() method (Ryan Ofsky)6a572dbda9refactor: Add ChainstateManager::ActivateBestChains() method (Ryan Ofsky)491d827d52refactor: Add ChainstateManager::m_chainstates member (Ryan Ofsky)e514fe6116refactor: Delete ChainstateManager::SnapshotBlockhash() method (Ryan Ofsky)ee35250683refactor: Delete ChainstateManager::IsSnapshotValidated() method (Ryan Ofsky)d9e82299fcrefactor: Delete ChainstateManager::IsSnapshotActive() method (Ryan Ofsky)4dfe383912refactor: Convert ChainstateRole enum to struct (Ryan Ofsky)352ad27fc1refactor: Add ChainstateManager::ValidatedChainstate() method (Ryan Ofsky)a229cb9477refactor: Add ChainstateManager::CurrentChainstate() method (Ryan Ofsky)a9b7f5614crefactor: Add Chainstate::StoragePath() method (Ryan Ofsky)840bd2ef23refactor: Pass chainstate parameters to MaybeCompleteSnapshotValidation (Ryan Ofsky)1598a15aedrefactor: Deduplicate Chainstate activation code (Ryan Ofsky)9fe927b6d6refactor: Add Chainstate m_assumeutxo and m_target_utxohash members (Ryan Ofsky)6082c84713refactor: Add Chainstate::m_target_blockhash member (Ryan Ofsky)de00e87548test: Fix broken chainstatemanager_snapshot_init check (Ryan Ofsky) Pull request description: This PR contains the first part of #28608, which tries to make assumeutxo code more maintainable, and improve it by not locking `cs_main` for a long time when the snapshot block is connected, and by deleting the snapshot validation chainstate when it is no longer used, instead of waiting until the next restart. The changes in this PR are just refactoring. They make `Chainstate` objects self-contained, so for example, it is possible to determine what blocks to connect to a chainstate without querying `ChainstateManager`, and to determine whether a Chainstate is validated without basing it on inferences like `&cs != &ActiveChainstate()` or `GetAll().size() == 1`. The PR also tries to make assumeutxo terminology less confusing, using "current chainstate" to refer to the chainstate targeting the current network tip, and "historical chainstate" to refer to the chainstate downloading old blocks and validating the assumeutxo snapshot. It removes uses of the terms "active chainstate," "usable chainstate," "disabled chainstate," "ibd chainstate," and "snapshot chainstate" which are confusing for various reasons. ACKs for top commit: maflcko: re-review ACK82be652e40🕍 fjahr: re-ACK82be652e40sedited: Re-ACK82be652e40Tree-SHA512: 81c67abba9fc5bb170e32b7bf8a1e4f7b5592315b4ef720be916d5f1f5a7088c0c59cfb697744dd385552f58aa31ee36176bae6a6e465723e65861089a1252e5
This commit is contained in:
@@ -8,6 +8,8 @@
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
using kernel::ChainstateRole;
|
||||
|
||||
// Taken from validation.cpp
|
||||
static constexpr auto DATABASE_WRITE_INTERVAL_MIN{50min};
|
||||
static constexpr auto DATABASE_WRITE_INTERVAL_MAX{70min};
|
||||
@@ -18,7 +20,7 @@ BOOST_FIXTURE_TEST_CASE(chainstate_write_interval, TestingSetup)
|
||||
{
|
||||
struct TestSubscriber final : CValidationInterface {
|
||||
bool m_did_flush{false};
|
||||
void ChainStateFlushed(ChainstateRole, const CBlockLocator&) override
|
||||
void ChainStateFlushed(const ChainstateRole&, const CBlockLocator&) override
|
||||
{
|
||||
m_did_flush = true;
|
||||
}
|
||||
@@ -55,7 +57,7 @@ BOOST_FIXTURE_TEST_CASE(write_during_multiblock_activation, TestChain100Setup)
|
||||
{
|
||||
const CBlockIndex* m_tip{nullptr};
|
||||
const CBlockIndex* m_flushed_at_block{nullptr};
|
||||
void ChainStateFlushed(ChainstateRole, const CBlockLocator&) override
|
||||
void ChainStateFlushed(const ChainstateRole&, const CBlockLocator&) override
|
||||
{
|
||||
m_flushed_at_block = m_tip;
|
||||
}
|
||||
|
||||
@@ -6,12 +6,15 @@
|
||||
#include <index/coinstatsindex.h>
|
||||
#include <interfaces/chain.h>
|
||||
#include <kernel/coinstats.h>
|
||||
#include <kernel/types.h>
|
||||
#include <test/util/setup_common.h>
|
||||
#include <test/util/validation.h>
|
||||
#include <validation.h>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
using kernel::ChainstateRole;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(coinstatsindex_tests)
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
|
||||
@@ -101,7 +104,7 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_unclean_shutdown, TestChain100Setup)
|
||||
// Send block connected notification, then stop the index without
|
||||
// sending a chainstate flushed notification. Prior to #24138, this
|
||||
// would cause the index to be corrupted and fail to reload.
|
||||
ValidationInterfaceTest::BlockConnected(ChainstateRole::NORMAL, index, new_block, new_block_index);
|
||||
ValidationInterfaceTest::BlockConnected(ChainstateRole{}, index, new_block, new_block_index);
|
||||
index.Stop();
|
||||
}
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ void utxo_snapshot_fuzz(FuzzBufferType buffer)
|
||||
|
||||
const auto snapshot_path = gArgs.GetDataDirNet() / "fuzzed_snapshot.dat";
|
||||
|
||||
Assert(!chainman.SnapshotBlockhash());
|
||||
Assert(!chainman.ActiveChainstate().m_from_snapshot_blockhash);
|
||||
|
||||
{
|
||||
AutoFile outfile{fsbridge::fopen(snapshot_path, "wb")};
|
||||
@@ -183,15 +183,13 @@ void utxo_snapshot_fuzz(FuzzBufferType buffer)
|
||||
if (ActivateFuzzedSnapshot()) {
|
||||
LOCK(::cs_main);
|
||||
Assert(!chainman.ActiveChainstate().m_from_snapshot_blockhash->IsNull());
|
||||
Assert(*chainman.ActiveChainstate().m_from_snapshot_blockhash ==
|
||||
*chainman.SnapshotBlockhash());
|
||||
const auto& coinscache{chainman.ActiveChainstate().CoinsTip()};
|
||||
for (const auto& block : *g_chain) {
|
||||
Assert(coinscache.HaveCoin(COutPoint{block->vtx.at(0)->GetHash(), 0}));
|
||||
const auto* index{chainman.m_blockman.LookupBlockIndex(block->GetHash())};
|
||||
Assert(index);
|
||||
Assert(index->nTx == 0);
|
||||
if (index->nHeight == chainman.GetSnapshotBaseHeight()) {
|
||||
if (index->nHeight == chainman.ActiveChainstate().SnapshotBase()->nHeight) {
|
||||
auto params{chainman.GetParams().AssumeutxoForHeight(index->nHeight)};
|
||||
Assert(params.has_value());
|
||||
Assert(params.value().m_chain_tx_count == index->m_chain_tx_count);
|
||||
@@ -202,7 +200,6 @@ void utxo_snapshot_fuzz(FuzzBufferType buffer)
|
||||
Assert(g_chain->size() == coinscache.GetCacheSize());
|
||||
dirty_chainman = true;
|
||||
} else {
|
||||
Assert(!chainman.SnapshotBlockhash());
|
||||
Assert(!chainman.ActiveChainstate().m_from_snapshot_blockhash);
|
||||
}
|
||||
// Snapshot should refuse to load a second time regardless of validity
|
||||
|
||||
@@ -81,7 +81,7 @@ CreateAndActivateUTXOSnapshot(
|
||||
Chainstate& chain = node.chainman->ActiveChainstate();
|
||||
Assert(chain.LoadGenesisBlock());
|
||||
// These cache values will be corrected shortly in `MaybeRebalanceCaches`.
|
||||
chain.InitCoinsDB(1 << 20, true, false, "");
|
||||
chain.InitCoinsDB(1 << 20, /*in_memory=*/true, /*should_wipe=*/false);
|
||||
chain.InitCoinsCache(1 << 20);
|
||||
chain.CoinsTip().SetBestBlock(gen_hash);
|
||||
chain.setBlockIndexCandidates.insert(node.chainman->m_blockman.LookupBlockIndex(gen_hash));
|
||||
|
||||
@@ -9,15 +9,19 @@
|
||||
#include <validation.h>
|
||||
#include <validationinterface.h>
|
||||
|
||||
using kernel::ChainstateRole;
|
||||
|
||||
void TestChainstateManager::DisableNextWrite()
|
||||
{
|
||||
struct TestChainstate : public Chainstate {
|
||||
void ResetNextWrite() { m_next_write = NodeClock::time_point::max() - 1s; }
|
||||
};
|
||||
for (auto* cs : GetAll()) {
|
||||
static_cast<TestChainstate*>(cs)->ResetNextWrite();
|
||||
LOCK(::cs_main);
|
||||
for (const auto& cs : m_chainstates) {
|
||||
static_cast<TestChainstate&>(*cs).ResetNextWrite();
|
||||
}
|
||||
}
|
||||
|
||||
void TestChainstateManager::ResetIbd()
|
||||
{
|
||||
m_cached_finished_ibd = false;
|
||||
@@ -32,10 +36,10 @@ void TestChainstateManager::JumpOutOfIbd()
|
||||
}
|
||||
|
||||
void ValidationInterfaceTest::BlockConnected(
|
||||
ChainstateRole role,
|
||||
CValidationInterface& obj,
|
||||
const std::shared_ptr<const CBlock>& block,
|
||||
const CBlockIndex* pindex)
|
||||
const ChainstateRole& role,
|
||||
CValidationInterface& obj,
|
||||
const std::shared_ptr<const CBlock>& block,
|
||||
const CBlockIndex* pindex)
|
||||
{
|
||||
obj.BlockConnected(role, block, pindex);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ class ValidationInterfaceTest
|
||||
{
|
||||
public:
|
||||
static void BlockConnected(
|
||||
ChainstateRole role,
|
||||
const kernel::ChainstateRole& role,
|
||||
CValidationInterface& obj,
|
||||
const std::shared_ptr<const CBlock>& block,
|
||||
const CBlockIndex* pindex);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <thread>
|
||||
|
||||
using kernel::ChainstateRole;
|
||||
using node::BlockAssembler;
|
||||
|
||||
namespace validation_block_tests {
|
||||
@@ -43,7 +44,7 @@ struct TestSubscriber final : public CValidationInterface {
|
||||
BOOST_CHECK_EQUAL(m_expected_tip, pindexNew->GetBlockHash());
|
||||
}
|
||||
|
||||
void BlockConnected(ChainstateRole role, const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex) override
|
||||
void BlockConnected(const ChainstateRole& role, const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex) override
|
||||
{
|
||||
BOOST_CHECK_EQUAL(m_expected_tip, block->hashPrevBlock);
|
||||
BOOST_CHECK_EQUAL(m_expected_tip, pindex->pprev->GetBlockHash());
|
||||
|
||||
@@ -95,7 +95,7 @@ BOOST_FIXTURE_TEST_CASE(chainstate_update_tip, TestChain100Setup)
|
||||
this, NoMalleation, /*reset_chainstate=*/ true));
|
||||
|
||||
// Ensure our active chain is the snapshot chainstate.
|
||||
BOOST_CHECK(WITH_LOCK(::cs_main, return chainman.IsSnapshotActive()));
|
||||
BOOST_CHECK(WITH_LOCK(::cs_main, return chainman.CurrentChainstate().m_from_snapshot_blockhash));
|
||||
|
||||
curr_tip = get_notify_tip();
|
||||
|
||||
@@ -107,16 +107,7 @@ BOOST_FIXTURE_TEST_CASE(chainstate_update_tip, TestChain100Setup)
|
||||
|
||||
curr_tip = get_notify_tip();
|
||||
|
||||
BOOST_CHECK_EQUAL(chainman.GetAll().size(), 2);
|
||||
|
||||
Chainstate& background_cs{*Assert([&]() -> Chainstate* {
|
||||
for (Chainstate* cs : chainman.GetAll()) {
|
||||
if (cs != &chainman.ActiveChainstate()) {
|
||||
return cs;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}())};
|
||||
Chainstate& background_cs{*Assert(WITH_LOCK(::cs_main, return chainman.HistoricalChainstate()))};
|
||||
|
||||
// Append the first block to the background chain.
|
||||
BlockValidationState state;
|
||||
|
||||
@@ -40,19 +40,19 @@ BOOST_FIXTURE_TEST_SUITE(validation_chainstatemanager_tests, TestingSetup)
|
||||
BOOST_FIXTURE_TEST_CASE(chainstatemanager, TestChain100Setup)
|
||||
{
|
||||
ChainstateManager& manager = *m_node.chainman;
|
||||
std::vector<Chainstate*> chainstates;
|
||||
|
||||
BOOST_CHECK(!manager.SnapshotBlockhash().has_value());
|
||||
BOOST_CHECK(WITH_LOCK(::cs_main, return !manager.CurrentChainstate().m_from_snapshot_blockhash));
|
||||
|
||||
// Create a legacy (IBD) chainstate.
|
||||
//
|
||||
Chainstate& c1 = manager.ActiveChainstate();
|
||||
chainstates.push_back(&c1);
|
||||
|
||||
BOOST_CHECK(!manager.IsSnapshotActive());
|
||||
BOOST_CHECK(WITH_LOCK(::cs_main, return !manager.IsSnapshotValidated()));
|
||||
auto all = manager.GetAll();
|
||||
BOOST_CHECK_EQUAL_COLLECTIONS(all.begin(), all.end(), chainstates.begin(), chainstates.end());
|
||||
BOOST_CHECK(WITH_LOCK(::cs_main, return !manager.CurrentChainstate().m_from_snapshot_blockhash));
|
||||
{
|
||||
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());
|
||||
BOOST_CHECK_EQUAL(&active_chain, &c1.m_chain);
|
||||
@@ -64,13 +64,12 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager, TestChain100Setup)
|
||||
auto exp_tip = c1.m_chain.Tip();
|
||||
BOOST_CHECK_EQUAL(active_tip, exp_tip);
|
||||
|
||||
BOOST_CHECK(!manager.SnapshotBlockhash().has_value());
|
||||
BOOST_CHECK(WITH_LOCK(::cs_main, return !manager.CurrentChainstate().m_from_snapshot_blockhash));
|
||||
|
||||
// Create a snapshot-based chainstate.
|
||||
//
|
||||
const uint256 snapshot_blockhash = active_tip->GetBlockHash();
|
||||
Chainstate& c2 = WITH_LOCK(::cs_main, return manager.ActivateExistingSnapshot(snapshot_blockhash));
|
||||
chainstates.push_back(&c2);
|
||||
Chainstate& c2{WITH_LOCK(::cs_main, return manager.AddChainstate(std::make_unique<Chainstate>(nullptr, manager.m_blockman, manager, snapshot_blockhash)))};
|
||||
c2.InitCoinsDB(
|
||||
/*cache_size_bytes=*/1 << 23, /*in_memory=*/true, /*should_wipe=*/false);
|
||||
{
|
||||
@@ -83,13 +82,16 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager, TestChain100Setup)
|
||||
BlockValidationState _;
|
||||
BOOST_CHECK(c2.ActivateBestChain(_, nullptr));
|
||||
|
||||
BOOST_CHECK_EQUAL(manager.SnapshotBlockhash().value(), snapshot_blockhash);
|
||||
BOOST_CHECK(manager.IsSnapshotActive());
|
||||
BOOST_CHECK(WITH_LOCK(::cs_main, return !manager.IsSnapshotValidated()));
|
||||
BOOST_CHECK_EQUAL(WITH_LOCK(::cs_main, return *manager.CurrentChainstate().m_from_snapshot_blockhash), snapshot_blockhash);
|
||||
BOOST_CHECK(WITH_LOCK(::cs_main, return manager.CurrentChainstate().m_assumeutxo == Assumeutxo::UNVALIDATED));
|
||||
BOOST_CHECK_EQUAL(&c2, &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());
|
||||
BOOST_CHECK_EQUAL(&active_chain2, &c2.m_chain);
|
||||
@@ -135,7 +137,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_rebalance_caches, TestChain100Setup)
|
||||
// Create a snapshot-based chainstate.
|
||||
//
|
||||
CBlockIndex* snapshot_base{WITH_LOCK(manager.GetMutex(), return manager.ActiveChain()[manager.ActiveChain().Height() / 2])};
|
||||
Chainstate& c2 = WITH_LOCK(cs_main, return manager.ActivateExistingSnapshot(*snapshot_base->phashBlock));
|
||||
Chainstate& c2{WITH_LOCK(::cs_main, return manager.AddChainstate(std::make_unique<Chainstate>(nullptr, manager.m_blockman, manager, *snapshot_base->phashBlock)))};
|
||||
chainstates.push_back(&c2);
|
||||
c2.InitCoinsDB(
|
||||
/*cache_size_bytes=*/1 << 23, /*in_memory=*/true, /*should_wipe=*/false);
|
||||
@@ -182,12 +184,10 @@ struct SnapshotTestSetup : TestChain100Setup {
|
||||
{
|
||||
ChainstateManager& chainman = *Assert(m_node.chainman);
|
||||
|
||||
BOOST_CHECK(!chainman.IsSnapshotActive());
|
||||
|
||||
{
|
||||
LOCK(::cs_main);
|
||||
BOOST_CHECK(!chainman.IsSnapshotValidated());
|
||||
BOOST_CHECK(!node::FindSnapshotChainstateDir(chainman.m_options.datadir));
|
||||
BOOST_CHECK(!chainman.CurrentChainstate().m_from_snapshot_blockhash);
|
||||
BOOST_CHECK(!node::FindAssumeutxoChainstateDir(chainman.m_options.datadir));
|
||||
}
|
||||
|
||||
size_t initial_size;
|
||||
@@ -215,7 +215,6 @@ struct SnapshotTestSetup : TestChain100Setup {
|
||||
// Snapshot should refuse to load at this height.
|
||||
BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(this));
|
||||
BOOST_CHECK(!chainman.ActiveChainstate().m_from_snapshot_blockhash);
|
||||
BOOST_CHECK(!chainman.SnapshotBlockhash());
|
||||
|
||||
// Mine 10 more blocks, putting at us height 110 where a valid assumeutxo value can
|
||||
// be found.
|
||||
@@ -240,7 +239,7 @@ struct SnapshotTestSetup : TestChain100Setup {
|
||||
auto_infile >> coin;
|
||||
}));
|
||||
|
||||
BOOST_CHECK(!node::FindSnapshotChainstateDir(chainman.m_options.datadir));
|
||||
BOOST_CHECK(!node::FindAssumeutxoChainstateDir(chainman.m_options.datadir));
|
||||
|
||||
BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(
|
||||
this, [](AutoFile& auto_infile, SnapshotMetadata& metadata) {
|
||||
@@ -264,25 +263,22 @@ struct SnapshotTestSetup : TestChain100Setup {
|
||||
}));
|
||||
|
||||
BOOST_REQUIRE(CreateAndActivateUTXOSnapshot(this));
|
||||
BOOST_CHECK(fs::exists(*node::FindSnapshotChainstateDir(chainman.m_options.datadir)));
|
||||
BOOST_CHECK(fs::exists(*node::FindAssumeutxoChainstateDir(chainman.m_options.datadir)));
|
||||
|
||||
// Ensure our active chain is the snapshot chainstate.
|
||||
BOOST_CHECK(!chainman.ActiveChainstate().m_from_snapshot_blockhash->IsNull());
|
||||
BOOST_CHECK_EQUAL(
|
||||
*chainman.ActiveChainstate().m_from_snapshot_blockhash,
|
||||
*chainman.SnapshotBlockhash());
|
||||
|
||||
Chainstate& snapshot_chainstate = chainman.ActiveChainstate();
|
||||
|
||||
{
|
||||
LOCK(::cs_main);
|
||||
|
||||
fs::path found = *node::FindSnapshotChainstateDir(chainman.m_options.datadir);
|
||||
fs::path found = *node::FindAssumeutxoChainstateDir(chainman.m_options.datadir);
|
||||
|
||||
// Note: WriteSnapshotBaseBlockhash() is implicitly tested above.
|
||||
BOOST_CHECK_EQUAL(
|
||||
*node::ReadSnapshotBaseBlockhash(found),
|
||||
*chainman.SnapshotBlockhash());
|
||||
*Assert(chainman.CurrentChainstate().m_from_snapshot_blockhash));
|
||||
}
|
||||
|
||||
const auto& au_data = ::Params().AssumeutxoForHeight(snapshot_height);
|
||||
@@ -291,7 +287,7 @@ struct SnapshotTestSetup : TestChain100Setup {
|
||||
BOOST_CHECK_EQUAL(tip->m_chain_tx_count, au_data->m_chain_tx_count);
|
||||
|
||||
// To be checked against later when we try loading a subsequent snapshot.
|
||||
uint256 loaded_snapshot_blockhash{*chainman.SnapshotBlockhash()};
|
||||
uint256 loaded_snapshot_blockhash{*Assert(WITH_LOCK(chainman.GetMutex(), return chainman.CurrentChainstate().m_from_snapshot_blockhash))};
|
||||
|
||||
// Make some assertions about the both chainstates. These checks ensure the
|
||||
// legacy chainstate hasn't changed and that the newly created chainstate
|
||||
@@ -300,7 +296,7 @@ struct SnapshotTestSetup : TestChain100Setup {
|
||||
LOCK(::cs_main);
|
||||
int chains_tested{0};
|
||||
|
||||
for (Chainstate* chainstate : chainman.GetAll()) {
|
||||
for (const auto& chainstate : chainman.m_chainstates) {
|
||||
BOOST_TEST_MESSAGE("Checking coins in " << chainstate->ToString());
|
||||
CCoinsViewCache& coinscache = chainstate->CoinsTip();
|
||||
|
||||
@@ -333,10 +329,10 @@ struct SnapshotTestSetup : TestChain100Setup {
|
||||
size_t coins_in_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());
|
||||
CCoinsViewCache& coinscache = chainstate->CoinsTip();
|
||||
bool is_background = chainstate != &chainman.ActiveChainstate();
|
||||
bool is_background = chainstate.get() != &chainman.ActiveChainstate();
|
||||
|
||||
for (CTransactionRef& txn : m_coinbase_txns) {
|
||||
COutPoint op{txn->GetHash(), 0};
|
||||
@@ -373,15 +369,17 @@ struct SnapshotTestSetup : TestChain100Setup {
|
||||
|
||||
BOOST_TEST_MESSAGE("Simulating node restart");
|
||||
{
|
||||
for (Chainstate* cs : chainman.GetAll()) {
|
||||
LOCK(::cs_main);
|
||||
cs->ForceFlushStateToDisk();
|
||||
LOCK(chainman.GetMutex());
|
||||
for (const auto& cs : chainman.m_chainstates) {
|
||||
if (cs->CanFlushToDisk()) cs->ForceFlushStateToDisk();
|
||||
}
|
||||
}
|
||||
{
|
||||
// Process all callbacks referring to the old manager before wiping it.
|
||||
m_node.validation_signals->SyncWithValidationInterfaceQueue();
|
||||
LOCK(::cs_main);
|
||||
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));
|
||||
const ChainstateManager::Options chainman_opts{
|
||||
.chainparams = ::Params(),
|
||||
@@ -446,17 +444,16 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_loadblockindex, TestChain100Setup)
|
||||
BOOST_CHECK_EQUAL(assumed_tip->nHeight, 120);
|
||||
|
||||
auto reload_all_block_indexes = [&]() {
|
||||
LOCK(chainman.GetMutex());
|
||||
// For completeness, we also reset the block sequence counters to
|
||||
// ensure that no state which affects the ranking of tip-candidates is
|
||||
// retained (even though this isn't strictly necessary).
|
||||
WITH_LOCK(::cs_main, return chainman.ResetBlockSequenceCounters());
|
||||
for (Chainstate* cs : chainman.GetAll()) {
|
||||
LOCK(::cs_main);
|
||||
chainman.ResetBlockSequenceCounters();
|
||||
for (const auto& cs : chainman.m_chainstates) {
|
||||
cs->ClearBlockIndexCandidates();
|
||||
BOOST_CHECK(cs->setBlockIndexCandidates.empty());
|
||||
}
|
||||
|
||||
WITH_LOCK(::cs_main, chainman.LoadBlockIndex());
|
||||
chainman.LoadBlockIndex();
|
||||
};
|
||||
|
||||
// Ensure that without any assumed-valid BlockIndex entries, only the current tip is
|
||||
@@ -489,8 +486,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_loadblockindex, TestChain100Setup)
|
||||
}
|
||||
|
||||
// Note: cs2's tip is not set when ActivateExistingSnapshot is called.
|
||||
Chainstate& cs2 = WITH_LOCK(::cs_main,
|
||||
return chainman.ActivateExistingSnapshot(*assumed_base->phashBlock));
|
||||
Chainstate& cs2{WITH_LOCK(::cs_main, return chainman.AddChainstate(std::make_unique<Chainstate>(nullptr, chainman.m_blockman, chainman, *assumed_base->phashBlock)))};
|
||||
|
||||
// Set tip of the fully validated chain to be the validated tip
|
||||
cs1.m_chain.SetTip(*validated_tip);
|
||||
@@ -557,7 +553,8 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_loadblockindex, TestChain100Setup)
|
||||
BOOST_CHECK_EQUAL(cs2.setBlockIndexCandidates.size(), num_indexes - last_assumed_valid_idx + 1);
|
||||
}
|
||||
|
||||
//! Ensure that snapshot chainstates initialize properly when found on disk.
|
||||
//! Ensure that snapshot chainstate can be loaded when found on disk after a
|
||||
//! restart, and that new blocks can be connected to both chainstates.
|
||||
BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_init, SnapshotTestSetup)
|
||||
{
|
||||
ChainstateManager& chainman = *Assert(m_node.chainman);
|
||||
@@ -565,16 +562,15 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_init, SnapshotTestSetup)
|
||||
|
||||
this->SetupSnapshot();
|
||||
|
||||
fs::path snapshot_chainstate_dir = *node::FindSnapshotChainstateDir(chainman.m_options.datadir);
|
||||
fs::path snapshot_chainstate_dir = *node::FindAssumeutxoChainstateDir(chainman.m_options.datadir);
|
||||
BOOST_CHECK(fs::exists(snapshot_chainstate_dir));
|
||||
BOOST_CHECK_EQUAL(snapshot_chainstate_dir, gArgs.GetDataDirNet() / "chainstate_snapshot");
|
||||
|
||||
BOOST_CHECK(chainman.IsSnapshotActive());
|
||||
BOOST_CHECK(WITH_LOCK(::cs_main, return chainman.CurrentChainstate().m_from_snapshot_blockhash));
|
||||
const uint256 snapshot_tip_hash = WITH_LOCK(chainman.GetMutex(),
|
||||
return chainman.ActiveTip()->GetBlockHash());
|
||||
|
||||
auto all_chainstates = chainman.GetAll();
|
||||
BOOST_CHECK_EQUAL(all_chainstates.size(), 2);
|
||||
BOOST_CHECK_EQUAL(WITH_LOCK(chainman.GetMutex(), return chainman.m_chainstates.size()), 2);
|
||||
|
||||
// "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,
|
||||
@@ -591,8 +587,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_init, SnapshotTestSetup)
|
||||
BOOST_CHECK_EQUAL(bg_chainstate.m_chain.Height(), 109);
|
||||
|
||||
// Test that simulating a shutdown (resetting ChainstateManager) and then performing
|
||||
// chainstate reinitializing successfully cleans up the background-validation
|
||||
// chainstate data, and we end up with a single chainstate that is at tip.
|
||||
// chainstate reinitializing successfully reloads both chainstates.
|
||||
ChainstateManager& chainman_restarted = this->SimulateNodeRestart();
|
||||
|
||||
BOOST_TEST_MESSAGE("Performing Load/Verify/Activate of chainstate");
|
||||
@@ -602,12 +597,20 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_init, SnapshotTestSetup)
|
||||
|
||||
{
|
||||
LOCK(chainman_restarted.GetMutex());
|
||||
BOOST_CHECK_EQUAL(chainman_restarted.GetAll().size(), 2);
|
||||
BOOST_CHECK(chainman_restarted.IsSnapshotActive());
|
||||
BOOST_CHECK(!chainman_restarted.IsSnapshotValidated());
|
||||
BOOST_CHECK_EQUAL(chainman_restarted.m_chainstates.size(), 2);
|
||||
// Background chainstate has height of 109 not 110 here due to a quirk
|
||||
// of the LoadVerifyActivate only calling ActivateBestChain on one
|
||||
// chainstate. The height would be 110 after a real restart, but it's
|
||||
// fine for this test which is focused on the snapshot chainstate.
|
||||
BOOST_CHECK_EQUAL(chainman_restarted.m_chainstates[0]->m_chain.Height(), 109);
|
||||
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_assumeutxo == Assumeutxo::UNVALIDATED);
|
||||
|
||||
BOOST_CHECK_EQUAL(chainman_restarted.ActiveTip()->GetBlockHash(), snapshot_tip_hash);
|
||||
BOOST_CHECK_EQUAL(chainman_restarted.ActiveHeight(), 210);
|
||||
BOOST_CHECK_EQUAL(chainman_restarted.HistoricalChainstate()->m_chain.Height(), 109);
|
||||
}
|
||||
|
||||
BOOST_TEST_MESSAGE(
|
||||
@@ -618,12 +621,11 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_init, SnapshotTestSetup)
|
||||
BOOST_CHECK_EQUAL(chainman_restarted.ActiveHeight(), 220);
|
||||
|
||||
// Background chainstate should be unaware of new blocks on the snapshot
|
||||
// chainstate.
|
||||
for (Chainstate* cs : chainman_restarted.GetAll()) {
|
||||
if (cs != &chainman_restarted.ActiveChainstate()) {
|
||||
BOOST_CHECK_EQUAL(cs->m_chain.Height(), 109);
|
||||
}
|
||||
}
|
||||
// chainstate, but the block disconnected above is now reattached.
|
||||
BOOST_CHECK_EQUAL(chainman_restarted.m_chainstates.size(), 2);
|
||||
BOOST_CHECK_EQUAL(chainman_restarted.m_chainstates[0]->m_chain.Height(), 110);
|
||||
BOOST_CHECK_EQUAL(chainman_restarted.m_chainstates[1]->m_chain.Height(), 220);
|
||||
BOOST_CHECK_EQUAL(chainman_restarted.HistoricalChainstate(), nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -633,37 +635,35 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_completion, SnapshotTestSetup
|
||||
|
||||
ChainstateManager& chainman = *Assert(m_node.chainman);
|
||||
Chainstate& active_cs = chainman.ActiveChainstate();
|
||||
Chainstate& validated_cs{*Assert(WITH_LOCK(cs_main, return chainman.HistoricalChainstate()))};
|
||||
auto tip_cache_before_complete = active_cs.m_coinstip_cache_size_bytes;
|
||||
auto db_cache_before_complete = active_cs.m_coinsdb_cache_size_bytes;
|
||||
|
||||
SnapshotCompletionResult res;
|
||||
m_node.notifications->m_shutdown_on_fatal_error = false;
|
||||
|
||||
fs::path snapshot_chainstate_dir = *node::FindSnapshotChainstateDir(chainman.m_options.datadir);
|
||||
fs::path snapshot_chainstate_dir = *node::FindAssumeutxoChainstateDir(chainman.m_options.datadir);
|
||||
BOOST_CHECK(fs::exists(snapshot_chainstate_dir));
|
||||
BOOST_CHECK_EQUAL(snapshot_chainstate_dir, gArgs.GetDataDirNet() / "chainstate_snapshot");
|
||||
|
||||
BOOST_CHECK(chainman.IsSnapshotActive());
|
||||
BOOST_CHECK(WITH_LOCK(::cs_main, return chainman.CurrentChainstate().m_from_snapshot_blockhash));
|
||||
const uint256 snapshot_tip_hash = WITH_LOCK(chainman.GetMutex(),
|
||||
return chainman.ActiveTip()->GetBlockHash());
|
||||
|
||||
res = WITH_LOCK(::cs_main, return chainman.MaybeCompleteSnapshotValidation());
|
||||
res = WITH_LOCK(::cs_main, return chainman.MaybeValidateSnapshot(validated_cs, active_cs));
|
||||
BOOST_CHECK_EQUAL(res, SnapshotCompletionResult::SUCCESS);
|
||||
|
||||
WITH_LOCK(::cs_main, BOOST_CHECK(chainman.IsSnapshotValidated()));
|
||||
BOOST_CHECK(chainman.IsSnapshotActive());
|
||||
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_EQUAL(WITH_LOCK(chainman.GetMutex(), return chainman.HistoricalChainstate()), nullptr);
|
||||
|
||||
// Cache should have been rebalanced and reallocated to the "only" remaining
|
||||
// chainstate.
|
||||
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);
|
||||
|
||||
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.
|
||||
res = WITH_LOCK(::cs_main, return chainman.MaybeCompleteSnapshotValidation());
|
||||
res = WITH_LOCK(::cs_main, return chainman.MaybeValidateSnapshot(validated_cs, active_cs));
|
||||
BOOST_CHECK_EQUAL(res, SnapshotCompletionResult::SKIPPED);
|
||||
|
||||
// The invalid snapshot path should not have been used.
|
||||
@@ -691,9 +691,8 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_completion, SnapshotTestSetup
|
||||
|
||||
{
|
||||
LOCK(chainman_restarted.GetMutex());
|
||||
BOOST_CHECK_EQUAL(chainman_restarted.GetAll().size(), 1);
|
||||
BOOST_CHECK(!chainman_restarted.IsSnapshotActive());
|
||||
BOOST_CHECK(!chainman_restarted.IsSnapshotValidated());
|
||||
BOOST_CHECK_EQUAL(chainman_restarted.m_chainstates.size(), 1);
|
||||
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_coinsdb_cache_size_bytes > db_cache_before_complete);
|
||||
|
||||
@@ -714,6 +713,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_completion_hash_mismatch, Sna
|
||||
{
|
||||
auto chainstates = this->SetupSnapshot();
|
||||
Chainstate& validation_chainstate = *std::get<0>(chainstates);
|
||||
Chainstate& unvalidated_cs = *std::get<1>(chainstates);
|
||||
ChainstateManager& chainman = *Assert(m_node.chainman);
|
||||
SnapshotCompletionResult res;
|
||||
m_node.notifications->m_shutdown_on_fatal_error = false;
|
||||
@@ -734,14 +734,18 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_completion_hash_mismatch, Sna
|
||||
|
||||
{
|
||||
ASSERT_DEBUG_LOG("failed to validate the -assumeutxo snapshot state");
|
||||
res = WITH_LOCK(::cs_main, return chainman.MaybeCompleteSnapshotValidation());
|
||||
res = WITH_LOCK(::cs_main, return chainman.MaybeValidateSnapshot(validation_chainstate, unvalidated_cs));
|
||||
BOOST_CHECK_EQUAL(res, SnapshotCompletionResult::HASH_MISMATCH);
|
||||
}
|
||||
|
||||
auto all_chainstates = chainman.GetAll();
|
||||
BOOST_CHECK_EQUAL(all_chainstates.size(), 1);
|
||||
BOOST_CHECK_EQUAL(all_chainstates[0], &validation_chainstate);
|
||||
BOOST_CHECK_EQUAL(&chainman.ActiveChainstate(), &validation_chainstate);
|
||||
{
|
||||
LOCK(chainman.GetMutex());
|
||||
BOOST_CHECK_EQUAL(chainman.m_chainstates.size(), 2);
|
||||
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";
|
||||
BOOST_CHECK(fs::exists(snapshot_invalid_dir));
|
||||
@@ -762,9 +766,8 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_completion_hash_mismatch, Sna
|
||||
|
||||
{
|
||||
LOCK(::cs_main);
|
||||
BOOST_CHECK_EQUAL(chainman_restarted.GetAll().size(), 1);
|
||||
BOOST_CHECK(!chainman_restarted.IsSnapshotActive());
|
||||
BOOST_CHECK(!chainman_restarted.IsSnapshotValidated());
|
||||
BOOST_CHECK_EQUAL(chainman_restarted.m_chainstates.size(), 1);
|
||||
BOOST_CHECK(!chainman_restarted.CurrentChainstate().m_from_snapshot_blockhash);
|
||||
BOOST_CHECK_EQUAL(chainman_restarted.ActiveHeight(), 210);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <scheduler.h>
|
||||
#include <test/util/setup_common.h>
|
||||
#include <util/check.h>
|
||||
#include <kernel/chain.h>
|
||||
#include <validationinterface.h>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
Reference in New Issue
Block a user