Merge bitcoin/bitcoin#29770: index: Check all necessary block data is available before starting to sync

fd06157d14 test: Add coverage for restarted node without any block sync (Fabian Jahr)
3d7ab7ecb7 rpc, test: Address feedback from #29668 (Fabian Jahr)
312919c9dd test: Indices can not start based on block data without undo data (Fabian Jahr)
a9a3b29dd6 index: Check availability of undo data for indices (Fabian Jahr)
881ab4fc82 support multiple block status checks in CheckBlockDataAvailability (furszy)

Pull request description:

  Currently, we check that `BLOCK_HAVE_DATA` is available for all blocks an index needs to sync during startup. However, for `coinstatsindex` and `blockfilterindex` we also need the undo data for these blocks. If that data is missing in the blocks, we are currently still starting to sync each of these indices and then crash later when we encounter the missing data.

  This PR adds explicit knowledge of which block data is needed for each index and then checks its availability during startup before initializing the sync process on them.

  This also addresses a few open comments from #29668 in the last commit.

ACKs for top commit:
  achow101:
    ACK fd06157d14
  furszy:
    utACK fd06157d14
  sedited:
    Re-ACK fd06157d14

Tree-SHA512: e2ed81c93372b02daa8ddf2819df4164f96d92de05b1d48855410ecac78d5fcd9612d7f0e63a9d57d7e75a0b46e1bea278e43ea87f2693af0220d1f9c600e416
This commit is contained in:
Ava Chow
2026-02-20 15:58:48 -08:00
12 changed files with 163 additions and 48 deletions

View File

@@ -80,7 +80,7 @@ BOOST_AUTO_TEST_CASE(get_difficulty_for_very_high_target)
//! Prune chain from height down to genesis block and check that
//! GetPruneHeight returns the correct value
static void CheckGetPruneHeight(node::BlockManager& blockman, CChain& chain, int height) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
static void CheckGetPruneHeight(const node::BlockManager& blockman, const CChain& chain, int height) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
AssertLockHeld(::cs_main);
@@ -98,8 +98,8 @@ static void CheckGetPruneHeight(node::BlockManager& blockman, CChain& chain, int
BOOST_FIXTURE_TEST_CASE(get_prune_height, TestChain100Setup)
{
LOCK(::cs_main);
auto& chain = m_node.chainman->ActiveChain();
auto& blockman = m_node.chainman->m_blockman;
const auto& chain = m_node.chainman->ActiveChain();
const auto& blockman = m_node.chainman->m_blockman;
// Fresh chain of 100 blocks without any pruned blocks, so std::nullopt should be returned
BOOST_CHECK(!GetPruneHeight(blockman, chain).has_value());

View File

@@ -126,6 +126,14 @@ BOOST_FIXTURE_TEST_CASE(blockmanager_block_data_availability, TestChain100Setup)
CBlockIndex* lower_block = chainman->ActiveChain()[tip.nHeight / 2];
BOOST_CHECK(blockman.CheckBlockDataAvailability(tip, *lower_block));
// Ensure we don't fail due to the expected absence of undo data in the genesis block
CBlockIndex* upper_block = chainman->ActiveChain()[2];
CBlockIndex* genesis = chainman->ActiveChain()[0];
BOOST_CHECK(blockman.CheckBlockDataAvailability(*upper_block, *genesis, BlockStatus{BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO}));
// Ensure we detect absence of undo data in the first block
chainman->ActiveChain()[1]->nStatus &= ~BLOCK_HAVE_UNDO;
BOOST_CHECK(!blockman.CheckBlockDataAvailability(tip, *genesis, BlockStatus{BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO}));
// Prune half of the blocks
int height_to_prune = tip.nHeight / 2;
CBlockIndex* first_available_block = chainman->ActiveChain()[height_to_prune + 1];
@@ -136,6 +144,12 @@ BOOST_FIXTURE_TEST_CASE(blockmanager_block_data_availability, TestChain100Setup)
BOOST_CHECK_EQUAL(&blockman.GetFirstBlock(tip, BLOCK_HAVE_DATA), first_available_block);
BOOST_CHECK(blockman.CheckBlockDataAvailability(tip, *first_available_block));
BOOST_CHECK(!blockman.CheckBlockDataAvailability(tip, *last_pruned_block));
// Simulate that the first available block is missing undo data and
// detect this by using a status mask.
first_available_block->nStatus &= ~BLOCK_HAVE_UNDO;
BOOST_CHECK(!blockman.CheckBlockDataAvailability(tip, *first_available_block, BlockStatus{BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO}));
BOOST_CHECK(blockman.CheckBlockDataAvailability(tip, *first_available_block, BlockStatus{BLOCK_HAVE_DATA}));
}
BOOST_FIXTURE_TEST_CASE(blockmanager_block_data_part, TestChain100Setup)