Skip to content

Commit 768690b

Browse files
committed
Fix initialization of setBlockIndexCandidates when working with multiple chainstates
When using assumeutxo and multiple chainstates are active, the background chainstate should consider all HAVE_DATA blocks that are ancestors of the snapshotted block and that have more work than the tip as potential candidates.
1 parent d43a1f1 commit 768690b

File tree

2 files changed

+30
-64
lines changed

2 files changed

+30
-64
lines changed

src/test/validation_chainstatemanager_tests.cpp

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_activate_snapshot, SnapshotTestSetup)
412412
//! - Then mark a region of the chain BLOCK_ASSUMED_VALID and introduce a second chainstate
413413
//! that will tolerate assumed-valid blocks. Run LoadBlockIndex() and ensure that the first
414414
//! chainstate only contains fully validated blocks and the other chainstate contains all blocks,
415-
//! even those assumed-valid.
415+
//! except those marked assume-valid, because those entries don't HAVE_DATA.
416416
//!
417417
BOOST_FIXTURE_TEST_CASE(chainstatemanager_loadblockindex, TestChain100Setup)
418418
{
@@ -444,16 +444,17 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_loadblockindex, TestChain100Setup)
444444
WITH_LOCK(::cs_main, chainman.LoadBlockIndex());
445445
};
446446

447-
// Ensure that without any assumed-valid BlockIndex entries, all entries are considered
448-
// tip candidates.
447+
// Ensure that without any assumed-valid BlockIndex entries, only the current tip is
448+
// considered as a candidate.
449449
reload_all_block_indexes();
450-
BOOST_CHECK_EQUAL(cs1.setBlockIndexCandidates.size(), cs1.m_chain.Height() + 1);
450+
BOOST_CHECK_EQUAL(cs1.setBlockIndexCandidates.size(), 1);
451451

452-
// Mark some region of the chain assumed-valid.
452+
// Mark some region of the chain assumed-valid, and remove the HAVE_DATA flag.
453453
for (int i = 0; i <= cs1.m_chain.Height(); ++i) {
454454
LOCK(::cs_main);
455455
auto index = cs1.m_chain[i];
456456

457+
// Blocks with heights in range [20, 40) are marked ASSUMED_VALID
457458
if (i < last_assumed_valid_idx && i >= assumed_valid_start_idx) {
458459
index->nStatus = BlockStatus::BLOCK_VALID_TREE | BlockStatus::BLOCK_ASSUMED_VALID;
459460
}
@@ -466,33 +467,41 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_loadblockindex, TestChain100Setup)
466467
validated_tip = index;
467468
BOOST_CHECK(!index->IsAssumedValid());
468469
}
469-
// Note the block after the last assumed valid block as the snapshot base
470-
if (i == last_assumed_valid_idx) {
470+
// Note the last assumed valid block as the snapshot base
471+
if (i == last_assumed_valid_idx - 1) {
471472
assumed_base = index;
473+
BOOST_CHECK(index->IsAssumedValid());
474+
} else if (i == last_assumed_valid_idx) {
472475
BOOST_CHECK(!index->IsAssumedValid());
473476
}
474477
}
475478

476479
BOOST_CHECK_EQUAL(expected_assumed_valid, num_assumed_valid);
477480

481+
// Note: cs2's tip is not set when ActivateExistingSnapshot is called.
478482
Chainstate& cs2 = WITH_LOCK(::cs_main,
479483
return chainman.ActivateExistingSnapshot(&mempool, *assumed_base->phashBlock));
480484

481485
// Set tip of the fully validated chain to be the validated tip
482486
cs1.m_chain.SetTip(*validated_tip);
483487

488+
// Set tip of the assume-valid-based chain to the assume-valid block
489+
cs2.m_chain.SetTip(*assumed_base);
490+
484491
reload_all_block_indexes();
485492

486-
// The fully validated chain only has candidates up to the start of the assumed-valid
487-
// blocks.
493+
// The fully validated chain should have the current validated tip
494+
// and the assumed valid base as candidates.
495+
BOOST_CHECK_EQUAL(cs1.setBlockIndexCandidates.size(), 2);
488496
BOOST_CHECK_EQUAL(cs1.setBlockIndexCandidates.count(validated_tip), 1);
489-
BOOST_CHECK_EQUAL(cs1.setBlockIndexCandidates.count(assumed_tip), 0);
490-
BOOST_CHECK_EQUAL(cs1.setBlockIndexCandidates.size(), assumed_valid_start_idx);
497+
BOOST_CHECK_EQUAL(cs1.setBlockIndexCandidates.count(assumed_base), 1);
491498

