Skip to content

Commit d0d40ea

Browse files
committed
Move block-storage-related logic to ChainstateManager
Separate the notion of which blocks are stored on disk, and what data is in our block index, from what tip a chainstate might be able to get to. We can use chainstate-agnostic data to determine when to store a block on disk (primarily, an anti-DoS set of criteria) and let the chainstates figure out for themselves when a block is of interest for being a candidate tip. Note: some of the invariants in CheckBlockIndex are modified, but more work is needed (ie to move CheckBlockIndex to ChainstateManager, as most of what CheckBlockIndex is doing is checking the consistency of the block index, which is outside of Chainstate).
1 parent 3cfc753 commit d0d40ea

File tree

7 files changed

+118
-76
lines changed

7 files changed

+118
-76
lines changed

src/bench/load_external.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,13 @@ static void LoadExternalBlockFile(benchmark::Bench& bench)
4949
fclose(file);
5050
}
5151

52-
Chainstate& chainstate{testing_setup->m_node.chainman->ActiveChainstate()};
5352
std::multimap<uint256, FlatFilePos> blocks_with_unknown_parent;
5453
FlatFilePos pos;
5554
bench.run([&] {
5655
// "rb" is "binary, O_RDONLY", positioned to the start of the file.
5756
// The file will be closed by LoadExternalBlockFile().
5857
FILE* file{fsbridge::fopen(blkfile, "rb")};
59-
chainstate.LoadExternalBlockFile(file, &pos, &blocks_with_unknown_parent);
58+
testing_setup->m_node.chainman->LoadExternalBlockFile(file, &pos, &blocks_with_unknown_parent);
6059
});
6160
fs::remove(blkfile);
6261
}

src/node/blockstorage.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -907,7 +907,7 @@ void ImportBlocks(ChainstateManager& chainman, std::vector<fs::path> vImportFile
907907
break; // This error is logged in OpenBlockFile
908908
}
909909
LogPrintf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile);
910-
chainman.ActiveChainstate().LoadExternalBlockFile(file, &pos, &blocks_with_unknown_parent);
910+
chainman.LoadExternalBlockFile(file, &pos, &blocks_with_unknown_parent);
911911
if (chainman.m_interrupt) {
912912
LogPrintf("Interrupt requested. Exit %s\n", __func__);
913913
return;
@@ -926,7 +926,7 @@ void ImportBlocks(ChainstateManager& chainman, std::vector<fs::path> vImportFile
926926
FILE* file = fsbridge::fopen(path, "rb");
927927
if (file) {
928928
LogPrintf("Importing blocks file %s...\n", fs::PathToString(path));
929-
chainman.ActiveChainstate().LoadExternalBlockFile(file);
929+
chainman.LoadExternalBlockFile(file);
930930
if (chainman.m_interrupt) {
931931
LogPrintf("Interrupt requested. Exit %s\n", __func__);
932932
return;

src/test/coinstatsindex_tests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_unclean_shutdown, TestChain100Setup)
9898
LOCK(cs_main);
9999
BlockValidationState state;
100100
BOOST_CHECK(CheckBlock(block, state, params.GetConsensus()));
101-
BOOST_CHECK(chainstate.AcceptBlock(new_block, state, &new_block_index, true, nullptr, nullptr, true));
101+
BOOST_CHECK(m_node.chainman->AcceptBlock(new_block, state, &new_block_index, true, nullptr, nullptr, true));
102102
CCoinsViewCache view(&chainstate.CoinsTip());
103103
BOOST_CHECK(chainstate.ConnectBlock(block, state, new_block_index, view));
104104
}

src/test/fuzz/load_external_block_file.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ FUZZ_TARGET_INIT(load_external_block_file, initialize_load_external_block_file)
3535
// Corresponds to the -reindex case (track orphan blocks across files).
3636
FlatFilePos flat_file_pos;
3737
std::multimap<uint256, FlatFilePos> blocks_with_unknown_parent;
38-
g_setup->m_node.chainman->ActiveChainstate().LoadExternalBlockFile(fuzzed_block_file, &flat_file_pos, &blocks_with_unknown_parent);
38+
g_setup->m_node.chainman->LoadExternalBlockFile(fuzzed_block_file, &flat_file_pos, &blocks_with_unknown_parent);
3939
} else {
4040
// Corresponds to the -loadblock= case (orphan blocks aren't tracked across files).
41-
g_setup->m_node.chainman->ActiveChainstate().LoadExternalBlockFile(fuzzed_block_file);
41+
g_setup->m_node.chainman->LoadExternalBlockFile(fuzzed_block_file);
4242
}
4343
}

