Skip to content

Commit

Permalink
merge bitcoin#24515: Only load BlockMan in BlockMan member functions
Browse files Browse the repository at this point in the history
  • Loading branch information
kwvg committed Jul 19, 2024
1 parent 5c1eb67 commit 1bf0bf4
Show file tree
Hide file tree
Showing 10 changed files with 152 additions and 126 deletions.
2 changes: 1 addition & 1 deletion src/index/coinstatsindex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ bool CoinStatsIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* n

{
LOCK(cs_main);
CBlockIndex* iter_tip{m_chainstate->m_blockman.LookupBlockIndex(current_tip->GetBlockHash())};
const CBlockIndex* iter_tip{m_chainstate->m_blockman.LookupBlockIndex(current_tip->GetBlockHash())};
const auto& consensus_params{Params().GetConsensus()};

do {
Expand Down
3 changes: 2 additions & 1 deletion src/miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParam
return nNewTime - nOldTime;
}

BlockAssembler::Options::Options() {
BlockAssembler::Options::Options()
{
blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE);
nBlockMaxSize = DEFAULT_BLOCK_MAX_SIZE;
}
Expand Down
129 changes: 50 additions & 79 deletions src/node/blockstorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,45 @@ bool fAddressIndex = DEFAULT_ADDRESSINDEX;
bool fTimestampIndex = DEFAULT_TIMESTAMPINDEX;
bool fSpentIndex = DEFAULT_SPENTINDEX;

bool CBlockIndexWorkComparator::operator()(const CBlockIndex* pa, const CBlockIndex* pb) const
{
// First sort by most total work, ...
if (pa->nChainWork > pb->nChainWork) return false;
if (pa->nChainWork < pb->nChainWork) return true;

// ... then by earliest time received, ...
if (pa->nSequenceId < pb->nSequenceId) return false;
if (pa->nSequenceId > pb->nSequenceId) return true;

// Use pointer address as tie breaker (should only happen with blocks
// loaded from disk, as those all have id 0).
if (pa < pb) return false;
if (pa > pb) return true;

// Identical blocks.
return false;
}

bool CBlockIndexHeightOnlyComparator::operator()(const CBlockIndex* pa, const CBlockIndex* pb) const
{
return pa->nHeight < pb->nHeight;
}

static FILE* OpenUndoFile(const FlatFilePos& pos, bool fReadOnly = false);
static FlatFileSeq BlockFileSeq();
static FlatFileSeq UndoFileSeq();

std::vector<CBlockIndex*> BlockManager::GetAllBlockIndices()
{
AssertLockHeld(cs_main);
std::vector<CBlockIndex*> rv;
rv.reserve(m_block_index.size());
for (auto& [_, block_index] : m_block_index) {
rv.push_back(&block_index);
}
return rv;
}

CBlockIndex* BlockManager::LookupBlockIndex(const uint256& hash)
{
AssertLockHeld(cs_main);
Expand Down Expand Up @@ -220,60 +255,34 @@ CBlockIndex* BlockManager::InsertBlockIndex(const uint256& hash)
return nullptr;
}

// Return existing or create new
auto [mi, inserted] = m_block_index.try_emplace(hash);
const auto [mi, inserted]{m_block_index.try_emplace(hash)};
CBlockIndex* pindex = &(*mi).second;
if (inserted) {
pindex->phashBlock = &((*mi).first);
}
return pindex;
}