492-
// The assumed-valid tolerant chain has all blocks as candidates.
493-
BOOST_CHECK_EQUAL(cs2.setBlockIndexCandidates.count(validated_tip), 1);
499+
// The assumed-valid tolerant chain has the assumed valid base as a
500+
// candidate, but otherwise has none of the assumed-valid (which do not
501+
// HAVE_DATA) blocks as candidates.
502+
BOOST_CHECK_EQUAL(cs2.setBlockIndexCandidates.count(validated_tip), 0);
494503
BOOST_CHECK_EQUAL(cs2.setBlockIndexCandidates.count(assumed_tip), 1);
495-
BOOST_CHECK_EQUAL(cs2.setBlockIndexCandidates.size(), num_indexes);
504+
BOOST_CHECK_EQUAL(cs2.setBlockIndexCandidates.size(), num_indexes - last_assumed_valid_idx + 1);
496505
}
497506

498507
//! Ensure that snapshot chainstates initialize properly when found on disk.

src/validation.cpp

Lines changed: 7 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -4432,62 +4432,19 @@ bool ChainstateManager::LoadBlockIndex()
44324432
std::sort(vSortedByHeight.begin(), vSortedByHeight.end(),
44334433
CBlockIndexHeightOnlyComparator());
44344434

4435-
// Find start of assumed-valid region.
4436-
int first_assumed_valid_height = std::numeric_limits<int>::max();
4437-
4438-
for (const CBlockIndex* block : vSortedByHeight) {
4439-
if (block->IsAssumedValid()) {
4440-
auto chainstates = GetAll();
4441-
4442-
// If we encounter an assumed-valid block index entry, ensure that we have
4443-
// one chainstate that tolerates assumed-valid entries and another that does
4444-
// not (i.e. the background validation chainstate), since assumed-valid
4445-
// entries should always be pending validation by a fully-validated chainstate.
4446-
auto any_chain = [&](auto fnc) { return std::any_of(chainstates.cbegin(), chainstates.cend(), fnc); };
4447-
assert(any_chain([](auto chainstate) { return chainstate->reliesOnAssumedValid(); }));
4448-
assert(any_chain([](auto chainstate) { return !chainstate->reliesOnAssumedValid(); }));
4449-
4450-
first_assumed_valid_height = block->nHeight;
4451-
LogPrintf("Saw first assumedvalid block at height %d (%s)\n",
4452-
first_assumed_valid_height, block->ToString());
4453-
break;
4454-
}
4455-
}
4456-
44574435
for (CBlockIndex* pindex : vSortedByHeight) {
44584436
if (m_interrupt) return false;
4459-
if (pindex->IsAssumedValid() ||
4437+
// If we have an assumeutxo-based chainstate, then the snapshot
4438+
// block will be a candidate for the tip, but it may not be
4439+
// VALID_TRANSACTIONS (eg if we haven't yet downloaded the block),
4440+
// so we special-case the snapshot block as a potential candidate
4441+
// here.
4442+
if (pindex == GetSnapshotBaseBlock() ||
44604443
(pindex->IsValid(BLOCK_VALID_TRANSACTIONS) &&
44614444
(pindex->HaveTxsDownloaded() || pindex->pprev == nullptr))) {
44624445

4463-
// Fill each chainstate's block candidate set. Only add assumed-valid
4464-
// blocks to the tip candidate set if the chainstate is allowed to rely on
4465-
// assumed-valid blocks.
4466-
//
4467-
// If all setBlockIndexCandidates contained the assumed-valid blocks, the
4468-
// background chainstate's ActivateBestChain() call would add assumed-valid
4469-
// blocks to the chain (based on how FindMostWorkChain() works). Obviously
4470-
// we don't want this since the purpose of the background validation chain
4471-
// is to validate assued-valid blocks.
4472-
//
4473-
// Note: This is considering all blocks whose height is greater or equal to
4474-
// the first assumed-valid block to be assumed-valid blocks, and excluding
4475-
// them from the background chainstate's setBlockIndexCandidates set. This
4476-
// does mean that some blocks which are not technically assumed-valid
4477-
// (later blocks on a fork beginning before the first assumed-valid block)
4478-
// might not get added to the background chainstate, but this is ok,
4479-
// because they will still be attached to the active chainstate if they
4480-
// actually contain more work.
4481-
//
4482-
// Instead of this height-based approach, an earlier attempt was made at
4483-
// detecting "holistically" whether the block index under consideration
4484-
// relied on an assumed-valid ancestor, but this proved to be too slow to
4485-
// be practical.
44864446
for (Chainstate* chainstate : GetAll()) {
4487-
if (chainstate->reliesOnAssumedValid() ||
4488-
pindex->nHeight < first_assumed_valid_height) {
4489-
chainstate->setBlockIndexCandidates.insert(pindex);
4490-
}
4447+
chainstate->TryAddBlockIndexCandidate(pindex);
44914448
}
44924449
}
44934450
if (pindex->nStatus & BLOCK_FAILED_MASK && (!m_best_invalid || pindex->nChainWork > m_best_invalid->nChainWork)) {

0 commit comments

Comments
 (0)