src/test/validation_chainstate_tests.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,11 @@ BOOST_FIXTURE_TEST_CASE(chainstate_update_tip, TestChain100Setup)
120120
LOCK(::cs_main);
121121
bool checked = CheckBlock(*pblock, state, chainparams.GetConsensus());
122122
BOOST_CHECK(checked);
123-
bool accepted = background_cs.AcceptBlock(
123+
bool accepted = chainman.AcceptBlock(
124124
pblock, state, &pindex, true, nullptr, &newblock, true);
125125
BOOST_CHECK(accepted);
126126
}
127+
127128
// UpdateTip is called here
128129
bool block_added = background_cs.ActivateBestChain(state, pblock);
129130

src/validation.cpp

Lines changed: 56 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3426,7 +3426,7 @@ void Chainstate::TryAddBlockIndexCandidate(CBlockIndex* pindex)
34263426
}
34273427

34283428
/** Mark a block as having its data received and checked (up to BLOCK_VALID_TRANSACTIONS). */
3429-
void Chainstate::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const FlatFilePos& pos)
3429+
void ChainstateManager::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const FlatFilePos& pos)
34303430
{
34313431
AssertLockHeld(cs_main);
34323432
pindexNew->nTx = block.vtx.size();
@@ -3435,7 +3435,7 @@ void Chainstate::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pin
34353435
pindexNew->nDataPos = pos.nPos;
34363436
pindexNew->nUndoPos = 0;
34373437
pindexNew->nStatus |= BLOCK_HAVE_DATA;
3438-
if (DeploymentActiveAt(*pindexNew, m_chainman, Consensus::DEPLOYMENT_SEGWIT)) {
3438+
if (DeploymentActiveAt(*pindexNew, *this, Consensus::DEPLOYMENT_SEGWIT)) {
34393439
pindexNew->nStatus |= BLOCK_OPT_WITNESS;
34403440
}
34413441
pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS);
@@ -3451,8 +3451,10 @@ void Chainstate::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pin
34513451
CBlockIndex *pindex = queue.front();
34523452
queue.pop_front();
34533453
pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx;
3454-
pindex->nSequenceId = m_chainman.nBlockSequenceId++;
3455-
TryAddBlockIndexCandidate(pindex);
3454+
pindex->nSequenceId = nBlockSequenceId++;
3455+
for (Chainstate *c : GetAll()) {
3456+
c->TryAddBlockIndexCandidate(pindex);
3457+
}
34563458
std::pair<std::multimap<CBlockIndex*, CBlockIndex*>::iterator, std::multimap<CBlockIndex*, CBlockIndex*>::iterator> range = m_blockman.m_blocks_unlinked.equal_range(pindex);
34573459
while (range.first != range.second) {
34583460
std::multimap<CBlockIndex*, CBlockIndex*>::iterator it = range.first;
@@ -3912,7 +3914,7 @@ void ChainstateManager::ReportHeadersPresync(const arith_uint256& work, int64_t
39123914
}
39133915

39143916
/** Store block on disk. If dbp is non-nullptr, the file is known to already reside on disk */
3915-
bool Chainstate::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockValidationState& state, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock, bool min_pow_checked)
3917+
bool ChainstateManager::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockValidationState& state, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock, bool min_pow_checked)
39163918
{
39173919
const CBlock& block = *pblock;
39183920

@@ -3922,23 +3924,24 @@ bool Chainstate::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockV
39223924
CBlockIndex *pindexDummy = nullptr;
39233925
CBlockIndex *&pindex = ppindex ? *ppindex : pindexDummy;
39243926

3925-
bool accepted_header{m_chainman.AcceptBlockHeader(block, state, &pindex, min_pow_checked)};
3926-
CheckBlockIndex();
3927+
bool accepted_header{AcceptBlockHeader(block, state, &pindex, min_pow_checked)};
3928+
ActiveChainstate().CheckBlockIndex();
39273929

39283930
if (!accepted_header)
39293931
return false;
39303932

3931-
// Try to process all requested blocks that we don't have, but only
3932-
// process an unrequested block if it's new and has enough work to
3933-
// advance our tip, and isn't too many blocks ahead.
3933+
// Check all requested blocks that we do not already have for validity and
3934+
// save them to disk. Skip processing of unrequested blocks as an anti-DoS
3935+
// measure, unless the blocks have more work than the active chain tip, and
3936+
// aren't too far ahead of it, so are likely to be attached soon.
39343937
bool fAlreadyHave = pindex->nStatus & BLOCK_HAVE_DATA;
3935-
bool fHasMoreOrSameWork = (m_chain.Tip() ? pindex->nChainWork >= m_chain.Tip()->nChainWork : true);
3938+
bool fHasMoreOrSameWork = (ActiveTip() ? pindex->nChainWork >= ActiveTip()->nChainWork : true);
39363939
// Blocks that are too out-of-order needlessly limit the effectiveness of
39373940
// pruning, because pruning will not delete block files that contain any
39383941
// blocks which are too close in height to the tip. Apply this test
39393942
// regardless of whether pruning is enabled; it should generally be safe to
39403943
// not process unrequested blocks.
3941-
bool fTooFarAhead{pindex->nHeight > m_chain.Height() + int(MIN_BLOCKS_TO_KEEP)};
3944+
bool fTooFarAhead{pindex->nHeight > ActiveHeight() + int(MIN_BLOCKS_TO_KEEP)};
39423945

39433946
// TODO: Decouple this function from the block download logic by removing fRequested
39443947
// This requires some new chain data structure to efficiently look up if a
@@ -3958,13 +3961,13 @@ bool Chainstate::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockV
39583961
// If our tip is behind, a peer could try to send us
39593962
// low-work blocks on a fake chain that we would never
39603963
// request; don't process these.
3961-
if (pindex->nChainWork < m_chainman.MinimumChainWork()) return true;
3964+
if (pindex->nChainWork < MinimumChainWork()) return true;
39623965
}
39633966

