Merge bitcoin/bitcoin#32950: validation: remove BLOCK_FAILED_CHILD

fb3e1bf9c9772631571ca46d29c50330ebf54dfd test: check LoadBlockIndex correctly recomputes invalidity flags (stratospher)
29740c06ac53f55f71acf2a1b42b193aac39f579 validation: remove BLOCK_FAILED_MASK (stratospher)
b5b2956bda32b7b4ebc25c83b4d792ecd01f02b4 validation: reset BLOCK_FAILED_CHILD to BLOCK_FAILED_VALID when loading from disk (stratospher)
37bc207852788340dc2a1b33a73748f43226978a validation: stop using BLOCK_FAILED_CHILD (stratospher)
120c631e16893821ea4c73ff70ac60e4fec0429f refactor: use clearer variables in InvalidateBlock() (stratospher)
18f11695c755c379ca67ca0bce8d17492ad9af18 validation: don't update BLOCK_FAILED_VALID to BLOCK_FAILED_CHILD in InvalidateBlock (stratospher)

Pull request description:

  Fixes https://github.com/bitcoin/bitcoin/issues/32173

  even though we have a distinction between `BLOCK_FAILED_VALID` and `BLOCK_FAILED_CHILD` in the codebase,
  we don't use it for anything. Whenever we check for BlockStatus, we use `BLOCK_FAILED_MASK` which encompasses both of them.

  Since there is no functional difference between `BLOCK_FAILED_VALID` and `BLOCK_FAILED_CHILD` and it's added
  code complexity to correctly categorise them (ex: https://github.com/bitcoin/bitcoin/pull/31405#discussion_r1914366243, https://github.com/bitcoin/bitcoin/pull/16856#issuecomment-565506585), we could just remove it.

  Looking for conceptual feedback on whether it's better to improve handling of `BLOCK_FAILED_CHILD` in the codebase or remove `BLOCK_FAILED_CHILD`.

  Of less relevance, but it would also fix a `reconsiderblock` crash that could happen in the situation mentioned in https://github.com/bitcoin/bitcoin/issues/32173#issuecomment-2767030982

  Similar attempt in the past in https://github.com/bitcoin/bitcoin/pull/16856#issuecomment-568073859

ACKs for top commit:
  stickies-v:
    re-ACK fb3e1bf9c9772631571ca46d29c50330ebf54dfd
  alexanderwiederin:
    ACK fb3e1bf9c9772631571ca46d29c50330ebf54dfd
  mzumsande:
    re-ACK fb3e1bf9c9772631571ca46d29c50330ebf54dfd

Tree-SHA512: e97b739885c40a8c021966438e9767cc02bc183056236d6a8c64f6819347ae70c0fbcd71cc2528917560d9f4fd56aed45faf1b6c75d98de7b08b621693a97fbc
This commit is contained in:
merge-script 2026-02-18 15:47:57 +00:00
commit 655b9d12ee
No known key found for this signature in database
GPG Key ID: 2EEB9F5CC09526C1
9 changed files with 83 additions and 68 deletions

View File

@ -77,8 +77,7 @@ enum BlockStatus : uint32_t {
BLOCK_HAVE_MASK = BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO,
BLOCK_FAILED_VALID = 32, //!< stage after last reached validness failed
BLOCK_FAILED_CHILD = 64, //!< descends from failed block
BLOCK_FAILED_MASK = BLOCK_FAILED_VALID | BLOCK_FAILED_CHILD,
BLOCK_FAILED_CHILD = 64, //!< Unused flag that was previously set when descending from failed block
BLOCK_OPT_WITNESS = 128, //!< block data in blk*.dat was received with a witness-enforcing client
@ -253,7 +252,7 @@ public:
{
AssertLockHeld(::cs_main);
assert(!(nUpTo & ~BLOCK_VALID_MASK)); // Only validity flags allowed.
if (nStatus & BLOCK_FAILED_MASK)
if (nStatus & BLOCK_FAILED_VALID)
return false;
return ((nStatus & BLOCK_VALID_MASK) >= nUpTo);
}
@ -264,7 +263,7 @@ public:
{
AssertLockHeld(::cs_main);
assert(!(nUpTo & ~BLOCK_VALID_MASK)); // Only validity flags allowed.
if (nStatus & BLOCK_FAILED_MASK) return false;
if (nStatus & BLOCK_FAILED_VALID) return false;
if ((nStatus & BLOCK_VALID_MASK) < nUpTo) {
nStatus = (nStatus & ~BLOCK_VALID_MASK) | nUpTo;

View File

@ -487,10 +487,18 @@ bool BlockManager::LoadBlockIndex(const std::optional<uint256>& snapshot_blockha
pindex->m_chain_tx_count = pindex->nTx;
}
}
if (!(pindex->nStatus & BLOCK_FAILED_MASK) && pindex->pprev && (pindex->pprev->nStatus & BLOCK_FAILED_MASK)) {
pindex->nStatus |= BLOCK_FAILED_CHILD;
if (pindex->nStatus & BLOCK_FAILED_CHILD) {
// BLOCK_FAILED_CHILD is deprecated, but may still exist on disk. Replace it with BLOCK_FAILED_VALID.
pindex->nStatus = (pindex->nStatus & ~BLOCK_FAILED_CHILD) | BLOCK_FAILED_VALID;
m_dirty_blockindex.insert(pindex);
}
if (!(pindex->nStatus & BLOCK_FAILED_VALID) && pindex->pprev && (pindex->pprev->nStatus & BLOCK_FAILED_VALID)) {
// All descendants of invalid blocks are invalid too.
pindex->nStatus |= BLOCK_FAILED_VALID;
m_dirty_blockindex.insert(pindex);
}
if (pindex->pprev) {
pindex->BuildSkip();
}

View File

@ -1595,7 +1595,7 @@ static RPCHelpMan getchaintips()
if (active_chain.Contains(block)) {
// This block is part of the currently active chain.
status = "active";
} else if (block->nStatus & BLOCK_FAILED_MASK) {
} else if (block->nStatus & BLOCK_FAILED_VALID) {
// This block or one of its ancestors is invalid.
status = "invalid";
} else if (!block->HaveNumChainTxs()) {

View File

@ -742,7 +742,7 @@ static RPCHelpMan getblocktemplate()
if (pindex) {
if (pindex->IsValid(BLOCK_VALID_SCRIPTS))
return "duplicate";
if (pindex->nStatus & BLOCK_FAILED_MASK)
if (pindex->nStatus & BLOCK_FAILED_VALID)
return "duplicate-invalid";
return "duplicate-inconclusive";
}

View File

@ -130,28 +130,21 @@ BOOST_FIXTURE_TEST_CASE(invalidate_block, TestChain100Setup)
// tip_to_invalidate just got invalidated, so it's BLOCK_FAILED_VALID
WITH_LOCK(::cs_main, assert(tip_to_invalidate->nStatus & BLOCK_FAILED_VALID));
WITH_LOCK(::cs_main, assert((tip_to_invalidate->nStatus & BLOCK_FAILED_CHILD) == 0));
// check all ancestors of the invalidated block are validated up to BLOCK_VALID_TRANSACTIONS and are not invalid
auto pindex = tip_to_invalidate->pprev;
while (pindex) {
WITH_LOCK(::cs_main, assert(pindex->IsValid(BLOCK_VALID_TRANSACTIONS)));
WITH_LOCK(::cs_main, assert((pindex->nStatus & BLOCK_FAILED_MASK) == 0));
WITH_LOCK(::cs_main, assert((pindex->nStatus & BLOCK_FAILED_VALID) == 0));
pindex = pindex->pprev;
}
// check all descendants of the invalidated block are BLOCK_FAILED_CHILD
// check all descendants of the invalidated block are BLOCK_FAILED_VALID
pindex = orig_tip;
while (pindex && pindex != tip_to_invalidate) {
WITH_LOCK(::cs_main, assert((pindex->nStatus & BLOCK_FAILED_VALID) == 0));
WITH_LOCK(::cs_main, assert(pindex->nStatus & BLOCK_FAILED_CHILD));
WITH_LOCK(::cs_main, assert(pindex->nStatus & BLOCK_FAILED_VALID));
pindex = pindex->pprev;
}
// don't mark already invalidated block (orig_tip is BLOCK_FAILED_CHILD) with BLOCK_FAILED_VALID again
m_node.chainman->ActiveChainstate().InvalidateBlock(state, orig_tip);
WITH_LOCK(::cs_main, assert(orig_tip->nStatus & BLOCK_FAILED_CHILD));
WITH_LOCK(::cs_main, assert((orig_tip->nStatus & BLOCK_FAILED_VALID) == 0));
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -61,7 +61,7 @@ FUZZ_TARGET(block_index_tree, .init = initialize_block_index_tree)
// Receive a header building on an existing valid one. This assumes headers are valid, so PoW is not relevant here.
LOCK(cs_main);
CBlockIndex* prev_block = PickValue(fuzzed_data_provider, blocks);
if (!(prev_block->nStatus & BLOCK_FAILED_MASK)) {
if (!(prev_block->nStatus & BLOCK_FAILED_VALID)) {
CBlockHeader header = ConsumeBlockHeader(fuzzed_data_provider, prev_block->GetBlockHash(), nonce_counter);
CBlockIndex* index = blockman.AddToBlockIndex(header, chainman.m_best_header);
assert(index->nStatus & BLOCK_VALID_TREE);
@ -74,7 +74,7 @@ FUZZ_TARGET(block_index_tree, .init = initialize_block_index_tree)
LOCK(cs_main);
CBlockIndex* index = PickValue(fuzzed_data_provider, blocks);
// Must be new to us and not known to be invalid (e.g. because of an invalid ancestor).
if (index->nTx == 0 && !(index->nStatus & BLOCK_FAILED_MASK)) {
if (index->nTx == 0 && !(index->nStatus & BLOCK_FAILED_VALID)) {
if (fuzzed_data_provider.ConsumeBool()) { // Invalid
BlockValidationState state;
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "consensus-invalid");
@ -124,7 +124,7 @@ FUZZ_TARGET(block_index_tree, .init = initialize_block_index_tree)
}
// Connect blocks, possibly fail
for (CBlockIndex* block : to_connect | std::views::reverse) {
assert(!(block->nStatus & BLOCK_FAILED_MASK));
assert(!(block->nStatus & BLOCK_FAILED_VALID));
assert(block->nStatus & BLOCK_HAVE_DATA);
if (!block->IsValid(BLOCK_VALID_SCRIPTS)) {
if (fuzzed_data_provider.ConsumeBool()) { // Invalid

View File

@ -50,8 +50,6 @@ FUZZ_TARGET(chain)
BlockStatus::BLOCK_HAVE_UNDO,
BlockStatus::BLOCK_HAVE_MASK,
BlockStatus::BLOCK_FAILED_VALID,
BlockStatus::BLOCK_FAILED_CHILD,
BlockStatus::BLOCK_FAILED_MASK,
BlockStatus::BLOCK_OPT_WITNESS,
});
if (block_status & ~BLOCK_VALID_MASK) {

View File

@ -591,6 +591,31 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_loadblockindex, TestChain100Setup)
BOOST_CHECK_EQUAL(cs2.setBlockIndexCandidates.size(), num_indexes - last_assumed_valid_idx + 1);
}
BOOST_FIXTURE_TEST_CASE(loadblockindex_invalid_descendants, TestChain100Setup)
{
LOCK(Assert(m_node.chainman)->GetMutex());
// consider the chain of blocks grand_parent <- parent <- child
// intentionally mark:
// - grand_parent: BLOCK_FAILED_VALID
// - parent: BLOCK_FAILED_CHILD
// - child: not invalid
// Test that when the block index is loaded, all blocks are marked as BLOCK_FAILED_VALID
auto* child{m_node.chainman->ActiveChain().Tip()};
auto* parent{child->pprev};
auto* grand_parent{parent->pprev};
grand_parent->nStatus = (grand_parent->nStatus | BLOCK_FAILED_VALID);
parent->nStatus = (parent->nStatus & ~BLOCK_FAILED_VALID) | BLOCK_FAILED_CHILD;
child->nStatus = (child->nStatus & ~BLOCK_FAILED_VALID);
// Reload block index to recompute block status validity flags.
m_node.chainman->LoadBlockIndex();
// check grand_parent, parent, child is marked as BLOCK_FAILED_VALID after reloading the block index
BOOST_CHECK(grand_parent->nStatus & BLOCK_FAILED_VALID);
BOOST_CHECK(parent->nStatus & BLOCK_FAILED_VALID);
BOOST_CHECK(child->nStatus & BLOCK_FAILED_VALID);
}
//! 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)

View File

@ -3173,7 +3173,7 @@ CBlockIndex* Chainstate::FindMostWorkChain()
// which block files have been deleted. Remove those as candidates
// for the most work chain if we come across them; we can't switch
// to a chain unless we have all the non-active-chain parent blocks.
bool fFailedChain = pindexTest->nStatus & BLOCK_FAILED_MASK;
bool fFailedChain = pindexTest->nStatus & BLOCK_FAILED_VALID;
bool fMissingData = !(pindexTest->nStatus & BLOCK_HAVE_DATA);
if (fFailedChain || fMissingData) {
// Candidate chain is not usable (either invalid or missing data)
@ -3184,7 +3184,7 @@ CBlockIndex* Chainstate::FindMostWorkChain()
// Remove the entire chain from the set.
while (pindexTest != pindexFailed) {
if (fFailedChain) {
pindexFailed->nStatus |= BLOCK_FAILED_CHILD;
pindexFailed->nStatus |= BLOCK_FAILED_VALID;
m_blockman.m_dirty_blockindex.insert(pindexFailed);
} else if (fMissingData) {
// If we're missing data, then add back to m_blocks_unlinked,
@ -3565,10 +3565,6 @@ bool Chainstate::InvalidateBlock(BlockValidationState& state, CBlockIndex* pinde
assert(pindex);
if (pindex->nHeight == 0) return false;
CBlockIndex* to_mark_failed = pindex;
bool pindex_was_in_chain = false;
int disconnected = 0;
// We do not allow ActivateBestChain() to run while InvalidateBlock() is
// running, as that could cause the tip to change while we disconnect
// blocks.
@ -3594,12 +3590,16 @@ bool Chainstate::InvalidateBlock(BlockValidationState& state, CBlockIndex* pinde
// at least as good with CBlockIndexWorkComparator as the new tip.
if (!m_chain.Contains(candidate) &&
!CBlockIndexWorkComparator()(candidate, pindex->pprev) &&
!(candidate->nStatus & BLOCK_FAILED_MASK)) {
!(candidate->nStatus & BLOCK_FAILED_VALID)) {
highpow_outofchain_headers.insert({candidate->nChainWork, candidate});
}
}
}
CBlockIndex* to_mark_failed = pindex;
bool pindex_was_in_chain = false;
int disconnected = 0;
// Disconnect (descendants of) pindex, and mark them invalid.
while (true) {
if (m_chainman.m_interrupt) break;
@ -3613,7 +3613,7 @@ bool Chainstate::InvalidateBlock(BlockValidationState& state, CBlockIndex* pinde
LOCK(MempoolMutex());
if (!m_chain.Contains(pindex)) break;
pindex_was_in_chain = true;
CBlockIndex *invalid_walk_tip = m_chain.Tip();
CBlockIndex* disconnected_tip{m_chain.Tip()};
// ActivateBestChain considers blocks already in m_chain
// unconditionally valid already, so force disconnect away from it.
@ -3626,49 +3626,42 @@ bool Chainstate::InvalidateBlock(BlockValidationState& state, CBlockIndex* pinde
// keeping the mempool up to date is probably futile anyway).
MaybeUpdateMempoolForReorg(disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret);
if (!ret) return false;
assert(invalid_walk_tip->pprev == m_chain.Tip());
CBlockIndex* new_tip{m_chain.Tip()};
assert(disconnected_tip->pprev == new_tip);
// We immediately mark the disconnected blocks as invalid.
// This prevents a case where pruned nodes may fail to invalidateblock
// and be left unable to start as they have no tip candidates (as there
// are no blocks that meet the "have data and are not invalid per
// nStatus" criteria for inclusion in setBlockIndexCandidates).
invalid_walk_tip->nStatus |= BLOCK_FAILED_VALID;
m_blockman.m_dirty_blockindex.insert(invalid_walk_tip);
setBlockIndexCandidates.erase(invalid_walk_tip);
setBlockIndexCandidates.insert(invalid_walk_tip->pprev);
if (invalid_walk_tip == to_mark_failed->pprev && (to_mark_failed->nStatus & BLOCK_FAILED_VALID)) {
// We only want to mark the last disconnected block as BLOCK_FAILED_VALID; its children
// need to be BLOCK_FAILED_CHILD instead.
to_mark_failed->nStatus = (to_mark_failed->nStatus ^ BLOCK_FAILED_VALID) | BLOCK_FAILED_CHILD;
m_blockman.m_dirty_blockindex.insert(to_mark_failed);
}
disconnected_tip->nStatus |= BLOCK_FAILED_VALID;
m_blockman.m_dirty_blockindex.insert(disconnected_tip);
setBlockIndexCandidates.erase(disconnected_tip);
setBlockIndexCandidates.insert(new_tip);
// Mark out-of-chain descendants of the invalidated block as invalid
// (possibly replacing a pre-existing BLOCK_FAILED_VALID with BLOCK_FAILED_CHILD)
// Add any equal or more work headers that are not invalidated to setBlockIndexCandidates
// Recalculate m_best_header if it became invalid.
auto candidate_it = highpow_outofchain_headers.lower_bound(invalid_walk_tip->pprev->nChainWork);
auto candidate_it = highpow_outofchain_headers.lower_bound(new_tip->nChainWork);
const bool best_header_needs_update{m_chainman.m_best_header->GetAncestor(invalid_walk_tip->nHeight) == invalid_walk_tip};
const bool best_header_needs_update{m_chainman.m_best_header->GetAncestor(disconnected_tip->nHeight) == disconnected_tip};
if (best_header_needs_update) {
// pprev is definitely still valid at this point, but there may be better ones
m_chainman.m_best_header = invalid_walk_tip->pprev;
// new_tip is definitely still valid at this point, but there may be better ones
m_chainman.m_best_header = new_tip;
}
while (candidate_it != highpow_outofchain_headers.end()) {
CBlockIndex* candidate{candidate_it->second};
if (candidate->GetAncestor(invalid_walk_tip->nHeight) == invalid_walk_tip) {
// Children of failed blocks should be marked as BLOCK_FAILED_CHILD instead.
candidate->nStatus &= ~BLOCK_FAILED_VALID;
candidate->nStatus |= BLOCK_FAILED_CHILD;
if (candidate->GetAncestor(disconnected_tip->nHeight) == disconnected_tip) {
// Children of failed blocks are marked as BLOCK_FAILED_VALID.
candidate->nStatus |= BLOCK_FAILED_VALID;
m_blockman.m_dirty_blockindex.insert(candidate);
// If invalidated, the block is irrelevant for setBlockIndexCandidates
// and for m_best_header and can be removed from the cache.
candidate_it = highpow_outofchain_headers.erase(candidate_it);
continue;
}
if (!CBlockIndexWorkComparator()(candidate, invalid_walk_tip->pprev) &&
if (!CBlockIndexWorkComparator()(candidate, new_tip) &&
candidate->IsValid(BLOCK_VALID_TRANSACTIONS) &&
candidate->HaveNumChainTxs()) {
setBlockIndexCandidates.insert(candidate);
@ -3682,9 +3675,8 @@ bool Chainstate::InvalidateBlock(BlockValidationState& state, CBlockIndex* pinde
++candidate_it;
}
// Track the last disconnected block, so we can correct its BLOCK_FAILED_CHILD status in future
// iterations, or, if it's the last one, call InvalidChainFound on it.
to_mark_failed = invalid_walk_tip;
// Track the last disconnected block to call InvalidChainFound on it.
to_mark_failed = disconnected_tip;
}
m_chainman.CheckBlockIndex();
@ -3697,7 +3689,7 @@ bool Chainstate::InvalidateBlock(BlockValidationState& state, CBlockIndex* pinde
}
// Mark pindex as invalid if it never was in the main chain
if (!pindex_was_in_chain && !(pindex->nStatus & BLOCK_FAILED_MASK)) {
if (!pindex_was_in_chain && !(pindex->nStatus & BLOCK_FAILED_VALID)) {
pindex->nStatus |= BLOCK_FAILED_VALID;
m_blockman.m_dirty_blockindex.insert(pindex);
setBlockIndexCandidates.erase(pindex);
@ -3748,7 +3740,7 @@ void Chainstate::SetBlockFailureFlags(CBlockIndex* invalid_block)
for (auto& [_, block_index] : m_blockman.m_block_index) {
if (invalid_block != &block_index && block_index.GetAncestor(invalid_block->nHeight) == invalid_block) {
block_index.nStatus = (block_index.nStatus & ~BLOCK_FAILED_VALID) | BLOCK_FAILED_CHILD;
block_index.nStatus |= BLOCK_FAILED_VALID;
m_blockman.m_dirty_blockindex.insert(&block_index);
}
}
@ -3761,8 +3753,8 @@ void Chainstate::ResetBlockFailureFlags(CBlockIndex *pindex) {
// Remove the invalidity flag from this block and all its descendants and ancestors.
for (auto& [_, block_index] : m_blockman.m_block_index) {
if ((block_index.nStatus & BLOCK_FAILED_MASK) && (block_index.GetAncestor(nHeight) == pindex || pindex->GetAncestor(block_index.nHeight) == &block_index)) {
block_index.nStatus &= ~BLOCK_FAILED_MASK;
if ((block_index.nStatus & BLOCK_FAILED_VALID) && (block_index.GetAncestor(nHeight) == pindex || pindex->GetAncestor(block_index.nHeight) == &block_index)) {
block_index.nStatus &= ~BLOCK_FAILED_VALID;
m_blockman.m_dirty_blockindex.insert(&block_index);
if (block_index.IsValid(BLOCK_VALID_TRANSACTIONS) && block_index.HaveNumChainTxs() && setBlockIndexCandidates.value_comp()(m_chain.Tip(), &block_index)) {
setBlockIndexCandidates.insert(&block_index);
@ -4245,7 +4237,7 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
CBlockIndex* pindex = &(miSelf->second);
if (ppindex)
*ppindex = pindex;
if (pindex->nStatus & BLOCK_FAILED_MASK) {
if (pindex->nStatus & BLOCK_FAILED_VALID) {
LogDebug(BCLog::VALIDATION, "%s: block %s is marked invalid\n", __func__, hash.ToString());
return state.Invalid(BlockValidationResult::BLOCK_CACHED_INVALID, "duplicate-invalid",
strprintf("block %s was previously marked invalid", hash.ToString()));
@ -4266,7 +4258,7 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
return state.Invalid(BlockValidationResult::BLOCK_MISSING_PREV, "prev-blk-not-found");
}
pindexPrev = &((*mi).second);
if (pindexPrev->nStatus & BLOCK_FAILED_MASK) {
if (pindexPrev->nStatus & BLOCK_FAILED_VALID) {
LogDebug(BCLog::VALIDATION, "header %s has prev block invalid: %s\n", hash.ToString(), block.hashPrevBlock.ToString());
return state.Invalid(BlockValidationResult::BLOCK_INVALID_PREV, "bad-prevblk");
}
@ -4960,7 +4952,7 @@ bool ChainstateManager::LoadBlockIndex()
chainstate->TryAddBlockIndexCandidate(pindex);
}
}
if (pindex->nStatus & BLOCK_FAILED_MASK && (!m_best_invalid || pindex->nChainWork > m_best_invalid->nChainWork)) {
if (pindex->nStatus & BLOCK_FAILED_VALID && (!m_best_invalid || pindex->nChainWork > m_best_invalid->nChainWork)) {
m_best_invalid = pindex;
}
if (pindex->IsValid(BLOCK_VALID_TREE) && (m_best_header == nullptr || CBlockIndexWorkComparator()(m_best_header, pindex)))
@ -5200,7 +5192,7 @@ void ChainstateManager::CheckBlockIndex() const
// are not yet validated.
CChain best_hdr_chain;
assert(m_best_header);
assert(!(m_best_header->nStatus & BLOCK_FAILED_MASK));
assert(!(m_best_header->nStatus & BLOCK_FAILED_VALID));
best_hdr_chain.SetTip(*m_best_header);
std::multimap<const CBlockIndex*, const CBlockIndex*> forward;
@ -5315,9 +5307,9 @@ void ChainstateManager::CheckBlockIndex() const
if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_SCRIPTS) assert(pindexFirstNotScriptsValid == nullptr); // SCRIPTS valid implies all parents are SCRIPTS valid
if (pindexFirstInvalid == nullptr) {
// Checks for not-invalid blocks.
assert((pindex->nStatus & BLOCK_FAILED_MASK) == 0); // The failed mask cannot be set for blocks without invalid parents.
assert((pindex->nStatus & BLOCK_FAILED_VALID) == 0); // The failed flag cannot be set for blocks without invalid parents.
} else {
assert(pindex->nStatus & BLOCK_FAILED_MASK); // Invalid blocks and their descendants must be marked as invalid
assert(pindex->nStatus & BLOCK_FAILED_VALID); // Invalid blocks and their descendants must be marked as invalid
}
// Make sure m_chain_tx_count sum is correctly computed.
if (!pindex->pprev) {
@ -5332,7 +5324,7 @@ void ChainstateManager::CheckBlockIndex() const
assert((pindex->m_chain_tx_count != 0) == (pindex == snap_base));
}
// There should be no block with more work than m_best_header, unless it's known to be invalid
assert((pindex->nStatus & BLOCK_FAILED_MASK) || pindex->nChainWork <= m_best_header->nChainWork);
assert((pindex->nStatus & BLOCK_FAILED_VALID) || pindex->nChainWork <= m_best_header->nChainWork);
// Chainstate-specific checks on setBlockIndexCandidates
for (const auto& c : m_chainstates) {
@ -5648,7 +5640,7 @@ util::Result<CBlockIndex*> ChainstateManager::ActivateSnapshot(
base_blockhash.ToString()))};
}
bool start_block_invalid = snapshot_start_block->nStatus & BLOCK_FAILED_MASK;
bool start_block_invalid = snapshot_start_block->nStatus & BLOCK_FAILED_VALID;
if (start_block_invalid) {
return util::Error{Untranslated(strprintf("The base block header (%s) is part of an invalid chain", base_blockhash.ToString()))};
}
@ -6293,7 +6285,7 @@ void ChainstateManager::RecalculateBestHeader()
AssertLockHeld(cs_main);
m_best_header = ActiveChain().Tip();
for (auto& entry : m_blockman.m_block_index) {
if (!(entry.second.nStatus & BLOCK_FAILED_MASK) && m_best_header->nChainWork < entry.second.nChainWork) {
if (!(entry.second.nStatus & BLOCK_FAILED_VALID) && m_best_header->nChainWork < entry.second.nChainWork) {
m_best_header = &entry.second;
}
}