refactoring: move block metadata structures into BlockManager

Separate out the management of chain-agnostic block metadata from any given
CChainState instance. This allows us to avoid duplicating data like
`mapBlockIndex` unnecessarily for multiple chainstates.

This also adds a CChainState constructor that accepts and sets m_blockman.
Ultimately this reference will point to a BlockMan instance that
is shared across CChainStates.

This commit can be decomposed into smaller commits if necessary.
This commit is contained in:
James O'Beirne 2019-03-27 17:07:32 -04:00
parent 2679bb8919
commit 613c46fe9e
2 changed files with 158 additions and 115 deletions

View file

@ -77,7 +77,11 @@ bool CBlockIndexWorkComparator::operator()(const CBlockIndex *pa, const CBlockIn
return false; return false;
} }
static CChainState g_chainstate; namespace {
BlockManager g_blockman;
} // anon namespace
static CChainState g_chainstate(g_blockman);
CChainState& ChainstateActive() { return g_chainstate; } CChainState& ChainstateActive() { return g_chainstate; }
@ -95,7 +99,7 @@ CChain& ChainActive() { return g_chainstate.m_chain; }
*/ */
RecursiveMutex cs_main; RecursiveMutex cs_main;
BlockMap& mapBlockIndex = ::ChainstateActive().mapBlockIndex; BlockMap& mapBlockIndex = g_blockman.m_block_index;
CBlockIndex *pindexBestHeader = nullptr; CBlockIndex *pindexBestHeader = nullptr;
Mutex g_best_block_mutex; Mutex g_best_block_mutex;
std::condition_variable g_best_block_cv; std::condition_variable g_best_block_cv;
@ -127,11 +131,6 @@ CScript COINBASE_FLAGS;
namespace { namespace {
CBlockIndex *&pindexBestInvalid = ::ChainstateActive().pindexBestInvalid; CBlockIndex *&pindexBestInvalid = ::ChainstateActive().pindexBestInvalid;
/** All pairs A->B, where A (or one of its ancestors) misses transactions, but B has transactions.
* Pruned nodes may have entries where B is missing data.
*/
std::multimap<CBlockIndex*, CBlockIndex*>& mapBlocksUnlinked = ::ChainstateActive().mapBlocksUnlinked;
CCriticalSection cs_LastBlockFile; CCriticalSection cs_LastBlockFile;
std::vector<CBlockFileInfo> vinfoBlockFile; std::vector<CBlockFileInfo> vinfoBlockFile;
int nLastBlockFile = 0; int nLastBlockFile = 0;
@ -1160,7 +1159,7 @@ void static InvalidChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(c
void CChainState::InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) { void CChainState::InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) {
if (state.GetReason() != ValidationInvalidReason::BLOCK_MUTATED) { if (state.GetReason() != ValidationInvalidReason::BLOCK_MUTATED) {
pindex->nStatus |= BLOCK_FAILED_VALID; pindex->nStatus |= BLOCK_FAILED_VALID;
m_failed_blocks.insert(pindex); m_blockman.m_failed_blocks.insert(pindex);
setDirtyBlockIndex.insert(pindex); setDirtyBlockIndex.insert(pindex);
setBlockIndexCandidates.erase(pindex); setBlockIndexCandidates.erase(pindex);
InvalidChainFound(pindex); InvalidChainFound(pindex);
@ -1695,8 +1694,8 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
// relative to a piece of software is an objective fact these defaults can be easily reviewed. // relative to a piece of software is an objective fact these defaults can be easily reviewed.
// This setting doesn't force the selection of any particular chain but makes validating some faster by // This setting doesn't force the selection of any particular chain but makes validating some faster by
// effectively caching the result of part of the verification. // effectively caching the result of part of the verification.
BlockMap::const_iterator it = mapBlockIndex.find(hashAssumeValid); BlockMap::const_iterator it = m_blockman.m_block_index.find(hashAssumeValid);
if (it != mapBlockIndex.end()) { if (it != m_blockman.m_block_index.end()) {
if (it->second->GetAncestor(pindex->nHeight) == pindex && if (it->second->GetAncestor(pindex->nHeight) == pindex &&
pindexBestHeader->GetAncestor(pindex->nHeight) == pindex && pindexBestHeader->GetAncestor(pindex->nHeight) == pindex &&
pindexBestHeader->nChainWork >= nMinimumChainWork) { pindexBestHeader->nChainWork >= nMinimumChainWork) {
@ -2366,10 +2365,11 @@ CBlockIndex* CChainState::FindMostWorkChain() {
if (fFailedChain) { if (fFailedChain) {
pindexFailed->nStatus |= BLOCK_FAILED_CHILD; pindexFailed->nStatus |= BLOCK_FAILED_CHILD;
} else if (fMissingData) { } else if (fMissingData) {
// If we're missing data, then add back to mapBlocksUnlinked, // If we're missing data, then add back to m_blocks_unlinked,
// so that if the block arrives in the future we can try adding // so that if the block arrives in the future we can try adding
// to setBlockIndexCandidates again. // to setBlockIndexCandidates again.
mapBlocksUnlinked.insert(std::make_pair(pindexFailed->pprev, pindexFailed)); m_blockman.m_blocks_unlinked.insert(
std::make_pair(pindexFailed->pprev, pindexFailed));
} }
setBlockIndexCandidates.erase(pindexFailed); setBlockIndexCandidates.erase(pindexFailed);
pindexFailed = pindexFailed->pprev; pindexFailed = pindexFailed->pprev;
@ -2720,12 +2720,12 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c
to_mark_failed->nStatus |= BLOCK_FAILED_VALID; to_mark_failed->nStatus |= BLOCK_FAILED_VALID;
setDirtyBlockIndex.insert(to_mark_failed); setDirtyBlockIndex.insert(to_mark_failed);
setBlockIndexCandidates.erase(to_mark_failed); setBlockIndexCandidates.erase(to_mark_failed);
m_failed_blocks.insert(to_mark_failed); m_blockman.m_failed_blocks.insert(to_mark_failed);
// The resulting new best tip may not be in setBlockIndexCandidates anymore, so // The resulting new best tip may not be in setBlockIndexCandidates anymore, so
// add it again. // add it again.
BlockMap::iterator it = mapBlockIndex.begin(); BlockMap::iterator it = m_blockman.m_block_index.begin();
while (it != mapBlockIndex.end()) { while (it != m_blockman.m_block_index.end()) {
if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && !setBlockIndexCandidates.value_comp()(it->second, m_chain.Tip())) { if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && !setBlockIndexCandidates.value_comp()(it->second, m_chain.Tip())) {
setBlockIndexCandidates.insert(it->second); setBlockIndexCandidates.insert(it->second);
} }
@ -2752,8 +2752,8 @@ void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
int nHeight = pindex->nHeight; int nHeight = pindex->nHeight;
// Remove the invalidity flag from this block and all its descendants. // Remove the invalidity flag from this block and all its descendants.
BlockMap::iterator it = mapBlockIndex.begin(); BlockMap::iterator it = m_blockman.m_block_index.begin();
while (it != mapBlockIndex.end()) { while (it != m_blockman.m_block_index.end()) {
if (!it->second->IsValid() && it->second->GetAncestor(nHeight) == pindex) { if (!it->second->IsValid() && it->second->GetAncestor(nHeight) == pindex) {
it->second->nStatus &= ~BLOCK_FAILED_MASK; it->second->nStatus &= ~BLOCK_FAILED_MASK;
setDirtyBlockIndex.insert(it->second); setDirtyBlockIndex.insert(it->second);
@ -2764,7 +2764,7 @@ void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
// Reset invalid block marker if it was pointing to one of those. // Reset invalid block marker if it was pointing to one of those.
pindexBestInvalid = nullptr; pindexBestInvalid = nullptr;
} }
m_failed_blocks.erase(it->second); m_blockman.m_failed_blocks.erase(it->second);
} }
it++; it++;
} }
@ -2774,7 +2774,7 @@ void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
if (pindex->nStatus & BLOCK_FAILED_MASK) { if (pindex->nStatus & BLOCK_FAILED_MASK) {
pindex->nStatus &= ~BLOCK_FAILED_MASK; pindex->nStatus &= ~BLOCK_FAILED_MASK;
setDirtyBlockIndex.insert(pindex); setDirtyBlockIndex.insert(pindex);
m_failed_blocks.erase(pindex); m_blockman.m_failed_blocks.erase(pindex);
} }
pindex = pindex->pprev; pindex = pindex->pprev;
} }
@ -2784,14 +2784,14 @@ void ResetBlockFailureFlags(CBlockIndex *pindex) {
return ::ChainstateActive().ResetBlockFailureFlags(pindex); return ::ChainstateActive().ResetBlockFailureFlags(pindex);
} }
CBlockIndex* CChainState::AddToBlockIndex(const CBlockHeader& block) CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block)
{ {
AssertLockHeld(cs_main); AssertLockHeld(cs_main);
// Check for duplicate // Check for duplicate
uint256 hash = block.GetHash(); uint256 hash = block.GetHash();
BlockMap::iterator it = mapBlockIndex.find(hash); BlockMap::iterator it = m_block_index.find(hash);
if (it != mapBlockIndex.end()) if (it != m_block_index.end())
return it->second; return it->second;
// Construct new block index object // Construct new block index object
@ -2800,10 +2800,10 @@ CBlockIndex* CChainState::AddToBlockIndex(const CBlockHeader& block)
// to avoid miners withholding blocks but broadcasting headers, to get a // to avoid miners withholding blocks but broadcasting headers, to get a
// competitive advantage. // competitive advantage.
pindexNew->nSequenceId = 0; pindexNew->nSequenceId = 0;
BlockMap::iterator mi = mapBlockIndex.insert(std::make_pair(hash, pindexNew)).first; BlockMap::iterator mi = m_block_index.insert(std::make_pair(hash, pindexNew)).first;
pindexNew->phashBlock = &((*mi).first); pindexNew->phashBlock = &((*mi).first);
BlockMap::iterator miPrev = mapBlockIndex.find(block.hashPrevBlock); BlockMap::iterator miPrev = m_block_index.find(block.hashPrevBlock);
if (miPrev != mapBlockIndex.end()) if (miPrev != m_block_index.end())
{ {
pindexNew->pprev = (*miPrev).second; pindexNew->pprev = (*miPrev).second;
pindexNew->nHeight = pindexNew->pprev->nHeight + 1; pindexNew->nHeight = pindexNew->pprev->nHeight + 1;
@ -2852,17 +2852,17 @@ void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pi
if (m_chain.Tip() == nullptr || !setBlockIndexCandidates.value_comp()(pindex, m_chain.Tip())) { if (m_chain.Tip() == nullptr || !setBlockIndexCandidates.value_comp()(pindex, m_chain.Tip())) {
setBlockIndexCandidates.insert(pindex); setBlockIndexCandidates.insert(pindex);
} }
std::pair<std::multimap<CBlockIndex*, CBlockIndex*>::iterator, std::multimap<CBlockIndex*, CBlockIndex*>::iterator> range = mapBlocksUnlinked.equal_range(pindex); std::pair<std::multimap<CBlockIndex*, CBlockIndex*>::iterator, std::multimap<CBlockIndex*, CBlockIndex*>::iterator> range = m_blockman.m_blocks_unlinked.equal_range(pindex);
while (range.first != range.second) { while (range.first != range.second) {
std::multimap<CBlockIndex*, CBlockIndex*>::iterator it = range.first; std::multimap<CBlockIndex*, CBlockIndex*>::iterator it = range.first;
queue.push_back(it->second); queue.push_back(it->second);
range.first++; range.first++;
mapBlocksUnlinked.erase(it); m_blockman.m_blocks_unlinked.erase(it);
} }
} }
} else { } else {
if (pindexNew->pprev && pindexNew->pprev->IsValid(BLOCK_VALID_TREE)) { if (pindexNew->pprev && pindexNew->pprev->IsValid(BLOCK_VALID_TREE)) {
mapBlocksUnlinked.insert(std::make_pair(pindexNew->pprev, pindexNew)); m_blockman.m_blocks_unlinked.insert(std::make_pair(pindexNew->pprev, pindexNew));
} }
} }
} }
@ -3230,15 +3230,15 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c
return true; return true;
} }
bool CChainState::AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex) bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex)
{ {
AssertLockHeld(cs_main); AssertLockHeld(cs_main);
// Check for duplicate // Check for duplicate
uint256 hash = block.GetHash(); uint256 hash = block.GetHash();
BlockMap::iterator miSelf = mapBlockIndex.find(hash); BlockMap::iterator miSelf = m_block_index.find(hash);
CBlockIndex *pindex = nullptr; CBlockIndex *pindex = nullptr;
if (hash != chainparams.GetConsensus().hashGenesisBlock) { if (hash != chainparams.GetConsensus().hashGenesisBlock) {
if (miSelf != mapBlockIndex.end()) { if (miSelf != m_block_index.end()) {
// Block header is already known. // Block header is already known.
pindex = miSelf->second; pindex = miSelf->second;
if (ppindex) if (ppindex)
@ -3253,8 +3253,8 @@ bool CChainState::AcceptBlockHeader(const CBlockHeader& block, CValidationState&
// Get prev block index // Get prev block index
CBlockIndex* pindexPrev = nullptr; CBlockIndex* pindexPrev = nullptr;
BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); BlockMap::iterator mi = m_block_index.find(block.hashPrevBlock);
if (mi == mapBlockIndex.end()) if (mi == m_block_index.end())
return state.Invalid(ValidationInvalidReason::BLOCK_MISSING_PREV, error("%s: prev block not found", __func__), 0, "prev-blk-not-found"); return state.Invalid(ValidationInvalidReason::BLOCK_MISSING_PREV, error("%s: prev block not found", __func__), 0, "prev-blk-not-found");
pindexPrev = (*mi).second; pindexPrev = (*mi).second;
if (pindexPrev->nStatus & BLOCK_FAILED_MASK) if (pindexPrev->nStatus & BLOCK_FAILED_MASK)
@ -3306,8 +3306,6 @@ bool CChainState::AcceptBlockHeader(const CBlockHeader& block, CValidationState&
if (ppindex) if (ppindex)
*ppindex = pindex; *ppindex = pindex;
CheckBlockIndex(chainparams.GetConsensus());
return true; return true;
} }
@ -3319,7 +3317,10 @@ bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidatio
LOCK(cs_main); LOCK(cs_main);
for (const CBlockHeader& header : headers) { for (const CBlockHeader& header : headers) {
CBlockIndex *pindex = nullptr; // Use a temp pindex instead of ppindex to avoid a const_cast CBlockIndex *pindex = nullptr; // Use a temp pindex instead of ppindex to avoid a const_cast
if (!::ChainstateActive().AcceptBlockHeader(header, state, chainparams, &pindex)) { bool accepted = g_blockman.AcceptBlockHeader(header, state, chainparams, &pindex);
::ChainstateActive().CheckBlockIndex(chainparams.GetConsensus());
if (!accepted) {
if (first_invalid) *first_invalid = header; if (first_invalid) *first_invalid = header;
return false; return false;
} }
@ -3362,7 +3363,10 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CVali
CBlockIndex *pindexDummy = nullptr; CBlockIndex *pindexDummy = nullptr;
CBlockIndex *&pindex = ppindex ? *ppindex : pindexDummy; CBlockIndex *&pindex = ppindex ? *ppindex : pindexDummy;
if (!AcceptBlockHeader(block, state, chainparams, &pindex)) bool accepted_header = m_blockman.AcceptBlockHeader(block, state, chainparams, &pindex);
CheckBlockIndex(chainparams.GetConsensus());
if (!accepted_header)
return false; return false;
// Try to process all requested blocks that we don't have, but only // Try to process all requested blocks that we don't have, but only
@ -3513,7 +3517,7 @@ void PruneOneBlockFile(const int fileNumber)
{ {
LOCK(cs_LastBlockFile); LOCK(cs_LastBlockFile);
for (const auto& entry : mapBlockIndex) { for (const auto& entry : g_blockman.m_block_index) {
CBlockIndex* pindex = entry.second; CBlockIndex* pindex = entry.second;
if (pindex->nFile == fileNumber) { if (pindex->nFile == fileNumber) {
pindex->nStatus &= ~BLOCK_HAVE_DATA; pindex->nStatus &= ~BLOCK_HAVE_DATA;
@ -3523,16 +3527,16 @@ void PruneOneBlockFile(const int fileNumber)
pindex->nUndoPos = 0; pindex->nUndoPos = 0;
setDirtyBlockIndex.insert(pindex); setDirtyBlockIndex.insert(pindex);
// Prune from mapBlocksUnlinked -- any block we prune would have // Prune from m_blocks_unlinked -- any block we prune would have
// to be downloaded again in order to consider its chain, at which // to be downloaded again in order to consider its chain, at which
// point it would be considered as a candidate for // point it would be considered as a candidate for
// mapBlocksUnlinked or setBlockIndexCandidates. // m_blocks_unlinked or setBlockIndexCandidates.
std::pair<std::multimap<CBlockIndex*, CBlockIndex*>::iterator, std::multimap<CBlockIndex*, CBlockIndex*>::iterator> range = mapBlocksUnlinked.equal_range(pindex->pprev); auto range = g_blockman.m_blocks_unlinked.equal_range(pindex->pprev);
while (range.first != range.second) { while (range.first != range.second) {
std::multimap<CBlockIndex *, CBlockIndex *>::iterator _it = range.first; std::multimap<CBlockIndex *, CBlockIndex *>::iterator _it = range.first;
range.first++; range.first++;
if (_it->second == pindex) { if (_it->second == pindex) {
mapBlocksUnlinked.erase(_it); g_blockman.m_blocks_unlinked.erase(_it);
} }
} }
} }
@ -3681,7 +3685,7 @@ fs::path GetBlockPosFilename(const FlatFilePos &pos)
return BlockFileSeq().FileName(pos); return BlockFileSeq().FileName(pos);
} }
CBlockIndex * CChainState::InsertBlockIndex(const uint256& hash) CBlockIndex * BlockManager::InsertBlockIndex(const uint256& hash)
{ {
AssertLockHeld(cs_main); AssertLockHeld(cs_main);
@ -3689,19 +3693,19 @@ CBlockIndex * CChainState::InsertBlockIndex(const uint256& hash)
return nullptr; return nullptr;
// Return existing // Return existing
BlockMap::iterator mi = mapBlockIndex.find(hash); BlockMap::iterator mi = m_block_index.find(hash);
if (mi != mapBlockIndex.end()) if (mi != m_block_index.end())
return (*mi).second; return (*mi).second;
// Create new // Create new
CBlockIndex* pindexNew = new CBlockIndex(); CBlockIndex* pindexNew = new CBlockIndex();
mi = mapBlockIndex.insert(std::make_pair(hash, pindexNew)).first; mi = m_block_index.insert(std::make_pair(hash, pindexNew)).first;
pindexNew->phashBlock = &((*mi).first); pindexNew->phashBlock = &((*mi).first);
return pindexNew; return pindexNew;
} }
bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlockTreeDB& blocktree) bool BlockManager::LoadBlockIndex(const Consensus::Params& consensus_params, CBlockTreeDB& blocktree)
{ {
if (!blocktree.LoadBlockIndexGuts(consensus_params, [this](const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return this->InsertBlockIndex(hash); })) if (!blocktree.LoadBlockIndexGuts(consensus_params, [this](const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return this->InsertBlockIndex(hash); }))
return false; return false;
@ -3729,7 +3733,7 @@ bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlo
pindex->nChainTx = pindex->pprev->nChainTx + pindex->nTx; pindex->nChainTx = pindex->pprev->nChainTx + pindex->nTx;
} else { } else {
pindex->nChainTx = 0; pindex->nChainTx = 0;
mapBlocksUnlinked.insert(std::make_pair(pindex->pprev, pindex)); m_blocks_unlinked.insert(std::make_pair(pindex->pprev, pindex));
} }
} else { } else {
pindex->nChainTx = pindex->nTx; pindex->nChainTx = pindex->nTx;
@ -3740,7 +3744,7 @@ bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlo
setDirtyBlockIndex.insert(pindex); setDirtyBlockIndex.insert(pindex);
} }
if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->HaveTxsDownloaded() || pindex->pprev == nullptr)) if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->HaveTxsDownloaded() || pindex->pprev == nullptr))
setBlockIndexCandidates.insert(pindex); ::ChainstateActive().setBlockIndexCandidates.insert(pindex);
if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork)) if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork))
pindexBestInvalid = pindex; pindexBestInvalid = pindex;
if (pindex->pprev) if (pindex->pprev)
@ -3752,9 +3756,20 @@ bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlo
return true; return true;
} }
void BlockManager::Unload() {
m_failed_blocks.clear();
m_blocks_unlinked.clear();
for (const BlockMap::value_type& entry : m_block_index) {
delete entry.second;
}
m_block_index.clear();
}
bool static LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main) bool static LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{ {
if (!::ChainstateActive().LoadBlockIndex(chainparams.GetConsensus(), *pblocktree)) if (!g_blockman.LoadBlockIndex(chainparams.GetConsensus(), *pblocktree))
return false; return false;
// Load block file info // Load block file info
@ -4051,10 +4066,10 @@ void CChainState::EraseBlockData(CBlockIndex* index)
setDirtyBlockIndex.insert(index); setDirtyBlockIndex.insert(index);
// Update indexes // Update indexes
setBlockIndexCandidates.erase(index); setBlockIndexCandidates.erase(index);
std::pair<std::multimap<CBlockIndex*, CBlockIndex*>::iterator, std::multimap<CBlockIndex*, CBlockIndex*>::iterator> ret = mapBlocksUnlinked.equal_range(index->pprev); auto ret = m_blockman.m_blocks_unlinked.equal_range(index->pprev);
while (ret.first != ret.second) { while (ret.first != ret.second) {
if (ret.first->second == index) { if (ret.first->second == index) {
mapBlocksUnlinked.erase(ret.first++); m_blockman.m_blocks_unlinked.erase(ret.first++);
} else { } else {
++ret.first; ++ret.first;
} }
@ -4180,7 +4195,6 @@ bool RewindBlockIndex(const CChainParams& params) {
void CChainState::UnloadBlockIndex() { void CChainState::UnloadBlockIndex() {
nBlockSequenceId = 1; nBlockSequenceId = 1;
m_failed_blocks.clear();
setBlockIndexCandidates.clear(); setBlockIndexCandidates.clear();
} }
@ -4191,10 +4205,10 @@ void UnloadBlockIndex()
{ {
LOCK(cs_main); LOCK(cs_main);
::ChainActive().SetTip(nullptr); ::ChainActive().SetTip(nullptr);
g_blockman.Unload();
pindexBestInvalid = nullptr; pindexBestInvalid = nullptr;
pindexBestHeader = nullptr; pindexBestHeader = nullptr;
mempool.clear(); mempool.clear();
mapBlocksUnlinked.clear();
vinfoBlockFile.clear(); vinfoBlockFile.clear();
nLastBlockFile = 0; nLastBlockFile = 0;
setDirtyBlockIndex.clear(); setDirtyBlockIndex.clear();
@ -4203,11 +4217,6 @@ void UnloadBlockIndex()
for (int b = 0; b < VERSIONBITS_NUM_BITS; b++) { for (int b = 0; b < VERSIONBITS_NUM_BITS; b++) {
warningcache[b].clear(); warningcache[b].clear();
} }
for (const BlockMap::value_type& entry : mapBlockIndex) {
delete entry.second;
}
mapBlockIndex.clear();
fHavePruned = false; fHavePruned = false;
::ChainstateActive().UnloadBlockIndex(); ::ChainstateActive().UnloadBlockIndex();
@ -4251,7 +4260,7 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams)
FlatFilePos blockPos = SaveBlockToDisk(block, 0, chainparams, nullptr); FlatFilePos blockPos = SaveBlockToDisk(block, 0, chainparams, nullptr);
if (blockPos.IsNull()) if (blockPos.IsNull())
return error("%s: writing genesis block to disk failed", __func__); return error("%s: writing genesis block to disk failed", __func__);
CBlockIndex *pindex = AddToBlockIndex(block); CBlockIndex *pindex = m_blockman.AddToBlockIndex(block);
ReceivedBlockTransactions(block, pindex, blockPos, chainparams.GetConsensus()); ReceivedBlockTransactions(block, pindex, blockPos, chainparams.GetConsensus());
} catch (const std::runtime_error& e) { } catch (const std::runtime_error& e) {
return error("%s: failed to write genesis block: %s", __func__, e.what()); return error("%s: failed to write genesis block: %s", __func__, e.what());
@ -4482,13 +4491,13 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
} }
// If some parent is missing, then it could be that this block was in // If some parent is missing, then it could be that this block was in
// setBlockIndexCandidates but had to be removed because of the missing data. // setBlockIndexCandidates but had to be removed because of the missing data.
// In this case it must be in mapBlocksUnlinked -- see test below. // In this case it must be in m_blocks_unlinked -- see test below.
} }
} else { // If this block sorts worse than the current tip or some ancestor's block has never been seen, it cannot be in setBlockIndexCandidates. } else { // If this block sorts worse than the current tip or some ancestor's block has never been seen, it cannot be in setBlockIndexCandidates.
assert(setBlockIndexCandidates.count(pindex) == 0); assert(setBlockIndexCandidates.count(pindex) == 0);
} }
// Check whether this block is in mapBlocksUnlinked. // Check whether this block is in m_blocks_unlinked.
std::pair<std::multimap<CBlockIndex*,CBlockIndex*>::iterator,std::multimap<CBlockIndex*,CBlockIndex*>::iterator> rangeUnlinked = mapBlocksUnlinked.equal_range(pindex->pprev); std::pair<std::multimap<CBlockIndex*,CBlockIndex*>::iterator,std::multimap<CBlockIndex*,CBlockIndex*>::iterator> rangeUnlinked = m_blockman.m_blocks_unlinked.equal_range(pindex->pprev);
bool foundInUnlinked = false; bool foundInUnlinked = false;
while (rangeUnlinked.first != rangeUnlinked.second) { while (rangeUnlinked.first != rangeUnlinked.second) {
assert(rangeUnlinked.first->first == pindex->pprev); assert(rangeUnlinked.first->first == pindex->pprev);
@ -4499,22 +4508,22 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
rangeUnlinked.first++; rangeUnlinked.first++;
} }
if (pindex->pprev && (pindex->nStatus & BLOCK_HAVE_DATA) && pindexFirstNeverProcessed != nullptr && pindexFirstInvalid == nullptr) { if (pindex->pprev && (pindex->nStatus & BLOCK_HAVE_DATA) && pindexFirstNeverProcessed != nullptr && pindexFirstInvalid == nullptr) {
// If this block has block data available, some parent was never received, and has no invalid parents, it must be in mapBlocksUnlinked. // If this block has block data available, some parent was never received, and has no invalid parents, it must be in m_blocks_unlinked.
assert(foundInUnlinked); assert(foundInUnlinked);
} }
if (!(pindex->nStatus & BLOCK_HAVE_DATA)) assert(!foundInUnlinked); // Can't be in mapBlocksUnlinked if we don't HAVE_DATA if (!(pindex->nStatus & BLOCK_HAVE_DATA)) assert(!foundInUnlinked); // Can't be in m_blocks_unlinked if we don't HAVE_DATA
if (pindexFirstMissing == nullptr) assert(!foundInUnlinked); // We aren't missing data for any parent -- cannot be in mapBlocksUnlinked. if (pindexFirstMissing == nullptr) assert(!foundInUnlinked); // We aren't missing data for any parent -- cannot be in m_blocks_unlinked.
if (pindex->pprev && (pindex->nStatus & BLOCK_HAVE_DATA) && pindexFirstNeverProcessed == nullptr && pindexFirstMissing != nullptr) { if (pindex->pprev && (pindex->nStatus & BLOCK_HAVE_DATA) && pindexFirstNeverProcessed == nullptr && pindexFirstMissing != nullptr) {
// We HAVE_DATA for this block, have received data for all parents at some point, but we're currently missing data for some parent. // We HAVE_DATA for this block, have received data for all parents at some point, but we're currently missing data for some parent.
assert(fHavePruned); // We must have pruned. assert(fHavePruned); // We must have pruned.
// This block may have entered mapBlocksUnlinked if: // This block may have entered m_blocks_unlinked if:
// - it has a descendant that at some point had more work than the // - it has a descendant that at some point had more work than the
// tip, and // tip, and
// - we tried switching to that descendant but were missing // - we tried switching to that descendant but were missing
// data for some intermediate block between m_chain and the // data for some intermediate block between m_chain and the
// tip. // tip.
// So if this block is itself better than m_chain.Tip() and it wasn't in // So if this block is itself better than m_chain.Tip() and it wasn't in
// setBlockIndexCandidates, then it must be in mapBlocksUnlinked. // setBlockIndexCandidates, then it must be in m_blocks_unlinked.
if (!CBlockIndexWorkComparator()(pindex, m_chain.Tip()) && setBlockIndexCandidates.count(pindex) == 0) { if (!CBlockIndexWorkComparator()(pindex, m_chain.Tip()) && setBlockIndexCandidates.count(pindex) == 0) {
if (pindexFirstInvalid == nullptr) { if (pindexFirstInvalid == nullptr) {
assert(foundInUnlinked); assert(foundInUnlinked);

View file

@ -439,27 +439,80 @@ struct CBlockIndexWorkComparator
}; };
/** /**
* CChainState stores and provides an API to update our local knowledge of the * Maintains a tree of blocks (stored in `m_block_index`) which is consulted
* current best chain and header tree. * to determine where the most-work tip is.
* *
* It generally provides access to the current block tree, as well as functions * This data is used mostly in `CChainState` - information about, e.g.,
* to provide new data, which it will appropriately validate and incorporate in * candidate tips is not maintained here.
* its state as necessary. */
class BlockManager {
public:
BlockMap m_block_index GUARDED_BY(cs_main);
/** In order to efficiently track invalidity of headers, we keep the set of
* blocks which we tried to connect and found to be invalid here (ie which
* were set to BLOCK_FAILED_VALID since the last restart). We can then
* walk this set and check if a new header is a descendant of something in
* this set, preventing us from having to walk m_block_index when we try
* to connect a bad block and fail.
*
* While this is more complicated than marking everything which descends
* from an invalid block as invalid at the time we discover it to be
* invalid, doing so would require walking all of m_block_index to find all
* descendants. Since this case should be very rare, keeping track of all
* BLOCK_FAILED_VALID blocks in a set should be just fine and work just as
* well.
*
* Because we already walk m_block_index in height-order at startup, we go
* ahead and mark descendants of invalid blocks as FAILED_CHILD at that time,
* instead of putting things in this set.
*/
std::set<CBlockIndex*> m_failed_blocks;
/**
* All pairs A->B, where A (or one of its ancestors) misses transactions, but B has transactions.
* Pruned nodes may have entries where B is missing data.
*/
std::multimap<CBlockIndex*, CBlockIndex*> m_blocks_unlinked;
bool LoadBlockIndex(
const Consensus::Params& consensus_params,
CBlockTreeDB& blocktree) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Clear all data members. */
void Unload() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
CBlockIndex* AddToBlockIndex(const CBlockHeader& block) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Create a new block index entry for a given block hash */
CBlockIndex* InsertBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/**
* If a block header hasn't already been seen, call CheckBlockHeader on it, ensure
* that it doesn't descend from an invalid block, and then add it to mapBlockIndex.
*/
bool AcceptBlockHeader(
const CBlockHeader& block,
CValidationState& state,
const CChainParams& chainparams,
CBlockIndex** ppindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
};
/**
* CChainState stores and provides an API to update our local knowledge of the
* current best chain.
* *
* Eventually, the API here is targeted at being exposed externally as a * Eventually, the API here is targeted at being exposed externally as a
* consumable libconsensus library, so any functions added must only call * consumable libconsensus library, so any functions added must only call
* other class member functions, pure functions in other parts of the consensus * other class member functions, pure functions in other parts of the consensus
* library, callbacks via the validation interface, or read/write-to-disk * library, callbacks via the validation interface, or read/write-to-disk
* functions (eventually this will also be via callbacks). * functions (eventually this will also be via callbacks).
*
* Anything that is contingent on the current tip of the chain is stored here,
* whereas block information and metadata independent of the current tip is
* kept in `BlockMetadataManager`.
*/ */
class CChainState { class CChainState {
private: private:
/**
* The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself and all ancestors) and
* as good as our current tip or better. Entries may be failed, though, and pruning nodes may be
* missing the data for the block.
*/
std::set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates;
/** /**
* Every received block is assigned a unique and increasing identifier, so we * Every received block is assigned a unique and increasing identifier, so we
@ -473,26 +526,6 @@ private:
/** chainwork for the last block that preciousblock has been applied to. */ /** chainwork for the last block that preciousblock has been applied to. */
arith_uint256 nLastPreciousChainwork = 0; arith_uint256 nLastPreciousChainwork = 0;
/** In order to efficiently track invalidity of headers, we keep the set of
* blocks which we tried to connect and found to be invalid here (ie which
* were set to BLOCK_FAILED_VALID since the last restart). We can then
* walk this set and check if a new header is a descendant of something in
* this set, preventing us from having to walk mapBlockIndex when we try
* to connect a bad block and fail.
*
* While this is more complicated than marking everything which descends
* from an invalid block as invalid at the time we discover it to be
* invalid, doing so would require walking all of mapBlockIndex to find all
* descendants. Since this case should be very rare, keeping track of all
* BLOCK_FAILED_VALID blocks in a set should be just fine and work just as
* well.
*
* Because we already walk mapBlockIndex in height-order at startup, we go
* ahead and mark descendants of invalid blocks as FAILED_CHILD at that time,
* instead of putting things in this set.
*/
std::set<CBlockIndex*> m_failed_blocks;
/** /**
* the ChainState CriticalSection * the ChainState CriticalSection
* A lock that must be held when modifying this ChainState - held in ActivateBestChain() * A lock that must be held when modifying this ChainState - held in ActivateBestChain()
@ -507,15 +540,24 @@ private:
*/ */
mutable std::atomic<bool> m_cached_finished_ibd{false}; mutable std::atomic<bool> m_cached_finished_ibd{false};
//! Reference to a BlockManager instance which itself is shared across all
//! CChainState instances. Keeping a local reference allows us to test more
//! easily as opposed to referencing a global.
BlockManager& m_blockman;
public: public:
CChainState(BlockManager& blockman) : m_blockman(blockman) { }
//! The current chain of blockheaders we consult and build on. //! The current chain of blockheaders we consult and build on.
//! @see CChain, CBlockIndex. //! @see CChain, CBlockIndex.
CChain m_chain; CChain m_chain;
BlockMap mapBlockIndex GUARDED_BY(cs_main);
std::multimap<CBlockIndex*, CBlockIndex*> mapBlocksUnlinked;
CBlockIndex *pindexBestInvalid = nullptr; CBlockIndex *pindexBestInvalid = nullptr;
/**
bool LoadBlockIndex(const Consensus::Params& consensus_params, CBlockTreeDB& blocktree) EXCLUSIVE_LOCKS_REQUIRED(cs_main); * The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself and all ancestors) and
* as good as our current tip or better. Entries may be failed, though, and pruning nodes may be
* missing the data for the block.
*/
std::set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates;
/** /**
* Update the on-disk chain state. * Update the on-disk chain state.
@ -541,11 +583,6 @@ public:
bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) LOCKS_EXCLUDED(cs_main); bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) LOCKS_EXCLUDED(cs_main);
/**
* If a block header hasn't already been seen, call CheckBlockHeader on it, ensure
* that it doesn't descend from an invalid block, and then add it to mapBlockIndex.
*/
bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock) EXCLUSIVE_LOCKS_REQUIRED(cs_main); bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
// Block (dis)connection on a given view: // Block (dis)connection on a given view:
@ -572,13 +609,6 @@ public:
/** Check whether we are doing an initial block download (synchronizing from disk or network) */ /** Check whether we are doing an initial block download (synchronizing from disk or network) */
bool IsInitialBlockDownload() const; bool IsInitialBlockDownload() const;
private:
bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs);
bool ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs);
CBlockIndex* AddToBlockIndex(const CBlockHeader& block) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Create a new block index entry for a given block hash */
CBlockIndex* InsertBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** /**
* Make various assertions about the state of the block index. * Make various assertions about the state of the block index.
* *
@ -586,6 +616,10 @@ private:
*/ */
void CheckBlockIndex(const Consensus::Params& consensusParams); void CheckBlockIndex(const Consensus::Params& consensusParams);
private:
bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs);
bool ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs);
void InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) EXCLUSIVE_LOCKS_REQUIRED(cs_main); void InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
CBlockIndex* FindMostWorkChain() EXCLUSIVE_LOCKS_REQUIRED(cs_main); CBlockIndex* FindMostWorkChain() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
void ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const FlatFilePos& pos, const Consensus::Params& consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main); void ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const FlatFilePos& pos, const Consensus::Params& consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);