test: add tests for LoadBlockIndex when using multiple chainstates

Incorporates feedback from Russ Yanofsky.
This commit is contained in:
James O'Beirne 2021-11-10 16:35:12 -05:00
parent 0fd599a51a
commit 2283b9cd1e
No known key found for this signature in database
GPG key ID: 7A935DADB2C44F05

View file

@ -312,4 +312,81 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_activate_snapshot, TestChain100Setup)
loaded_snapshot_blockhash);
}
//! Test LoadBlockIndex behavior when multiple chainstates are in use.
//!
//! - First, verfiy that setBlockIndexCandidates is as expected when using a single,
//! fully-validating chainstate.
//!
//! - Then mark a region of the chain BLOCK_ASSUMED_VALID and introduce a second chainstate
//! that will tolerate assumed-valid blocks. Run LoadBlockIndex() and ensure that the first
//! chainstate only contains fully validated blocks and the other chainstate contains all blocks,
//! even those assumed-valid.
//!
BOOST_FIXTURE_TEST_CASE(chainstatemanager_loadblockindex, TestChain100Setup)
{
ChainstateManager& chainman = *Assert(m_node.chainman);
CTxMemPool& mempool = *m_node.mempool;
CChainState& cs1 = chainman.ActiveChainstate();
int num_indexes{0};
int num_assumed_valid{0};
const int expected_assumed_valid{20};
const int last_assumed_valid_idx{40};
const int assumed_valid_start_idx = last_assumed_valid_idx - expected_assumed_valid;
CBlockIndex* validated_tip{nullptr};
CBlockIndex* assumed_tip{chainman.ActiveChain().Tip()};
auto reload_all_block_indexes = [&]() {
for (CChainState* cs : chainman.GetAll()) {
LOCK(::cs_main);
cs->UnloadBlockIndex();
BOOST_CHECK(cs->setBlockIndexCandidates.empty());
}
WITH_LOCK(::cs_main, chainman.LoadBlockIndex());
};
// Ensure that without any assumed-valid BlockIndex entries, all entries are considered
// tip candidates.
reload_all_block_indexes();
BOOST_CHECK_EQUAL(cs1.setBlockIndexCandidates.size(), cs1.m_chain.Height() + 1);
// Mark some region of the chain assumed-valid.
for (int i = 0; i <= cs1.m_chain.Height(); ++i) {
auto index = cs1.m_chain[i];
if (i < last_assumed_valid_idx && i >= assumed_valid_start_idx) {
index->nStatus = BlockStatus::BLOCK_VALID_TREE | BlockStatus::BLOCK_ASSUMED_VALID;
}
++num_indexes;
if (index->IsAssumedValid()) ++num_assumed_valid;
// Note the last fully-validated block as the expected validated tip.
if (i == (assumed_valid_start_idx - 1)) {
validated_tip = index;
BOOST_CHECK(!index->IsAssumedValid());
}
}
BOOST_CHECK_EQUAL(expected_assumed_valid, num_assumed_valid);
CChainState& cs2 = WITH_LOCK(::cs_main,
return chainman.InitializeChainstate(&mempool, GetRandHash()));
reload_all_block_indexes();
// The fully validated chain only has candidates up to the start of the assumed-valid
// blocks.
BOOST_CHECK_EQUAL(cs1.setBlockIndexCandidates.count(validated_tip), 1);
BOOST_CHECK_EQUAL(cs1.setBlockIndexCandidates.count(assumed_tip), 0);
BOOST_CHECK_EQUAL(cs1.setBlockIndexCandidates.size(), assumed_valid_start_idx);
// The assumed-valid tolerant chain has all blocks as candidates.
BOOST_CHECK_EQUAL(cs2.setBlockIndexCandidates.count(validated_tip), 1);
BOOST_CHECK_EQUAL(cs2.setBlockIndexCandidates.count(assumed_tip), 1);
BOOST_CHECK_EQUAL(cs2.setBlockIndexCandidates.size(), num_indexes);
}
BOOST_AUTO_TEST_SUITE_END()