Merge bitcoin/bitcoin#24050: validation: Give m_block_index ownership of CBlockIndexs

6c23c41561 refactor: Rewrite AddToBlockIndex with try_emplace (Carl Dong)
c05cf7aa1e style: Modernize range-based loops over m_block_index (Carl Dong)
c2a1655799 style-only: Use using instead of typedef for BlockMap (Carl Dong)
dd79dad175 refactor: Rewrite InsertBlockIndex with try_emplace (Carl Dong)
531dce0347 tests: Remove now-unnecessary manual Unload's (Carl Dong)
bec86ae326 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 6c23c41561
  MarcoFalke:
    re-ACK 6c23c41561 🎨

Tree-SHA512: 81b2b5119be27cc0f8a9457b11da60cc60930315d2a5be36be89fe253d32073ffe622348ff153114b9b3212197bddbc791810913a43811b33cc58e7162bd105b
This commit is contained in:
MarcoFalke committed 2022-03-07 13:15:27 +01:00
commit 5e49b2a252
7 files changed
+61 -70

No files matched your search

+27 -32
View File
@@ -32,35 +32,39 @@ static FILE* OpenUndoFile(const FlatFilePos& pos, bool fReadOnly = false);
static FlatFileSeq BlockFileSeq(); static FlatFileSeq BlockFileSeq();
static FlatFileSeq UndoFileSeq(); static FlatFileSeq UndoFileSeq();
CBlockIndex* BlockManager::LookupBlockIndex(const uint256& hash) const CBlockIndex* BlockManager::LookupBlockIndex(const uint256& hash)
{
AssertLockHeld(cs_main);
BlockMap::iterator it = m_block_index.find(hash);
return it == m_block_index.end() ? nullptr : &it->second;
}
const CBlockIndex* BlockManager::LookupBlockIndex(const uint256& hash) const
{ {
AssertLockHeld(cs_main); AssertLockHeld(cs_main);
BlockMap::const_iterator it = m_block_index.find(hash); BlockMap::const_iterator it = m_block_index.find(hash);
return it == m_block_index.end() ? nullptr : it->second; return it == m_block_index.end() ? nullptr : &it->second;
} }
CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block) CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block)
{ {
AssertLockHeld(cs_main); AssertLockHeld(cs_main);
// Check for duplicate auto [mi, inserted] = m_block_index.try_emplace(block.GetHash(), block);
uint256 hash = block.GetHash(); if (!inserted) {
BlockMap::iterator it = m_block_index.find(hash); return &mi->second;
if (it != m_block_index.end()) {
return it->second;
} }
CBlockIndex* pindexNew = &(*mi).second;
// Construct new block index object
CBlockIndex* pindexNew = new CBlockIndex(block);
// We assign the sequence id to blocks only when the full data is available, // We assign the sequence id to blocks only when the full data is available,
// 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 = m_block_index.insert(std::make_pair(hash, pindexNew)).first;
pindexNew->phashBlock = &((*mi).first); pindexNew->phashBlock = &((*mi).first);
BlockMap::iterator miPrev = m_block_index.find(block.hashPrevBlock); BlockMap::iterator miPrev = m_block_index.find(block.hashPrevBlock);
if (miPrev != m_block_index.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;
pindexNew->BuildSkip(); pindexNew->BuildSkip();
} }
@@ -80,8 +84,8 @@ void BlockManager::PruneOneBlockFile(const int fileNumber)
AssertLockHeld(cs_main); AssertLockHeld(cs_main);
LOCK(cs_LastBlockFile); LOCK(cs_LastBlockFile);
for (const auto& entry : m_block_index) { for (auto& entry : 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;
pindex->nStatus &= ~BLOCK_HAVE_UNDO; pindex->nStatus &= ~BLOCK_HAVE_UNDO;
@@ -199,18 +203,13 @@ CBlockIndex* BlockManager::InsertBlockIndex(const uint256& hash)
return nullptr; return nullptr;
} }
// Return existing // Return existing or create new
BlockMap::iterator mi = m_block_index.find(hash); auto [mi, inserted] = m_block_index.try_emplace(hash);
if (mi != m_block_index.end()) { CBlockIndex* pindex = &(*mi).second;
return (*mi).second; if (inserted) {
pindex->phashBlock = &((*mi).first);
} }
return pindex;
// Create new
CBlockIndex* pindexNew = new CBlockIndex();
mi = m_block_index.insert(std::make_pair(hash, pindexNew)).first;
pindexNew->phashBlock = &((*mi).first);
return pindexNew;
} }
bool BlockManager::LoadBlockIndex( bool BlockManager::LoadBlockIndex(
@@ -224,8 +223,8 @@ bool BlockManager::LoadBlockIndex(
// Calculate nChainWork // Calculate nChainWork
std::vector<std::pair<int, CBlockIndex*>> vSortedByHeight; std::vector<std::pair<int, CBlockIndex*>> vSortedByHeight;
vSortedByHeight.reserve(m_block_index.size()); vSortedByHeight.reserve(m_block_index.size());
for (const std::pair<const uint256, CBlockIndex*>& item : m_block_index) { for (auto& [_, block_index] : m_block_index) {
CBlockIndex* pindex = item.second; CBlockIndex* pindex = &block_index;
vSortedByHeight.push_back(std::make_pair(pindex->nHeight, pindex)); vSortedByHeight.push_back(std::make_pair(pindex->nHeight, pindex));
} }
sort(vSortedByHeight.begin(), vSortedByHeight.end()); sort(vSortedByHeight.begin(), vSortedByHeight.end());
@@ -327,10 +326,6 @@ void BlockManager::Unload()
{ {
m_blocks_unlinked.clear(); m_blocks_unlinked.clear();
for (const BlockMap::value_type& entry : m_block_index) {
delete entry.second;
}
m_block_index.clear(); m_block_index.clear();
m_blockfile_info.clear(); m_blockfile_info.clear();
@@ -386,8 +381,8 @@ bool BlockManager::LoadBlockIndexDB(ChainstateManager& chainman)
// Check presence of blk files // Check presence of blk files
LogPrintf("Checking all blk files are present...\n"); LogPrintf("Checking all blk files are present...\n");
std::set<int> setBlkDataFiles; std::set<int> setBlkDataFiles;
for (const std::pair<const uint256, CBlockIndex*>& item : m_block_index) { for (const auto& [_, block_index] : m_block_index) {
CBlockIndex* pindex = item.second; const CBlockIndex* pindex = &block_index;
if (pindex->nStatus & BLOCK_HAVE_DATA) { if (pindex->nStatus & BLOCK_HAVE_DATA) {
setBlkDataFiles.insert(pindex->nFile); setBlkDataFiles.insert(pindex->nFile);
} }
+8 -3
View File
@@ -5,6 +5,7 @@
#ifndef BITCOIN_NODE_BLOCKSTORAGE_H #ifndef BITCOIN_NODE_BLOCKSTORAGE_H
#define BITCOIN_NODE_BLOCKSTORAGE_H #define BITCOIN_NODE_BLOCKSTORAGE_H
#include <chain.h>
#include <fs.h> #include <fs.h>
#include <protocol.h> // For CMessageHeader::MessageStartChars #include <protocol.h> // For CMessageHeader::MessageStartChars
#include <sync.h> #include <sync.h>
@@ -20,7 +21,6 @@ class ArgsManager;
class BlockValidationState; class BlockValidationState;
class CBlock; class CBlock;
class CBlockFileInfo; class CBlockFileInfo;
class CBlockIndex;
class CBlockUndo; class CBlockUndo;
class CChain; class CChain;
class CChainParams; class CChainParams;
@@ -52,7 +52,11 @@ extern bool fPruneMode;
/** Number of MiB of block files that we're trying to stay below. */ /** Number of MiB of block files that we're trying to stay below. */
extern uint64_t nPruneTarget; extern uint64_t nPruneTarget;
typedef std::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap; // Because validation code takes pointers to the map's CBlockIndex objects, if
// we ever switch to another associative container, we need to either use a
// container that has stable addressing (true of all std associative
// containers), or make the key a `std::unique_ptr<CBlockIndex>`
using BlockMap = std::unordered_map<uint256, CBlockIndex, BlockHasher>;
struct CBlockIndexWorkComparator { struct CBlockIndexWorkComparator {
bool operator()(const CBlockIndex* pa, const CBlockIndex* pb) const; bool operator()(const CBlockIndex* pa, const CBlockIndex* pb) const;
@@ -144,7 +148,8 @@ public:
//! Mark one block file as pruned (modify associated database entries) //! Mark one block file as pruned (modify associated database entries)
void PruneOneBlockFile(const int fileNumber) EXCLUSIVE_LOCKS_REQUIRED(cs_main); void PruneOneBlockFile(const int fileNumber) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
CBlockIndex* LookupBlockIndex(const uint256& hash) const EXCLUSIVE_LOCKS_REQUIRED(cs_main); CBlockIndex* LookupBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
const CBlockIndex* LookupBlockIndex(const uint256& hash) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Get block file info entry for one block file */ /** Get block file info entry for one block file */
CBlockFileInfo* GetBlockFileInfo(size_t n); CBlockFileInfo* GetBlockFileInfo(size_t n);
+4 -4
View File
@@ -1753,10 +1753,10 @@ static RPCHelpMan getchaintips()
std::set<const CBlockIndex*> setOrphans; std::set<const CBlockIndex*> setOrphans;
std::set<const CBlockIndex*> setPrevs; std::set<const CBlockIndex*> setPrevs;
for (const std::pair<const uint256, CBlockIndex*>& item : chainman.BlockIndex()) { for (const auto& [_, block_index] : chainman.BlockIndex()) {
if (!active_chain.Contains(item.second)) { if (!active_chain.Contains(&block_index)) {
setOrphans.insert(item.second); setOrphans.insert(&block_index);
setPrevs.insert(item.second->pprev); setPrevs.insert(block_index.pprev);
} }
} }
-3
View File
@@ -72,9 +72,6 @@ BOOST_AUTO_TEST_CASE(validation_chainstate_resize_caches)
// The view cache should be empty since we had to destruct to downsize. // The view cache should be empty since we had to destruct to downsize.
BOOST_CHECK(!c1.CoinsTip().HaveCoinInCache(outpoint)); BOOST_CHECK(!c1.CoinsTip().HaveCoinInCache(outpoint));
} }
// Avoid triggering the address sanitizer.
WITH_LOCK(::cs_main, manager.Unload());
} }
//! Test UpdateTip behavior for both active and background chainstates. //! Test UpdateTip behavior for both active and background chainstates.
@@ -99,8 +99,6 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
// Let scheduler events finish running to avoid accessing memory that is going to be unloaded // Let scheduler events finish running to avoid accessing memory that is going to be unloaded
SyncWithValidationInterfaceQueue(); SyncWithValidationInterfaceQueue();
WITH_LOCK(::cs_main, manager.Unload());
} }
//! Test rebalancing the caches associated with each chainstate. //! Test rebalancing the caches associated with each chainstate.
+20 -24
View File
@@ -1978,7 +1978,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
// effectively caching the result of part of the verification. // effectively caching the result of part of the verification.
BlockMap::const_iterator it = m_blockman.m_block_index.find(hashAssumeValid); BlockMap::const_iterator it = m_blockman.m_block_index.find(hashAssumeValid);
if (it != m_blockman.m_block_index.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) {
// This block is a member of the assumed verified chain and an ancestor of the best header. // 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
{ {
LOCK(cs_main); LOCK(cs_main);
for (const auto& entry : m_blockman.m_block_index) { for (auto& entry : m_blockman.m_block_index) {
CBlockIndex *candidate = entry.second; CBlockIndex* candidate = &entry.second;
// We don't need to put anything in our active chain into the // We don't need to put anything in our active chain into the
// multimap, because those candidates will be found and considered // multimap, because those candidates will be found and considered
// as we disconnect. // as we disconnect.
@@ -3133,12 +3133,10 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, CBlockIndex* pind
// it up here, this should be an essentially unobservable error. // it up here, this should be an essentially unobservable error.
// Loop back over all block index entries and add any missing entries // Loop back over all block index entries and add any missing entries
// to setBlockIndexCandidates. // to setBlockIndexCandidates.
BlockMap::iterator it = m_blockman.m_block_index.begin(); for (auto& [_, block_index] : m_blockman.m_block_index) {
while (it != m_blockman.m_block_index.end()) { if (block_index.IsValid(BLOCK_VALID_TRANSACTIONS) && block_index.HaveTxsDownloaded() && !setBlockIndexCandidates.value_comp()(&block_index, m_chain.Tip())) {
if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && !setBlockIndexCandidates.value_comp()(it->second, m_chain.Tip())) { setBlockIndexCandidates.insert(&block_index);
setBlockIndexCandidates.insert(it->second);
} }
it++;
} }
InvalidChainFound(to_mark_failed); InvalidChainFound(to_mark_failed);
@@ -3157,21 +3155,19 @@ 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 = m_blockman.m_block_index.begin(); for (auto& [_, block_index] : m_blockman.m_block_index) {
while (it != m_blockman.m_block_index.end()) { if (!block_index.IsValid() && block_index.GetAncestor(nHeight) == pindex) {
if (!it->second->IsValid() && it->second->GetAncestor(nHeight) == pindex) { block_index.nStatus &= ~BLOCK_FAILED_MASK;
it->second->nStatus &= ~BLOCK_FAILED_MASK; m_blockman.m_dirty_blockindex.insert(&block_index);
m_blockman.m_dirty_blockindex.insert(it->second); if (block_index.IsValid(BLOCK_VALID_TRANSACTIONS) && block_index.HaveTxsDownloaded() && setBlockIndexCandidates.value_comp()(m_chain.Tip(), &block_index)) {
if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && setBlockIndexCandidates.value_comp()(m_chain.Tip(), it->second)) { setBlockIndexCandidates.insert(&block_index);
setBlockIndexCandidates.insert(it->second);
} }
if (it->second == m_chainman.m_best_invalid) { if (&block_index == m_chainman.m_best_invalid) {
// Reset invalid block marker if it was pointing to one of those. // Reset invalid block marker if it was pointing to one of those.
m_chainman.m_best_invalid = nullptr; m_chainman.m_best_invalid = nullptr;
} }
m_chainman.m_failed_blocks.erase(it->second); m_chainman.m_failed_blocks.erase(&block_index);
} }
it++;
} }
// Remove the invalidity flag from all ancestors too. // Remove the invalidity flag from all ancestors too.
@@ -3500,7 +3496,7 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
if (hash != chainparams.GetConsensus().hashGenesisBlock) { if (hash != chainparams.GetConsensus().hashGenesisBlock) {
if (miSelf != m_blockman.m_block_index.end()) { if (miSelf != m_blockman.m_block_index.end()) {
// Block header is already known. // Block header is already known.
CBlockIndex* pindex = miSelf->second; CBlockIndex* pindex = &(miSelf->second);
if (ppindex) if (ppindex)
*ppindex = pindex; *ppindex = pindex;
if (pindex->nStatus & BLOCK_FAILED_MASK) { if (pindex->nStatus & BLOCK_FAILED_MASK) {
@@ -3522,7 +3518,7 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
LogPrint(BCLog::VALIDATION, "%s: %s prev block not found\n", __func__, hash.ToString()); LogPrint(BCLog::VALIDATION, "%s: %s prev block not found\n", __func__, hash.ToString());
return state.Invalid(BlockValidationResult::BLOCK_MISSING_PREV, "prev-blk-not-found"); return state.Invalid(BlockValidationResult::BLOCK_MISSING_PREV, "prev-blk-not-found");
} }
pindexPrev = (*mi).second; pindexPrev = &((*mi).second);
if (pindexPrev->nStatus & BLOCK_FAILED_MASK) { if (pindexPrev->nStatus & BLOCK_FAILED_MASK) {
LogPrint(BCLog::VALIDATION, "%s: %s prev block invalid\n", __func__, hash.ToString()); LogPrint(BCLog::VALIDATION, "%s: %s prev block invalid\n", __func__, hash.ToString());
return state.Invalid(BlockValidationResult::BLOCK_INVALID_PREV, "bad-prevblk"); return state.Invalid(BlockValidationResult::BLOCK_INVALID_PREV, "bad-prevblk");
@@ -3994,13 +3990,13 @@ bool CChainState::ReplayBlocks()
if (m_blockman.m_block_index.count(hashHeads[0]) == 0) { if (m_blockman.m_block_index.count(hashHeads[0]) == 0) {
return error("ReplayBlocks(): reorganization to unknown block requested"); return error("ReplayBlocks(): reorganization to unknown block requested");
} }
pindexNew = m_blockman.m_block_index[hashHeads[0]]; pindexNew = &(m_blockman.m_block_index[hashHeads[0]]);
if (!hashHeads[1].IsNull()) { // The old tip is allowed to be 0, indicating it's the first flush. if (!hashHeads[1].IsNull()) { // The old tip is allowed to be 0, indicating it's the first flush.
if (m_blockman.m_block_index.count(hashHeads[1]) == 0) { if (m_blockman.m_block_index.count(hashHeads[1]) == 0) {
return error("ReplayBlocks(): reorganization from unknown block requested"); return error("ReplayBlocks(): reorganization from unknown block requested");
} }
pindexOld = m_blockman.m_block_index[hashHeads[1]]; pindexOld = &(m_blockman.m_block_index[hashHeads[1]]);
pindexFork = LastCommonAncestor(pindexOld, pindexNew); pindexFork = LastCommonAncestor(pindexOld, pindexNew);
assert(pindexFork != nullptr); assert(pindexFork != nullptr);
} }
@@ -4267,8 +4263,8 @@ void CChainState::CheckBlockIndex()
// Build forward-pointing map of the entire block tree. // Build forward-pointing map of the entire block tree.
std::multimap<CBlockIndex*,CBlockIndex*> forward; std::multimap<CBlockIndex*,CBlockIndex*> forward;
for (const std::pair<const uint256, CBlockIndex*>& entry : m_blockman.m_block_index) { for (auto& [_, block_index] : m_blockman.m_block_index) {
forward.insert(std::make_pair(entry.second->pprev, entry.second)); forward.emplace(block_index.pprev, &block_index);
} }
assert(forward.size() == m_blockman.m_block_index.size()); assert(forward.size() == m_blockman.m_block_index.size());
+2 -2
View File
@@ -367,10 +367,10 @@ static int64_t AddTx(ChainstateManager& chainman, CWallet& wallet, uint32_t lock
CBlockIndex* block = nullptr; CBlockIndex* block = nullptr;
if (blockTime > 0) { if (blockTime > 0) {
LOCK(cs_main); LOCK(cs_main);
auto inserted = chainman.BlockIndex().emplace(GetRandHash(), new CBlockIndex); auto inserted = chainman.BlockIndex().emplace(std::piecewise_construct, std::make_tuple(GetRandHash()), std::make_tuple());
assert(inserted.second); assert(inserted.second);
const uint256& hash = inserted.first->first; const uint256& hash = inserted.first->first;
block = inserted.first->second; block = &inserted.first->second;
block->nTime = blockTime; block->nTime = blockTime;
block->phashBlock = &hash; block->phashBlock = &hash;
state = TxStateConfirmed{hash, block->nHeight, /*position_in_block=*/0}; state = TxStateConfirmed{hash, block->nHeight, /*position_in_block=*/0};