3964-
const CChainParams& params{m_chainman.GetParams()};
3967+
const CChainParams& params{GetParams()};
39653968

39663969
if (!CheckBlock(block, state, params.GetConsensus()) ||
3967-
!ContextualCheckBlock(block, state, m_chainman, pindex->pprev)) {
3970+
!ContextualCheckBlock(block, state, *this, pindex->pprev)) {
39683971
if (state.IsInvalid() && state.GetResult() != BlockValidationResult::BLOCK_MUTATED) {
39693972
pindex->nStatus |= BLOCK_FAILED_VALID;
39703973
m_blockman.m_dirty_blockindex.insert(pindex);
@@ -3974,7 +3977,7 @@ bool Chainstate::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockV
39743977

39753978
// Header is valid/has work, merkle tree and segwit merkle tree are good...RELAY NOW
39763979
// (but if it does not build on our best tip, let the SendMessages loop relay it)
3977-
if (!IsInitialBlockDownload() && m_chain.Tip() == pindex->pprev)
3980+
if (!ActiveChainstate().IsInitialBlockDownload() && ActiveTip() == pindex->pprev)
39783981
GetMainSignals().NewPoWValidBlock(pindex, pblock);
39793982

39803983
// Write block to history file
@@ -3987,12 +3990,19 @@ bool Chainstate::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockV
39873990
}
39883991
ReceivedBlockTransactions(block, pindex, blockPos);
39893992
} catch (const std::runtime_error& e) {
3990-
return FatalError(m_chainman.GetNotifications(), state, std::string("System error: ") + e.what());
3993+
return FatalError(GetNotifications(), state, std::string("System error: ") + e.what());
39913994
}
39923995

3993-
FlushStateToDisk(state, FlushStateMode::NONE);
3996+
// TODO: FlushStateToDisk() handles flushing of both block and chainstate
3997+
// data, so we should move this to ChainstateManager so that we can be more
3998+
// intelligent about how we flush.
3999+
// For now, since FlushStateMode::NONE is used, all that can happen is that
4000+
// the block files may be pruned, so we can just call this on one
4001+
// chainstate (particularly if we haven't implemented pruning with
4002+
// background validation yet).
4003+
ActiveChainstate().FlushStateToDisk(state, FlushStateMode::NONE);
39944004

3995-
CheckBlockIndex();
4005+
ActiveChainstate().CheckBlockIndex();
39964006

39974007
return true;
39984008
}
@@ -4018,7 +4028,7 @@ bool ChainstateManager::ProcessNewBlock(const std::shared_ptr<const CBlock>& blo
40184028
bool ret = CheckBlock(*block, state, GetConsensus());
40194029
if (ret) {
40204030
// Store to disk
4021-
ret = ActiveChainstate().AcceptBlock(block, state, &pindex, force_processing, nullptr, new_block, min_pow_checked);
4031+
ret = AcceptBlock(block, state, &pindex, force_processing, nullptr, new_block, min_pow_checked);
40224032
}
40234033
if (!ret) {
40244034
GetMainSignals().BlockChecked(*block, state);
@@ -4507,26 +4517,24 @@ bool Chainstate::LoadGenesisBlock()
45074517
return error("%s: writing genesis block to disk failed", __func__);
45084518
}
45094519
CBlockIndex* pindex = m_blockman.AddToBlockIndex(block, m_chainman.m_best_header);
4510-
ReceivedBlockTransactions(block, pindex, blockPos);
4520+
m_chainman.ReceivedBlockTransactions(block, pindex, blockPos);
45114521
} catch (const std::runtime_error& e) {
45124522
return error("%s: failed to write genesis block: %s", __func__, e.what());
45134523
}
45144524

