mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-03-02 09:46:14 +00:00
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:
commit
655b9d12ee
@ -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;
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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()) {
|
||||
|
||||
@ -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";
|
||||
}
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user