Skip to content

Commit 601bfc4

Browse files
author
MarcoFalke
committed
Merge bitcoin/bitcoin#24515: Only load BlockMan in BlockMan member functions
f865cf8 Add and use BlockManager::GetAllBlockIndices (Carl Dong) 28ba031 Add and use CBlockIndexHeightOnlyComparator (Carl Dong) 12eb05d move-only: Move CBlockIndexWorkComparator to blockstorage (Carl Dong) c600ee3 Only load BlockMan in BlockMan member functions (Carl Dong) 42e56d9 style-only: No need for std::pair for vSortedByHeight (Carl Dong) 3bbb6fe style-only: Various blockstorage.cpp cleanups (Carl Dong) 5be9ee3 refactor: more const annotations for uses of CBlockIndex* (Anthony Towns) Pull request description: The only important commit is "Only load BlockMan in BlockMan member functions", everything else is all just small style changes. Here's the commit message, reproduced: ``` This commit effectively splits the "load block index itself" logic from "derive Chainstate variables from loaded block index" logic. This means that BlockManager::LoadBlockIndex{,DB} will only load what's relevant to the BlockManager. ``` ACKs for top commit: ajtowns: ACK f865cf8 ; code review only MarcoFalke: review ACK f865cf8 🗂 Tree-SHA512: 7b204d782834e06fd7329d022e2ae860181b4e8105c33bfb928539a4ec24161dc7438a9c4d4ee279dcad77de310c160b997bb8aa18923243d0fd55ccf4ad7c3a
2 parents aece566 + f865cf8 commit 601bfc4

File tree

10 files changed

+155
-130
lines changed

10 files changed

+155
-130
lines changed

src/index/coinstatsindex.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ bool CoinStatsIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* n
277277

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

283283
do {

src/node/blockstorage.cpp

Lines changed: 47 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,45 @@ bool fHavePruned = false;
2828
bool fPruneMode = false;
2929
uint64_t nPruneTarget = 0;
3030

31+
bool CBlockIndexWorkComparator::operator()(const CBlockIndex* pa, const CBlockIndex* pb) const
32+
{
33+
// First sort by most total work, ...
34+
if (pa->nChainWork > pb->nChainWork) return false;
35+
if (pa->nChainWork < pb->nChainWork) return true;
36+
37+
// ... then by earliest time received, ...
38+
if (pa->nSequenceId < pb->nSequenceId) return false;
39+
if (pa->nSequenceId > pb->nSequenceId) return true;
40+
41+
// Use pointer address as tie breaker (should only happen with blocks
42+
// loaded from disk, as those all have id 0).
43+
if (pa < pb) return false;
44+
if (pa > pb) return true;
45+
46+
// Identical blocks.
47+
return false;
48+
}
49+
50+
bool CBlockIndexHeightOnlyComparator::operator()(const CBlockIndex* pa, const CBlockIndex* pb) const
51+
{
52+
return pa->nHeight < pb->nHeight;
53+
}
54+
3155
static FILE* OpenUndoFile(const FlatFilePos& pos, bool fReadOnly = false);
3256
static FlatFileSeq BlockFileSeq();
3357
static FlatFileSeq UndoFileSeq();
3458

59+
std::vector<CBlockIndex*> BlockManager::GetAllBlockIndices()
60+
{
61+
AssertLockHeld(cs_main);
62+
std::vector<CBlockIndex*> rv;
63+
rv.reserve(m_block_index.size());
64+
for (auto& [_, block_index] : m_block_index) {
65+
rv.push_back(&block_index);
66+
}
67+
return rv;
68+
}
69+
3570
CBlockIndex* BlockManager::LookupBlockIndex(const uint256& hash)
3671
{
3772
AssertLockHeld(cs_main);
@@ -203,55 +238,27 @@ CBlockIndex* BlockManager::InsertBlockIndex(const uint256& hash)
203238
return nullptr;
204239
}
205240

206-
// Return existing or create new
207-
auto [mi, inserted] = m_block_index.try_emplace(hash);
241+
const auto [mi, inserted]{m_block_index.try_emplace(hash)};
208242
CBlockIndex* pindex = &(*mi).second;
209243
if (inserted) {
210244
pindex->phashBlock = &((*mi).first);
211245
}
212246
return pindex;
213247
}
214248

215-
bool BlockManager::LoadBlockIndex(
216-
const Consensus::Params& consensus_params,
217-
ChainstateManager& chainman)
249+
bool BlockManager::LoadBlockIndex(const Consensus::Params& consensus_params)
218250
{
219251
if (!m_block_tree_db->LoadBlockIndexGuts(consensus_params, [this](const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return this->InsertBlockIndex(hash); })) {
220252
return false;
221253
}
222254

223255
// Calculate nChainWork
224-
std::vector<std::pair<int, CBlockIndex*>> vSortedByHeight;
225-
vSortedByHeight.reserve(m_block_index.size());
226-
for (auto& [_, block_index] : m_block_index) {
227-
CBlockIndex* pindex = &block_index;
228-
vSortedByHeight.push_back(std::make_pair(pindex->nHeight, pindex));
229-
}
230-
sort(vSortedByHeight.begin(), vSortedByHeight.end());
231-
232-
// Find start of assumed-valid region.
233-
int first_assumed_valid_height = std::numeric_limits<int>::max();
234-
235-
for (const auto& [height, block] : vSortedByHeight) {
236-
if (block->IsAssumedValid()) {
237-
auto chainstates = chainman.GetAll();
238-
239-
// If we encounter an assumed-valid block index entry, ensure that we have
240-
// one chainstate that tolerates assumed-valid entries and another that does
241-
// not (i.e. the background validation chainstate), since assumed-valid
242-
// entries should always be pending validation by a fully-validated chainstate.
243-
auto any_chain = [&](auto fnc) { return std::any_of(chainstates.cbegin(), chainstates.cend(), fnc); };
244-
assert(any_chain([](auto chainstate) { return chainstate->reliesOnAssumedValid(); }));
245-
assert(any_chain([](auto chainstate) { return !chainstate->reliesOnAssumedValid(); }));
246-
247-
first_assumed_valid_height = height;
248-
break;
249-
}
250-
}
256+
std::vector<CBlockIndex*> vSortedByHeight{GetAllBlockIndices()};
257+
std::sort(vSortedByHeight.begin(), vSortedByHeight.end(),
258+
CBlockIndexHeightOnlyComparator());
251259

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

@@ -275,43 +282,6 @@ bool BlockManager::LoadBlockIndex(
275282
pindex->nStatus |= BLOCK_FAILED_CHILD;
276283
m_dirty_blockindex.insert(pindex);
277284
}
278-
if (pindex->IsAssumedValid() ||
279-
(pindex->IsValid(BLOCK_VALID_TRANSACTIONS) &&
280-
(pindex->HaveTxsDownloaded() || pindex->pprev == nullptr))) {
281-
282-
// Fill each chainstate's block candidate set. Only add assumed-valid
283-
// blocks to the tip candidate set if the chainstate is allowed to rely on
284-
// assumed-valid blocks.
285-
//
286-
// If all setBlockIndexCandidates contained the assumed-valid blocks, the
287-
// background chainstate's ActivateBestChain() call would add assumed-valid
288-
// blocks to the chain (based on how FindMostWorkChain() works). Obviously
289-
// we don't want this since the purpose of the background validation chain
290-
// is to validate assued-valid blocks.
291-
//
292-
// Note: This is considering all blocks whose height is greater or equal to
293-
// the first assumed-valid block to be assumed-valid blocks, and excluding
294-
// them from the background chainstate's setBlockIndexCandidates set. This
295-
// does mean that some blocks which are not technically assumed-valid
296-
// (later blocks on a fork beginning before the first assumed-valid block)
297-
// might not get added to the background chainstate, but this is ok,
298-
// because they will still be attached to the active chainstate if they
299-
// actually contain more work.
300-
//
301-
// Instead of this height-based approach, an earlier attempt was made at
302-
// detecting "holistically" whether the block index under consideration
303-
// relied on an assumed-valid ancestor, but this proved to be too slow to
304-
// be practical.
305-
for (CChainState* chainstate : chainman.GetAll()) {
306-
if (chainstate->reliesOnAssumedValid() ||
307-
pindex->nHeight < first_assumed_valid_height) {
308-
chainstate->setBlockIndexCandidates.insert(pindex);
309-
}
310-
}
311-
}
312-
if (pindex->nStatus & BLOCK_FAILED_MASK && (!chainman.m_best_invalid || pindex->nChainWork > chainman.m_best_invalid->nChainWork)) {
313-
chainman.m_best_invalid = pindex;
314-
}
315285
if (pindex->pprev) {
316286
pindex->BuildSkip();
317287
}
@@ -355,9 +325,9 @@ bool BlockManager::WriteBlockIndexDB()
355325
return true;
356326
}
357327

358-
bool BlockManager::LoadBlockIndexDB(ChainstateManager& chainman)
328+
bool BlockManager::LoadBlockIndexDB()
359329
{
360-
if (!LoadBlockIndex(::Params().GetConsensus(), chainman)) {
330+
if (!LoadBlockIndex(::Params().GetConsensus())) {
361331
return false;
362332
}
363333

@@ -382,9 +352,8 @@ bool BlockManager::LoadBlockIndexDB(ChainstateManager& chainman)
382352
LogPrintf("Checking all blk files are present...\n");
383353
std::set<int> setBlkDataFiles;
384354
for (const auto& [_, block_index] : m_block_index) {
385-
const CBlockIndex* pindex = &block_index;
386-
if (pindex->nStatus & BLOCK_HAVE_DATA) {
387-
setBlkDataFiles.insert(pindex->nFile);
355+
if (block_index.nStatus & BLOCK_HAVE_DATA) {
356+
setBlkDataFiles.insert(block_index.nFile);
388357
}
389358
}
390359
for (std::set<int>::iterator it = setBlkDataFiles.begin(); it != setBlkDataFiles.end(); it++) {
@@ -408,13 +377,13 @@ bool BlockManager::LoadBlockIndexDB(ChainstateManager& chainman)
408377
return true;
409378
}
410379

411-
CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)
380+
const CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)
412381
{
413382
const MapCheckpoints& checkpoints = data.mapCheckpoints;
414383

415384
for (const MapCheckpoints::value_type& i : reverse_iterate(checkpoints)) {
416385
const uint256& hash = i.second;
417-
CBlockIndex* pindex = LookupBlockIndex(hash);
386+
const CBlockIndex* pindex = LookupBlockIndex(hash);
418387
if (pindex) {
419388
return pindex;
420389
}

src/node/blockstorage.h

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ struct CBlockIndexWorkComparator {
6262
bool operator()(const CBlockIndex* pa, const CBlockIndex* pb) const;
6363
};
6464

65+
struct CBlockIndexHeightOnlyComparator {
66+
/* Only compares the height of two block indices, doesn't try to tie-break */
67+
bool operator()(const CBlockIndex* pa, const CBlockIndex* pb) const;
68+
};
69+
6570
/**
6671
* Maintains a tree of blocks (stored in `m_block_index`) which is consulted
6772
* to determine where the most-work tip is.
@@ -118,6 +123,8 @@ class BlockManager
118123
public:
119124
BlockMap m_block_index GUARDED_BY(cs_main);
120125

126+
std::vector<CBlockIndex*> GetAllBlockIndices() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
127+
121128
/**
122129
* All pairs A->B, where A (or one of its ancestors) misses transactions, but B has transactions.
123130
* Pruned nodes may have entries where B is missing data.
@@ -127,16 +134,15 @@ class BlockManager
127134
std::unique_ptr<CBlockTreeDB> m_block_tree_db GUARDED_BY(::cs_main);
128135

129136
bool WriteBlockIndexDB() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
130-
bool LoadBlockIndexDB(ChainstateManager& chainman) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
137+
bool LoadBlockIndexDB() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
131138

132139
/**
133140
* Load the blocktree off disk and into memory. Populate certain metadata
134141
* per index entry (nStatus, nChainWork, nTimeMax, etc.) as well as peripheral
135142
* collections like m_dirty_blockindex.
136143
*/
137-
bool LoadBlockIndex(
138-
const Consensus::Params& consensus_params,
139-
ChainstateManager& chainman) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
144+
bool LoadBlockIndex(const Consensus::Params& consensus_params)
145+
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
140146

141147
/** Clear all data members. */
142148
void Unload() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -163,7 +169,7 @@ class BlockManager
163169
uint64_t CalculateCurrentUsage();
164170

165171
//! Returns last CBlockIndex* that is a checkpoint
166-
CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
172+
const CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
167173

168174
~BlockManager()
169175
{

src/node/interfaces.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,7 @@ class ChainImpl : public Chain
490490
{
491491
LOCK(cs_main);
492492
const CChainState& active = Assert(m_node.chainman)->ActiveChainstate();
493-
if (CBlockIndex* fork = active.FindForkInGlobalIndex(locator)) {
493+
if (const CBlockIndex* fork = active.FindForkInGlobalIndex(locator)) {
494494
return fork->nHeight;
495495
}
496496
return std::nullopt;
@@ -557,7 +557,7 @@ class ChainImpl : public Chain
557557
// used to limit the range, and passing min_height that's too low or
558558
// max_height that's too high will not crash or change the result.
559559
LOCK(::cs_main);
560-
if (CBlockIndex* block = chainman().m_blockman.LookupBlockIndex(block_hash)) {
560+
if (const CBlockIndex* block = chainman().m_blockman.LookupBlockIndex(block_hash)) {
561561
if (max_height && block->nHeight >= *max_height) block = block->GetAncestor(*max_height);
562562
for (; block->nStatus & BLOCK_HAVE_DATA; block = block->pprev) {
563563
// Check pprev to not segfault if min_height is too low

src/node/miner.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ void RegenerateCommitments(CBlock& block, ChainstateManager& chainman)
5050
tx.vout.erase(tx.vout.begin() + GetWitnessCommitmentIndex(block));
5151
block.vtx.at(0) = MakeTransactionRef(tx);
5252

53-
CBlockIndex* prev_block = WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock));
53+
const CBlockIndex* prev_block = WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock));
5454
GenerateCoinbaseCommitment(block, prev_block, Params().GetConsensus());
5555

5656
block.hashMerkleRoot = BlockMerkleRoot(block);

src/rest.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,8 +284,8 @@ static bool rest_block(const std::any& context,
284284
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
285285

286286
CBlock block;
287-
CBlockIndex* pblockindex = nullptr;
288-
CBlockIndex* tip = nullptr;
287+
const CBlockIndex* pblockindex = nullptr;
288+
const CBlockIndex* tip = nullptr;
289289
{
290290
ChainstateManager* maybe_chainman = GetChainman(context, req);
291291
if (!maybe_chainman) return false;

src/rpc/blockchain.cpp

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ static int ComputeNextBlockAndDepth(const CBlockIndex* tip, const CBlockIndex* b
104104
return blockindex == tip ? 1 : -1;
105105
}
106106

107-
CBlockIndex* ParseHashOrHeight(const UniValue& param, ChainstateManager& chainman) {
107+
static const CBlockIndex* ParseHashOrHeight(const UniValue& param, ChainstateManager& chainman)
108+
{
108109
LOCK(::cs_main);
109110
CChain& active_chain = chainman.ActiveChain();
110111

@@ -121,7 +122,7 @@ CBlockIndex* ParseHashOrHeight(const UniValue& param, ChainstateManager& chainma
121122
return active_chain[height];
122123
} else {
123124
const uint256 hash{ParseHashV(param, "hash_or_height")};
124-
CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash);
125+
const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash);
125126

126127
if (!pindex) {
127128
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
@@ -488,7 +489,7 @@ static RPCHelpMan getblockhash()
488489
if (nHeight < 0 || nHeight > active_chain.Height())
489490
throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range");
490491

491-
CBlockIndex* pblockindex = active_chain[nHeight];
492+
const CBlockIndex* pblockindex = active_chain[nHeight];
492493
return pblockindex->GetBlockHash().GetHex();
493494
},
494495
};
@@ -766,7 +767,7 @@ static RPCHelpMan pruneblockchain()
766767
// too low to be a block time (corresponds to timestamp from Sep 2001).
767768
if (heightParam > 1000000000) {
768769
// Add a 2 hour buffer to include blocks which might have had old timestamps
769-
CBlockIndex* pindex = active_chain.FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW, 0);
770+
const CBlockIndex* pindex = active_chain.FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW, 0);
770771
if (!pindex) {
771772
throw JSONRPCError(RPC_INVALID_PARAMETER, "Could not find block with at least the specified timestamp.");
772773
}
@@ -860,7 +861,7 @@ static RPCHelpMan gettxoutsetinfo()
860861
{
861862
UniValue ret(UniValue::VOBJ);
862863

863-
CBlockIndex* pindex{nullptr};
864+
const CBlockIndex* pindex{nullptr};
864865
const CoinStatsHashType hash_type{request.params[0].isNull() ? CoinStatsHashType::HASH_SERIALIZED : ParseHashType(request.params[0].get_str())};
865866
CCoinsStats stats{hash_type};
866867
stats.index_requested = request.params[2].isNull() || request.params[2].get_bool();
@@ -1764,7 +1765,7 @@ static RPCHelpMan getblockstats()
17641765
{
17651766
ChainstateManager& chainman = EnsureAnyChainman(request.context);
17661767
LOCK(cs_main);
1767-
CBlockIndex* pindex{ParseHashOrHeight(request.params[0], chainman)};
1768+
const CBlockIndex* pindex{ParseHashOrHeight(request.params[0], chainman)};
17681769
CHECK_NONFATAL(pindex != nullptr);
17691770

17701771
std::set<std::string> stats;
@@ -2124,7 +2125,7 @@ static RPCHelpMan scantxoutset()
21242125
g_should_abort_scan = false;
21252126
int64_t count = 0;
21262127
std::unique_ptr<CCoinsViewCursor> pcursor;
2127-
CBlockIndex* tip;
2128+
const CBlockIndex* tip;
21282129
NodeContext& node = EnsureAnyNodeContext(request.context);
21292130
{
21302131
ChainstateManager& chainman = EnsureChainman(node);
@@ -2312,7 +2313,7 @@ UniValue CreateUTXOSnapshot(
23122313
{
23132314
std::unique_ptr<CCoinsViewCursor> pcursor;
23142315
CCoinsStats stats{CoinStatsHashType::HASH_SERIALIZED};
2315-
CBlockIndex* tip;
2316+
const CBlockIndex* tip;
23162317

23172318
{
23182319
// We need to lock cs_main to ensure that the coinsdb isn't written to

src/rpc/rawtransaction.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue&
6767
LOCK(cs_main);
6868

6969
entry.pushKV("blockhash", hashBlock.GetHex());
70-
CBlockIndex* pindex = active_chainstate.m_blockman.LookupBlockIndex(hashBlock);
70+
const CBlockIndex* pindex = active_chainstate.m_blockman.LookupBlockIndex(hashBlock);
7171
if (pindex) {
7272
if (active_chainstate.m_chain.Contains(pindex)) {
7373
entry.pushKV("confirmations", 1 + active_chainstate.m_chain.Height() - pindex->nHeight);
@@ -207,7 +207,7 @@ static RPCHelpMan getrawtransaction()
207207

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

212212
if (hash == Params().GenesisBlock().hashMerkleRoot) {
213213
// Special exception for the genesis block coinbase transaction
@@ -302,7 +302,7 @@ static RPCHelpMan gettxoutproof()
302302
}
303303
}
304304

305-
CBlockIndex* pblockindex = nullptr;
305+
const CBlockIndex* pblockindex = nullptr;
306306
uint256 hashBlock;
307307
ChainstateManager& chainman = EnsureAnyChainman(request.context);
308308
if (!request.params[1].isNull()) {

0 commit comments

Comments
 (0)