45154525
return true;
45164526
}
45174527

4518-
void Chainstate::LoadExternalBlockFile(
4528+
void ChainstateManager::LoadExternalBlockFile(
45194529
FILE* fileIn,
45204530
FlatFilePos* dbp,
45214531
std::multimap<uint256, FlatFilePos>* blocks_with_unknown_parent)
45224532
{
4523-
AssertLockNotHeld(m_chainstate_mutex);
4524-
45254533
// Either both should be specified (-reindex), or neither (-loadblock).
45264534
assert(!dbp == !blocks_with_unknown_parent);
45274535

45284536
const auto start{SteadyClock::now()};
4529-
const CChainParams& params{m_chainman.GetParams()};
4537+
const CChainParams& params{GetParams()};
45304538

45314539
int nLoaded = 0;
45324540
try {
@@ -4536,7 +4544,7 @@ void Chainstate::LoadExternalBlockFile(
45364544
// such as a block fails to deserialize.
45374545
uint64_t nRewind = blkdat.GetPos();
45384546
while (!blkdat.eof()) {
4539-
if (m_chainman.m_interrupt) return;
4547+
if (m_interrupt) return;
45404548

45414549
blkdat.SetPos(nRewind);
45424550
nRewind++; // start one byte further next time, in case of failure
@@ -4611,8 +4619,15 @@ void Chainstate::LoadExternalBlockFile(
46114619

46124620
// Activate the genesis block so normal node progress can continue
46134621
if (hash == params.GetConsensus().hashGenesisBlock) {
4614-
BlockValidationState state;
4615-
if (!ActivateBestChain(state, nullptr)) {
4622+
bool genesis_activation_failure = false;
4623+
for (auto c : GetAll()) {
4624+
BlockValidationState state;
4625+
if (!c->ActivateBestChain(state, nullptr)) {
4626+
genesis_activation_failure = true;
4627+
break;
4628+
}
4629+
}
4630+
if (genesis_activation_failure) {
46164631
break;
46174632
}
46184633
}
@@ -4625,14 +4640,21 @@ void Chainstate::LoadExternalBlockFile(
46254640
// until after all of the block files are loaded. ActivateBestChain can be
46264641
// called by concurrent network message processing. but, that is not
46274642
// reliable for the purpose of pruning while importing.
4628-
BlockValidationState state;
4629-
if (!ActivateBestChain(state, pblock)) {
4630-
LogPrint(BCLog::REINDEX, "failed to activate chain (%s)\n", state.ToString());
4643+
bool activation_failure = false;
4644+
for (auto c : GetAll()) {
4645+
BlockValidationState state;
4646+
if (!c->ActivateBestChain(state, pblock)) {
4647+
LogPrint(BCLog::REINDEX, "failed to activate chain (%s)\n", state.ToString());
4648+
activation_failure = true;
4649+
break;
4650+
}
4651+
}
4652+
if (activation_failure) {
46314653
break;
46324654
}
46334655
}
46344656

4635-
NotifyHeaderTip(*this);
4657+
NotifyHeaderTip(ActiveChainstate());
46364658

46374659
if (!blocks_with_unknown_parent) continue;
46384660

@@ -4658,7 +4680,7 @@ void Chainstate::LoadExternalBlockFile(
46584680
}
46594681
range.first++;
46604682
blocks_with_unknown_parent->erase(it);
4661-
NotifyHeaderTip(*this);
4683+
NotifyHeaderTip(ActiveChainstate());
46624684
}
46634685
}
46644686
} catch (const std::exception& e) {
@@ -4677,7 +4699,7 @@ void Chainstate::LoadExternalBlockFile(
46774699
}
46784700
}
46794701
} catch (const std::runtime_error& e) {
4680-
m_chainman.GetNotifications().fatalError(std::string("System error: ") + e.what());
4702+
GetNotifications().fatalError(std::string("System error: ") + e.what());
46814703
}
46824704
LogPrintf("Loaded %i blocks from external file in %dms\n", nLoaded, Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
46834705
}

0 commit comments

Comments
 (0)