From 881ab4fc82fe3cf36b227cf1ba704448df160745 Mon Sep 17 00:00:00 2001 From: furszy Date: Mon, 16 Jun 2025 11:28:04 -0400 Subject: [PATCH] support multiple block status checks in CheckBlockDataAvailability --- src/node/blockstorage.cpp | 14 +++++++++++--- src/node/blockstorage.h | 2 +- src/test/blockmanager_tests.cpp | 13 +++++++++++++ 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp index 37bf1799aa4..a8ff110c3ad 100644 --- a/src/node/blockstorage.cpp +++ b/src/node/blockstorage.cpp @@ -623,10 +623,18 @@ const CBlockIndex& BlockManager::GetFirstBlock(const CBlockIndex& upper_block, u return *last_block; } -bool BlockManager::CheckBlockDataAvailability(const CBlockIndex& upper_block, const CBlockIndex& lower_block) +bool BlockManager::CheckBlockDataAvailability(const CBlockIndex& upper_block, const CBlockIndex& lower_block, BlockStatus block_status) { - if (!(upper_block.nStatus & BLOCK_HAVE_DATA)) return false; - return &GetFirstBlock(upper_block, BLOCK_HAVE_DATA, &lower_block) == &lower_block; + if (!(upper_block.nStatus & block_status)) return false; + const auto& first_block = GetFirstBlock(upper_block, block_status, &lower_block); + // Special case: the genesis block has no undo data + if (block_status & BLOCK_HAVE_UNDO && lower_block.nHeight == 0 && first_block.nHeight == 1) { + // This might indicate missing data, or it could simply reflect the expected absence of undo data for the genesis block. + // To distinguish between the two, check if all required block data *except* undo is available up to the genesis block. + BlockStatus flags{block_status & ~BLOCK_HAVE_UNDO}; + return first_block.pprev && first_block.pprev->nStatus & flags; + } + return &first_block == &lower_block; } // If we're using -prune with -reindex, then delete block files that will be ignored by the diff --git a/src/node/blockstorage.h b/src/node/blockstorage.h index d8b716e98b1..8cb398fea64 100644 --- a/src/node/blockstorage.h +++ b/src/node/blockstorage.h @@ -414,7 +414,7 @@ public: //! Check if all blocks in the [upper_block, lower_block] range have data available. //! The caller is responsible for ensuring that lower_block is an ancestor of upper_block //! (part of the same chain). - bool CheckBlockDataAvailability(const CBlockIndex& upper_block, const CBlockIndex& lower_block) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + bool CheckBlockDataAvailability(const CBlockIndex& upper_block, const CBlockIndex& lower_block, BlockStatus block_status = BLOCK_HAVE_DATA) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); /** * @brief Returns the earliest block with specified `status_mask` flags set after diff --git a/src/test/blockmanager_tests.cpp b/src/test/blockmanager_tests.cpp index db59931ba78..ff99a1ee4ff 100644 --- a/src/test/blockmanager_tests.cpp +++ b/src/test/blockmanager_tests.cpp @@ -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,11 @@ 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_FIXTURE_TEST_CASE(blockmanager_block_data_part, TestChain100Setup)