Skip to content

Commit 5e49b2a

Browse files
author
MarcoFalke
committed
Merge bitcoin/bitcoin#24050: validation: Give m_block_index ownership of CBlockIndexs
6c23c41 refactor: Rewrite AddToBlockIndex with try_emplace (Carl Dong) c05cf7a style: Modernize range-based loops over m_block_index (Carl Dong) c2a1655 style-only: Use using instead of typedef for BlockMap (Carl Dong) dd79dad refactor: Rewrite InsertBlockIndex with try_emplace (Carl Dong) 531dce0 tests: Remove now-unnecessary manual Unload's (Carl Dong) bec86ae blockstorage: Make m_block_index own CBlockIndex's (Carl Dong) Pull request description: Part of: #24303 Split off from: #22564 ``` Instead of having CBlockIndex's live on the heap, which requires manual memory management, have them be owned by m_block_index. This means that they will live and die with BlockManager. ``` The second commit demonstrates how this makes calls to `Unload()` to satisfy the address sanitizer unnecessary. ACKs for top commit: ajtowns: ACK 6c23c41 MarcoFalke: re-ACK 6c23c41 🎨 Tree-SHA512: 81b2b5119be27cc0f8a9457b11da60cc60930315d2a5be36be89fe253d32073ffe622348ff153114b9b3212197bddbc791810913a43811b33cc58e7162bd105b
2 parents b9894a1 + 6c23c41 commit 5e49b2a

File tree

7 files changed

+61
-70
lines changed

7 files changed

+61
-70
lines changed

src/node/blockstorage.cpp

Lines changed: 27 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -32,35 +32,39 @@ static FILE* OpenUndoFile(const FlatFilePos& pos, bool fReadOnly = false);
3232
static FlatFileSeq BlockFileSeq();
3333
static FlatFileSeq UndoFileSeq();
3434

35-
CBlockIndex* BlockManager::LookupBlockIndex(const uint256& hash) const
35+
CBlockIndex* BlockManager::LookupBlockIndex(const uint256& hash)
36+
{
37+
AssertLockHeld(cs_main);
38+
BlockMap::iterator it = m_block_index.find(hash);
39+
return it == m_block_index.end() ? nullptr : &it->second;
40+
}
41+
42+
const CBlockIndex* BlockManager::LookupBlockIndex(const uint256& hash) const
3643
{
3744
AssertLockHeld(cs_main);
3845
BlockMap::const_iterator it = m_block_index.find(hash);
39-
return it == m_block_index.end() ? nullptr : it->second;
46+
return it == m_block_index.end() ? nullptr : &it->second;
4047
}
4148

