Skip to content

Commit 0fd599a

Browse files
jamesobryanofsky
andcommitted
validation: have LoadBlockIndex account for snapshot use
Ensure that blocks past the snapshot base block (i.e. the end of the assumed-valid region of the chain) are not included in setBlockIndexCandidates for the background validation chainstate. These blocks, while fully validated and lacking the BLOCK_ASSUMED_VALID flag, *rely* on blocks which are assumed-valid, and so shouldn't be added to the IBD chainstate. Co-authored-by: Russ Yanofsky <[email protected]>
1 parent d0c6e61 commit 0fd599a

File tree

2 files changed

+67
-15
lines changed

2 files changed

+67
-15
lines changed

src/validation.cpp

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
#include <validationinterface.h>
5656
#include <warnings.h>
5757

58+
#include <algorithm>
5859
#include <numeric>
5960
#include <optional>
6061
#include <string>
@@ -3607,7 +3608,7 @@ CBlockIndex * BlockManager::InsertBlockIndex(const uint256& hash)
36073608

36083609
bool BlockManager::LoadBlockIndex(
36093610
const Consensus::Params& consensus_params,
3610-
std::set<CBlockIndex*, CBlockIndexWorkComparator>& block_index_candidates)
3611+
ChainstateManager& chainman)
36113612
{
36123613
if (!m_block_tree_db->LoadBlockIndexGuts(consensus_params, [this](const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return this->InsertBlockIndex(hash); })) {
36133614
return false;
@@ -3622,17 +3623,41 @@ bool BlockManager::LoadBlockIndex(
36223623
vSortedByHeight.push_back(std::make_pair(pindex->nHeight, pindex));
36233624
}
36243625
sort(vSortedByHeight.begin(), vSortedByHeight.end());
3626+
3627+
// Find start of assumed-valid region.
3628+
int first_assumed_valid_height = std::numeric_limits<int>::max();
3629+
3630+
for (const auto& [height, block] : vSortedByHeight) {
3631+
if (block->IsAssumedValid()) {
3632+
auto chainstates = chainman.GetAll();
3633+
3634+
// If we encounter an assumed-valid block index entry, ensure that we have
3635+
// one chainstate that tolerates assumed-valid entries and another that does
3636+
// not (i.e. the background validation chainstate), since assumed-valid
3637+
// entries should always be pending validation by a fully-validated chainstate.
3638+
auto any_chain = [&](auto fnc) { return std::any_of(chainstates.cbegin(), chainstates.cend(), fnc); };
3639+
assert(any_chain([](auto chainstate) { return chainstate->reliesOnAssumedValid(); }));
3640+
assert(any_chain([](auto chainstate) { return !chainstate->reliesOnAssumedValid(); }));
3641+
3642+
first_assumed_valid_height = height;
3643+
break;
3644+
}
3645+
}
3646+
36253647
for (const std::pair<int, CBlockIndex*>& item : vSortedByHeight)
36263648
{
36273649
if (ShutdownRequested()) return false;
36283650
CBlockIndex* pindex = item.second;
36293651
pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex);
36303652
pindex->nTimeMax = (pindex->pprev ? std::max(pindex->pprev->nTimeMax, pindex->nTime) : pindex->nTime);
3631-
// We can link the chain of blocks for which we've received transactions at some point.
3653+
3654+
// We can link the chain of blocks for which we've received transactions at some point, or
3655+
// blocks that are assumed-valid on the basis of snapshot load (see
3656+
// PopulateAndValidateSnapshot()).
36323657
// Pruned nodes may have deleted the block.
36333658
if (pindex->nTx > 0) {
36343659
if (pindex->pprev) {
3635-
if (pindex->pprev->HaveTxsDownloaded()) {
3660+
if (pindex->pprev->nChainTx > 0) {
36363661
pindex->nChainTx = pindex->pprev->nChainTx + pindex->nTx;
36373662
} else {
36383663
pindex->nChainTx = 0;
@@ -3649,7 +3674,36 @@ bool BlockManager::LoadBlockIndex(
36493674
if (pindex->IsAssumedValid() ||
36503675
(pindex->IsValid(BLOCK_VALID_TRANSACTIONS) &&
36513676
(pindex->HaveTxsDownloaded() || pindex->pprev == nullptr))) {
3652-
block_index_candidates.insert(pindex);
3677+
3678+
// Fill each chainstate's block candidate set. Only add assumed-valid
3679+
// blocks to the tip candidate set if the chainstate is allowed to rely on
3680+
// assumed-valid blocks.
3681+
//
3682+
// If all setBlockIndexCandidates contained the assumed-valid blocks, the
3683+
// background chainstate's ActivateBestChain() call would add assumed-valid
3684+
// blocks to the chain (based on how FindMostWorkChain() works). Obviously
3685+
// we don't want this since the purpose of the background validation chain
3686+
// is to validate assued-valid blocks.
3687+
//
3688+
// Note: This is considering all blocks whose height is greater or equal to
3689+
// the first assumed-valid block to be assumed-valid blocks, and excluding
3690+
// them from the background chainstate's setBlockIndexCandidates set. This
3691+
// does mean that some blocks which are not technically assumed-valid
3692+
// (later blocks on a fork beginning before the first assumed-valid block)
3693+
// might not get added to the the background chainstate, but this is ok,
3694+
// because they will still be attached to the active chainstate if they
3695+
// actually contain more work.
3696+
//
3697+
// Instad of this height-based approach, an earlier attempt was made at
3698+
// detecting "holistically" whether the block index under consideration
3699+
// relied on an assumed-valid ancestor, but this proved to be too slow to
3700+
// be practical.
3701+
for (CChainState* chainstate : chainman.GetAll()) {
3702+
if (chainstate->reliesOnAssumedValid() ||
3703+
pindex->nHeight < first_assumed_valid_height) {
3704+
chainstate->setBlockIndexCandidates.insert(pindex);
3705+
}
3706+
}
36533707
}
36543708
if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork))
36553709
pindexBestInvalid = pindex;
@@ -3673,11 +3727,9 @@ void BlockManager::Unload() {
36733727
m_block_index.clear();
36743728
}
36753729

3676-
bool BlockManager::LoadBlockIndexDB(std::set<CBlockIndex*, CBlockIndexWorkComparator>& setBlockIndexCandidates)
3730+
bool BlockManager::LoadBlockIndexDB(ChainstateManager& chainman)
36773731
{
3678-
if (!LoadBlockIndex(
3679-
::Params().GetConsensus(),
3680-
setBlockIndexCandidates)) {
3732+
if (!LoadBlockIndex(::Params().GetConsensus(), chainman)) {
36813733
return false;
36823734
}
36833735

@@ -4023,7 +4075,7 @@ bool ChainstateManager::LoadBlockIndex()
40234075
// Load block index from databases
40244076
bool needs_init = fReindex;
40254077
if (!fReindex) {
4026-
bool ret = m_blockman.LoadBlockIndexDB(ActiveChainstate().setBlockIndexCandidates);
4078+
bool ret = m_blockman.LoadBlockIndexDB(*this);
40274079
if (!ret) return false;
40284080
needs_init = m_blockman.m_block_index.empty();
40294081
}

src/validation.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -427,20 +427,16 @@ class BlockManager
427427

428428
std::unique_ptr<CBlockTreeDB> m_block_tree_db GUARDED_BY(::cs_main);
429429

430-
bool LoadBlockIndexDB(std::set<CBlockIndex*, CBlockIndexWorkComparator>& setBlockIndexCandidates) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
430+
bool LoadBlockIndexDB(ChainstateManager& chainman) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
431431

432432
/**
433433
* Load the blocktree off disk and into memory. Populate certain metadata
434434
* per index entry (nStatus, nChainWork, nTimeMax, etc.) as well as peripheral
435435
* collections like setDirtyBlockIndex.
436-
*
437-
* @param[out] block_index_candidates Fill this set with any valid blocks for
438-
* which we've downloaded all transactions.
439436
*/
440437
bool LoadBlockIndex(
441438
const Consensus::Params& consensus_params,
442-
std::set<CBlockIndex*, CBlockIndexWorkComparator>& block_index_candidates)
443-
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
439+
ChainstateManager& chainman) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
444440

445441
/** Clear all data members. */
446442
void Unload() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -626,6 +622,10 @@ class CChainState
626622
*/
627623
const std::optional<uint256> m_from_snapshot_blockhash;
628624

625+
//! Return true if this chainstate relies on blocks that are assumed-valid. In
626+
//! practice this means it was created based on a UTXO snapshot.
627+
bool reliesOnAssumedValid() { return m_from_snapshot_blockhash.has_value(); }
628+
629629
/**
630630
* The set of all CBlockIndex entries with either BLOCK_VALID_TRANSACTIONS (for
631631
* itself and all ancestors) *or* BLOCK_ASSUMED_VALID (if using background

0 commit comments

Comments
 (0)