bool BlockManager::LoadBlockIndex(
const Consensus::Params& consensus_params,
ChainstateManager& chainman)
bool BlockManager::LoadBlockIndex(const Consensus::Params& consensus_params)
{
if (!m_block_tree_db->LoadBlockIndexGuts(consensus_params, [this](const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return this->InsertBlockIndex(hash); })) {
return false;
}

// Calculate nChainWork
std::vector<std::pair<int, CBlockIndex*>> vSortedByHeight;
vSortedByHeight.reserve(m_block_index.size());
for (auto& [_, block_index] : m_block_index) {
CBlockIndex* pindex = &block_index;
vSortedByHeight.push_back(std::make_pair(pindex->nHeight, pindex));

// build m_blockman.m_prev_block_index
if (pindex->pprev) {
m_prev_block_index.emplace(pindex->pprev->GetBlockHash(), pindex);
if (block_index.pprev) {
m_prev_block_index.emplace(block_index.pprev->GetBlockHash(), &block_index);
}
}
sort(vSortedByHeight.begin(), vSortedByHeight.end());

// Find start of assumed-valid region.
int first_assumed_valid_height = std::numeric_limits<int>::max();

for (const auto& [height, block] : vSortedByHeight) {
if (block->IsAssumedValid()) {
auto chainstates = chainman.GetAll();

// If we encounter an assumed-valid block index entry, ensure that we have
// one chainstate that tolerates assumed-valid entries and another that does
// not (i.e. the background validation chainstate), since assumed-valid
// entries should always be pending validation by a fully-validated chainstate.
auto any_chain = [&](auto fnc) { return std::any_of(chainstates.cbegin(), chainstates.cend(), fnc); };
assert(any_chain([](auto chainstate) { return chainstate->reliesOnAssumedValid(); }));
assert(any_chain([](auto chainstate) { return !chainstate->reliesOnAssumedValid(); }));

first_assumed_valid_height = height;
break;
}
}
// Calculate nChainWork
std::vector<CBlockIndex*> vSortedByHeight{GetAllBlockIndices()};
std::sort(vSortedByHeight.begin(), vSortedByHeight.end(),
CBlockIndexHeightOnlyComparator());

for (const std::pair<int, CBlockIndex*>& item : vSortedByHeight) {
for (CBlockIndex* pindex : vSortedByHeight) {
if (ShutdownRequested()) return false;
CBlockIndex* pindex = item.second;
pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex);
pindex->nTimeMax = (pindex->pprev ? std::max(pindex->pprev->nTimeMax, pindex->nTime) : pindex->nTime);

Expand All @@ -297,43 +306,6 @@ bool BlockManager::LoadBlockIndex(
pindex->nStatus |= BLOCK_FAILED_CHILD;
m_dirty_blockindex.insert(pindex);
}
if (pindex->IsAssumedValid() ||
(pindex->IsValid(BLOCK_VALID_TRANSACTIONS) &&
(pindex->HaveTxsDownloaded() || pindex->pprev == nullptr))) {

// Fill each chainstate's block candidate set. Only add assumed-valid
// blocks to the tip candidate set if the chainstate is allowed to rely on
// assumed-valid blocks.
//
// If all setBlockIndexCandidates contained the assumed-valid blocks, the
// background chainstate's ActivateBestChain() call would add assumed-valid
// blocks to the chain (based on how FindMostWorkChain() works). Obviously
// we don't want this since the purpose of the background validation chain
// is to validate assued-valid blocks.
//
// Note: This is considering all blocks whose height is greater or equal to
// the first assumed-valid block to be assumed-valid blocks, and excluding
// them from the background chainstate's setBlockIndexCandidates set. This
// does mean that some blocks which are not technically assumed-valid
// (later blocks on a fork beginning before the first assumed-valid block)
// might not get added to the the background chainstate, but this is ok,
// because they will still be attached to the active chainstate if they
// actually contain more work.
//
// Instad of this height-based approach, an earlier attempt was made at
// detecting "holistically" whether the block index under consideration
// relied on an assumed-valid ancestor, but this proved to be too slow to
// be practical.
for (CChainState* chainstate : chainman.GetAll()) {
if (chainstate->reliesOnAssumedValid() ||
pindex->nHeight < first_assumed_valid_height) {
chainstate->setBlockIndexCandidates.insert(pindex);
}
}
}
if (pindex->nStatus & BLOCK_FAILED_MASK && (!chainman.m_best_invalid || pindex->nChainWork > chainman.m_best_invalid->nChainWork)) {
chainman.m_best_invalid = pindex;
}
if (pindex->pprev) {
pindex->BuildSkip();
}
Expand Down Expand Up @@ -377,9 +349,9 @@ bool BlockManager::WriteBlockIndexDB()
return true;
}

bool BlockManager::LoadBlockIndexDB(ChainstateManager& chainman)
bool BlockManager::LoadBlockIndexDB()
{
if (!LoadBlockIndex(::Params().GetConsensus(), chainman)) {
if (!LoadBlockIndex(::Params().GetConsensus())) {
return false;
}

Expand All @@ -404,9 +376,8 @@ bool BlockManager::LoadBlockIndexDB(ChainstateManager& chainman)
LogPrintf("Checking all blk files are present...\n");
std::set<int> setBlkDataFiles;
for (const auto& [_, block_index] : m_block_index) {
const CBlockIndex* pindex = &block_index;
if (pindex->nStatus & BLOCK_HAVE_DATA) {
setBlkDataFiles.insert(pindex->nFile);
if (block_index.nStatus & BLOCK_HAVE_DATA) {
setBlkDataFiles.insert(block_index.nFile);
}
}
for (std::set<int>::iterator it = setBlkDataFiles.begin(); it != setBlkDataFiles.end(); it++) {
Expand Down Expand Up @@ -442,13 +413,13 @@ bool BlockManager::LoadBlockIndexDB(ChainstateManager& chainman)
return true;
}

CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)
const CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)
{
const MapCheckpoints& checkpoints = data.mapCheckpoints;

for (const MapCheckpoints::value_type& i : reverse_iterate(checkpoints)) {
const uint256& hash = i.second;
CBlockIndex* pindex = LookupBlockIndex(hash);
const CBlockIndex* pindex = LookupBlockIndex(hash);
if (pindex) {
return pindex;
}
Expand Down
16 changes: 11 additions & 5 deletions src/node/blockstorage.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ struct CBlockIndexWorkComparator {
bool operator()(const CBlockIndex* pa, const CBlockIndex* pb) const;
};

struct CBlockIndexHeightOnlyComparator {
/* Only compares the height of two block indices, doesn't try to tie-break */
bool operator()(const CBlockIndex* pa, const CBlockIndex* pb) const;
};

/**
* Maintains a tree of blocks (stored in `m_block_index`) which is consulted
* to determine where the most-work tip is.
Expand Down Expand Up @@ -131,6 +136,8 @@ class BlockManager
BlockMap m_block_index GUARDED_BY(cs_main);
PrevBlockMap m_prev_block_index GUARDED_BY(cs_main);

std::vector<CBlockIndex*> GetAllBlockIndices() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);

/**
* 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.
Expand All @@ -140,16 +147,15 @@ class BlockManager
std::unique_ptr<CBlockTreeDB> m_block_tree_db GUARDED_BY(::cs_main);

bool WriteBlockIndexDB() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
bool LoadBlockIndexDB(ChainstateManager& chainman) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
bool LoadBlockIndexDB() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);

/**
* Load the blocktree off disk and into memory. Populate certain metadata
* per index entry (nStatus, nChainWork, nTimeMax, etc.) as well as peripheral
* collections like m_dirty_blockindex.
*/
bool LoadBlockIndex(
const Consensus::Params& consensus_params,
ChainstateManager& chainman) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool LoadBlockIndex(const Consensus::Params& consensus_params)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);

/** Clear all data members. */
void Unload() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
Expand All @@ -176,7 +182,7 @@ class BlockManager
uint64_t CalculateCurrentUsage();

//! Returns last CBlockIndex* that is a checkpoint
CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
const CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) EXCLUSIVE_LOCKS_REQUIRED(cs_main);

/**
* Return the spend height, which is one more than the inputs.GetBestBlock().
Expand Down
4 changes: 2 additions & 2 deletions src/node/interfaces.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -763,7 +763,7 @@ class ChainImpl : public Chain
{
LOCK(cs_main);
const CChainState& active = Assert(m_node.chainman)->ActiveChainstate();
if (CBlockIndex* fork = active.FindForkInGlobalIndex(locator)) {
if (const CBlockIndex* fork = active.FindForkInGlobalIndex(locator)) {
return fork->nHeight;
}
return std::nullopt;
Expand Down Expand Up @@ -861,7 +861,7 @@ class ChainImpl : public Chain
// used to limit the range, and passing min_height that's too low or
// max_height that's too high will not crash or change the result.
LOCK(::cs_main);
if (CBlockIndex* block = chainman().m_blockman.LookupBlockIndex(block_hash)) {
if (const CBlockIndex* block = chainman().m_blockman.LookupBlockIndex(block_hash)) {
if (max_height && block->nHeight >= *max_height) block = block->GetAncestor(*max_height);
for (; block->nStatus & BLOCK_HAVE_DATA; block = block->pprev) {
// Check pprev to not segfault if min_height is too low
Expand Down
4 changes: 2 additions & 2 deletions src/rest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,8 +271,8 @@ static bool rest_block(const CoreContext& context,
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);

CBlock block;
CBlockIndex* pblockindex = nullptr;
CBlockIndex* tip = nullptr;
const CBlockIndex* pblockindex = nullptr;
const CBlockIndex* tip = nullptr;
{
ChainstateManager* maybe_chainman = GetChainman(context, req);
if (!maybe_chainman) return false;
Expand Down
17 changes: 9 additions & 8 deletions src/rpc/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ static int ComputeNextBlockAndDepth(const CBlockIndex* tip, const CBlockIndex* b
return blockindex == tip ? 1 : -1;
}

CBlockIndex* ParseHashOrHeight(const UniValue& param, ChainstateManager& chainman) {
static const CBlockIndex* ParseHashOrHeight(const UniValue& param, ChainstateManager& chainman)
{
LOCK(::cs_main);
CChain& active_chain = chainman.ActiveChain();

Expand All @@ -113,7 +114,7 @@ CBlockIndex* ParseHashOrHeight(const UniValue& param, ChainstateManager& chainma
return active_chain[height];
} else {
const uint256 hash{ParseHashV(param, "hash_or_height")};
CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash);
const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash);

if (!pindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
Expand Down Expand Up @@ -874,7 +875,7 @@ static RPCHelpMan getblockhash()
if (nHeight < 0 || nHeight > active_chain.Height())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range");

CBlockIndex* pblockindex = active_chain[nHeight];
const CBlockIndex* pblockindex = active_chain[nHeight];
return pblockindex->GetBlockHash().GetHex();
},
};
Expand Down Expand Up @@ -1304,7 +1305,7 @@ static RPCHelpMan pruneblockchain()
// too low to be a block time (corresponds to timestamp from Sep 2001).
if (heightParam > 1000000000) {
// Add a 2 hour buffer to include blocks which might have had old timestamps
CBlockIndex* pindex = active_chain.FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW, 0);
const CBlockIndex* pindex = active_chain.FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW, 0);
if (!pindex) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Could not find block with at least the specified timestamp.");
}
Expand Down Expand Up @@ -1398,7 +1399,7 @@ static RPCHelpMan gettxoutsetinfo()
{
UniValue ret(UniValue::VOBJ);

CBlockIndex* pindex{nullptr};
const CBlockIndex* pindex{nullptr};
const CoinStatsHashType hash_type{request.params[0].isNull() ? CoinStatsHashType::HASH_SERIALIZED : ParseHashType(request.params[0].get_str())};
CCoinsStats stats{hash_type};
stats.index_requested = request.params[2].isNull() || request.params[2].get_bool();
Expand Down Expand Up @@ -2344,7 +2345,7 @@ static RPCHelpMan getblockstats()

ChainstateManager& chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
CBlockIndex* pindex{ParseHashOrHeight(request.params[0], chainman)};
const CBlockIndex* pindex{ParseHashOrHeight(request.params[0], chainman)};
CHECK_NONFATAL(pindex != nullptr);

std::set<std::string> stats;
Expand Down Expand Up @@ -2801,7 +2802,7 @@ static RPCHelpMan scantxoutset()
g_should_abort_scan = false;
int64_t count = 0;
std::unique_ptr<CCoinsViewCursor> pcursor;
CBlockIndex* tip;
const CBlockIndex* tip;
NodeContext& node = EnsureAnyNodeContext(request.context);
{
ChainstateManager& chainman = EnsureChainman(node);
Expand Down Expand Up @@ -2981,7 +2982,7 @@ UniValue CreateUTXOSnapshot(NodeContext& node, CChainState& chainstate, CAutoFil
{
std::unique_ptr<CCoinsViewCursor> pcursor;
CCoinsStats stats{CoinStatsHashType::NONE};
CBlockIndex* tip;
const CBlockIndex* tip;

{
// We need to lock cs_main to ensure that the coinsdb isn't written to
Expand Down
4 changes: 2 additions & 2 deletions src/rpc/rawtransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ static RPCHelpMan getrawtransaction()

bool in_active_chain = true;
uint256 hash = ParseHashV(request.params[0], "parameter 1");
CBlockIndex* blockindex = nullptr;
const CBlockIndex* blockindex = nullptr;

if (hash == Params().GenesisBlock().hashMerkleRoot) {
// Special exception for the genesis block coinbase transaction
Expand Down Expand Up @@ -610,7 +610,7 @@ static RPCHelpMan gettxoutproof()
}
}

CBlockIndex* pblockindex = nullptr;
const CBlockIndex* pblockindex = nullptr;
uint256 hashBlock;
ChainstateManager& chainman = EnsureAnyChainman(request.context);
if (!request.params[1].isNull()) {
Expand Down
Loading

0 comments on commit 1bf0bf4

Please sign in to comment.