4249
CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block)
4350
{
4451
AssertLockHeld(cs_main);
4552

46-
// Check for duplicate
47-
uint256 hash = block.GetHash();
48-
BlockMap::iterator it = m_block_index.find(hash);
49-
if (it != m_block_index.end()) {
50-
return it->second;
53+
auto [mi, inserted] = m_block_index.try_emplace(block.GetHash(), block);
54+
if (!inserted) {
55+
return &mi->second;
5156
}
57+
CBlockIndex* pindexNew = &(*mi).second;
5258

53-
// Construct new block index object
54-
CBlockIndex* pindexNew = new CBlockIndex(block);
5559
// We assign the sequence id to blocks only when the full data is available,
5660
// to avoid miners withholding blocks but broadcasting headers, to get a
5761
// competitive advantage.
5862
pindexNew->nSequenceId = 0;
59-
BlockMap::iterator mi = m_block_index.insert(std::make_pair(hash, pindexNew)).first;
63+
6064
pindexNew->phashBlock = &((*mi).first);
6165
BlockMap::iterator miPrev = m_block_index.find(block.hashPrevBlock);
6266
if (miPrev != m_block_index.end()) {
63-
pindexNew->pprev = (*miPrev).second;
67+
pindexNew->pprev = &(*miPrev).second;
6468
pindexNew->nHeight = pindexNew->pprev->nHeight + 1;
6569
pindexNew->BuildSkip();
6670
}
@@ -80,8 +84,8 @@ void BlockManager::PruneOneBlockFile(const int fileNumber)
8084
AssertLockHeld(cs_main);
8185
LOCK(cs_LastBlockFile);
8286

83-
for (const auto& entry : m_block_index) {
84-
CBlockIndex* pindex = entry.second;
87+
for (auto& entry : m_block_index) {
88+
CBlockIndex* pindex = &entry.second;
8589
if (pindex->nFile == fileNumber) {
8690
pindex->nStatus &= ~BLOCK_HAVE_DATA;
8791
pindex->nStatus &= ~BLOCK_HAVE_UNDO;
@@ -199,18 +203,13 @@ CBlockIndex* BlockManager::InsertBlockIndex(const uint256& hash)
199203
return nullptr;
200204
}
201205

202-
// Return existing
203-
BlockMap::iterator mi = m_block_index.find(hash);
204-
if (mi != m_block_index.end()) {
205-
return (*mi).second;
206+
// Return existing or create new
207+
auto [mi, inserted] = m_block_index.try_emplace(hash);
208+
CBlockIndex* pindex = &(*mi).second;
209+
if (inserted) {
210+
pindex->phashBlock = &((*mi).first);
206211
}
207-
208-
// Create new
209-
CBlockIndex* pindexNew = new CBlockIndex();
210-
mi = m_block_index.insert(std::make_pair(hash, pindexNew)).first;
211-
pindexNew->phashBlock = &((*mi).first);
212-
213-
return pindexNew;
212+
return pindex;
214213
}
215214

216215
bool BlockManager::LoadBlockIndex(
@@ -224,8 +223,8 @@ bool BlockManager::LoadBlockIndex(
224223
// Calculate nChainWork
225224
std::vector<std::pair<int, CBlockIndex*>> vSortedByHeight;
226225
vSortedByHeight.reserve(m_block_index.size());
227-
for (const std::pair<const uint256, CBlockIndex*>& item : m_block_index) {
228-
CBlockIndex* pindex = item.second;
226+
for (auto& [_, block_index] : m_block_index) {
227+
CBlockIndex* pindex = &block_index;
229228
vSortedByHeight.push_back(std::make_pair(pindex->nHeight, pindex));
230229
}
231230
sort(vSortedByHeight.begin(), vSortedByHeight.end());
@@ -327,10 +326,6 @@ void BlockManager::Unload()
327326
{
328327
m_blocks_unlinked.clear();
329328

330-
for (const BlockMap::value_type& entry : m_block_index) {
331-
delete entry.second;
332-
}
333-
334329
m_block_index.clear();
335330

336331
m_blockfile_info.clear();
@@ -386,8 +381,8 @@ bool BlockManager::LoadBlockIndexDB(ChainstateManager& chainman)
386381
// Check presence of blk files
387382
LogPrintf("Checking all blk files are present...\n");
388383
std::set<int> setBlkDataFiles;
389-
for (const std::pair<const uint256, CBlockIndex*>& item : m_block_index) {
390-
CBlockIndex* pindex = item.second;
384+
for (const auto& [_, block_index] : m_block_index) {
385+
const CBlockIndex* pindex = &block_index;
391386
if (pindex->nStatus & BLOCK_HAVE_DATA) {
392387
setBlkDataFiles.insert(pindex->nFile);
393388
}

src/node/blockstorage.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#ifndef BITCOIN_NODE_BLOCKSTORAGE_H
66
#define BITCOIN_NODE_BLOCKSTORAGE_H
77

8+
#include <chain.h>
89
#include <fs.h>
910
#include <protocol.h> // For CMessageHeader::MessageStartChars
1011
#include <sync.h>
@@ -20,7 +21,6 @@ class ArgsManager;
2021
class BlockValidationState;
2122
class CBlock;
2223
class CBlockFileInfo;
23-
class CBlockIndex;
2424
class CBlockUndo;
2525
class CChain;
2626
class CChainParams;
@@ -52,7 +52,11 @@ extern bool fPruneMode;
5252
/** Number of MiB of block files that we're trying to stay below. */
5353
extern uint64_t nPruneTarget;
5454

55-
typedef std::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
55+
// Because validation code takes pointers to the map's CBlockIndex objects, if
56+
// we ever switch to another associative container, we need to either use a
57+
// container that has stable addressing (true of all std associative
58+
// containers), or make the key a `std::unique_ptr<CBlockIndex>`
59+
using BlockMap = std::unordered_map<uint256, CBlockIndex, BlockHasher>;
5660

5761
struct CBlockIndexWorkComparator {
5862
bool operator()(const CBlockIndex* pa, const CBlockIndex* pb) const;
@@ -144,7 +148,8 @@ class BlockManager
144148
//! Mark one block file as pruned (modify associated database entries)
145149
void PruneOneBlockFile(const int fileNumber) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
146150

147-
CBlockIndex* LookupBlockIndex(const uint256& hash) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
151+
CBlockIndex* LookupBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
152+
const CBlockIndex* LookupBlockIndex(const uint256& hash) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
148153

149154
/** Get block file info entry for one block file */
150155
CBlockFileInfo* GetBlockFileInfo(size_t n);

src/rpc/blockchain.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1753,10 +1753,10 @@ static RPCHelpMan getchaintips()
17531753
std::set<const CBlockIndex*> setOrphans;
17541754
std::set<const CBlockIndex*> setPrevs;
17551755

1756-
for (const std::pair<const uint256, CBlockIndex*>& item : chainman.BlockIndex()) {
1757-
if (!active_chain.Contains(item.second)) {
1758-
setOrphans.insert(item.second);
1759-
setPrevs.insert(item.second->pprev);
1756+
for (const auto& [_, block_index] : chainman.BlockIndex()) {
1757+
if (!active_chain.Contains(&block_index)) {
1758+
setOrphans.insert(&block_index);
1759+
setPrevs.insert(block_index.pprev);
17601760
}
17611761
}
17621762

src/test/validation_chainstate_tests.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,6 @@ BOOST_AUTO_TEST_CASE(validation_chainstate_resize_caches)
7272
// The view cache should be empty since we had to destruct to downsize.
7373
BOOST_CHECK(!c1.CoinsTip().HaveCoinInCache(outpoint));
7474
}
75-
76-
// Avoid triggering the address sanitizer.
77-
WITH_LOCK(::cs_main, manager.Unload());
7875
}
7976

8077
//! Test UpdateTip behavior for both active and background chainstates.

src/test/validation_chainstatemanager_tests.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,6 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
9999

100100
// Let scheduler events finish running to avoid accessing memory that is going to be unloaded
101101
SyncWithValidationInterfaceQueue();
102-
103-
WITH_LOCK(::cs_main, manager.Unload());
104102
}
105103

106104
//! Test rebalancing the caches associated with each chainstate.

src/validation.cpp

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1978,7 +1978,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
19781978
// effectively caching the result of part of the verification.
19791979
BlockMap::const_iterator it = m_blockman.m_block_index.find(hashAssumeValid);
19801980
if (it != m_blockman.m_block_index.end()) {
1981-
if (it->second->GetAncestor(pindex->nHeight) == pindex &&
1981+
if (it->second.GetAncestor(pindex->nHeight) == pindex &&
19821982
pindexBestHeader->GetAncestor(pindex->nHeight) == pindex &&
19831983
pindexBestHeader->nChainWork >= nMinimumChainWork) {
19841984
// This block is a member of the assumed verified chain and an ancestor of the best header.
@@ -3035,8 +3035,8 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, CBlockIndex* pind
30353035

30363036
{
30373037
LOCK(cs_main);
3038-
for (const auto& entry : m_blockman.m_block_index) {
3039-
CBlockIndex *candidate = entry.second;
3038+
for (auto& entry : m_blockman.m_block_index) {
3039+
CBlockIndex* candidate = &entry.second;
30403040
// We don't need to put anything in our active chain into the
30413041
// multimap, because those candidates will be found and considered
30423042
// as we disconnect.
@@ -3133,12 +3133,10 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, CBlockIndex* pind
31333133
// it up here, this should be an essentially unobservable error.
31343134
// Loop back over all block index entries and add any missing entries
31353135
// to setBlockIndexCandidates.
3136-
BlockMap::iterator it = m_blockman.m_block_index.begin();
3137-
while (it != m_blockman.m_block_index.end()) {
3138-
if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && !setBlockIndexCandidates.value_comp()(it->second, m_chain.Tip())) {
3139-
setBlockIndexCandidates.insert(it->second);
3136+
for (auto& [_, block_index] : m_blockman.m_block_index) {
3137+
if (block_index.IsValid(BLOCK_VALID_TRANSACTIONS) && block_index.HaveTxsDownloaded() && !setBlockIndexCandidates.value_comp()(&block_index, m_chain.Tip())) {
3138+
setBlockIndexCandidates.insert(&block_index);
31403139
}
3141-
it++;
31423140
}
31433141

31443142
InvalidChainFound(to_mark_failed);
@@ -3157,21 +3155,19 @@ void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
31573155
int nHeight = pindex->nHeight;
31583156

31593157
// Remove the invalidity flag from this block and all its descendants.
3160-
BlockMap::iterator it = m_blockman.m_block_index.begin();
3161-
while (it != m_blockman.m_block_index.end()) {
3162-
if (!it->second->IsValid() && it->second->GetAncestor(nHeight) == pindex) {
3163-
it->second->nStatus &= ~BLOCK_FAILED_MASK;
3164-
m_blockman.m_dirty_blockindex.insert(it->second);
3165-
if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && setBlockIndexCandidates.value_comp()(m_chain.Tip(), it->second)) {
3166-
setBlockIndexCandidates.insert(it->second);
3158+
for (auto& [_, block_index] : m_blockman.m_block_index) {
3159+
if (!block_index.IsValid() && block_index.GetAncestor(nHeight) == pindex) {
3160+
block_index.nStatus &= ~BLOCK_FAILED_MASK;
3161+
m_blockman.m_dirty_blockindex.insert(&block_index);
3162+
if (block_index.IsValid(BLOCK_VALID_TRANSACTIONS) && block_index.HaveTxsDownloaded() && setBlockIndexCandidates.value_comp()(m_chain.Tip(), &block_index)) {
3163+
setBlockIndexCandidates.insert(&block_index);
31673164
}
3168-
if (it->second == m_chainman.m_best_invalid) {
3165+
if (&block_index == m_chainman.m_best_invalid) {
31693166
// Reset invalid block marker if it was pointing to one of those.
31703167
m_chainman.m_best_invalid = nullptr;
31713168
}
3172-
m_chainman.m_failed_blocks.erase(it->second);
3169+
m_chainman.m_failed_blocks.erase(&block_index);
31733170
}
3174-
it++;
31753171
}
31763172

31773173
// Remove the invalidity flag from all ancestors too.
@@ -3500,7 +3496,7 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
35003496
if (hash != chainparams.GetConsensus().hashGenesisBlock) {
35013497
if (miSelf != m_blockman.m_block_index.end()) {
35023498
// Block header is already known.
3503-
CBlockIndex* pindex = miSelf->second;
3499+
CBlockIndex* pindex = &(miSelf->second);
35043500
if (ppindex)
35053501
*ppindex = pindex;
35063502
if (pindex->nStatus & BLOCK_FAILED_MASK) {
@@ -3522,7 +3518,7 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
35223518
LogPrint(BCLog::VALIDATION, "%s: %s prev block not found\n", __func__, hash.ToString());
35233519
return state.Invalid(BlockValidationResult::BLOCK_MISSING_PREV, "prev-blk-not-found");
35243520
}
3525-
pindexPrev = (*mi).second;
3521+
pindexPrev = &((*mi).second);
35263522
if (pindexPrev->nStatus & BLOCK_FAILED_MASK) {
35273523
LogPrint(BCLog::VALIDATION, "%s: %s prev block invalid\n", __func__, hash.ToString());
35283524
return state.Invalid(BlockValidationResult::BLOCK_INVALID_PREV, "bad-prevblk");
@@ -3994,13 +3990,13 @@ bool CChainState::ReplayBlocks()
39943990
if (m_blockman.m_block_index.count(hashHeads[0]) == 0) {
39953991
return error("ReplayBlocks(): reorganization to unknown block requested");
39963992
}
3997-
pindexNew = m_blockman.m_block_index[hashHeads[0]];
3993+
pindexNew = &(m_blockman.m_block_index[hashHeads[0]]);
39983994

39993995
if (!hashHeads[1].IsNull()) { // The old tip is allowed to be 0, indicating it's the first flush.
40003996
if (m_blockman.m_block_index.count(hashHeads[1]) == 0) {
40013997
return error("ReplayBlocks(): reorganization from unknown block requested");
40023998
}
4003-
pindexOld = m_blockman.m_block_index[hashHeads[1]];
3999+
pindexOld = &(m_blockman.m_block_index[hashHeads[1]]);
40044000
pindexFork = LastCommonAncestor(pindexOld, pindexNew);
40054001
assert(pindexFork != nullptr);
40064002
}
@@ -4267,8 +4263,8 @@ void CChainState::CheckBlockIndex()
42674263

42684264
// Build forward-pointing map of the entire block tree.
42694265
std::multimap<CBlockIndex*,CBlockIndex*> forward;
4270-
for (const std::pair<const uint256, CBlockIndex*>& entry : m_blockman.m_block_index) {
4271-
forward.insert(std::make_pair(entry.second->pprev, entry.second));
4266+
for (auto& [_, block_index] : m_blockman.m_block_index) {
4267+
forward.emplace(block_index.pprev, &block_index);
42724268
}
42734269

42744270
assert(forward.size() == m_blockman.m_block_index.size());

src/wallet/test/wallet_tests.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -367,10 +367,10 @@ static int64_t AddTx(ChainstateManager& chainman, CWallet& wallet, uint32_t lock
367367
CBlockIndex* block = nullptr;
368368
if (blockTime > 0) {
369369
LOCK(cs_main);
370-
auto inserted = chainman.BlockIndex().emplace(GetRandHash(), new CBlockIndex);
370+
auto inserted = chainman.BlockIndex().emplace(std::piecewise_construct, std::make_tuple(GetRandHash()), std::make_tuple());
371371
assert(inserted.second);
372372
const uint256& hash = inserted.first->first;
373-
block = inserted.first->second;
373+
block = &inserted.first->second;
374374
block->nTime = blockTime;
375375
block->phashBlock = &hash;
376376
state = TxStateConfirmed{hash, block->nHeight, /*position_in_block=*/0};

0 commit comments

Comments
 (0)