mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-06-10 06:39:15 +02:00
Merge bitcoin/bitcoin#34440: refactor: Change CChain methods to use references, add tests
7c75244adeChange pindexMostWork parameter of ActivateBestChainStep() to reference (optout)c5eb283bcaChange CChain::FindFork() to take ref (optout)20b58e281aChange CChain::Next() to take reference (optout)fe2d6e25e0Change CChain::Contains() to take reference (optout)db56bcd692test: Add CChain::FindFork() tests (optout)8333abdd91test: Add CChain basic tests (optout) Pull request description: Refactor `CChain` methods (`Contains()`, `Next()`, `FindFork()`) to use references instead of pointers, to minimize the risk of accidental `nullptr` dereference (memory access violation). Also add missing unit tests to the `CChain` class. The `CChain::Contains()` method (in `src/chain.h`) dereferences its input without checking. The `Next()` method also calls into this with a `nullptr` if invoked with `nullptr`. While most call sites have indirect guarantee that the input is not `nullptr`, it's not easy to establish this to all call sites with high confidence. These methods are publicly available. There is no known high-level use case to trigger this error, but the fix is easy, and makes the code safer. Changes: - Add basic unit tests for `CChain` class methods - Add unit tests for `CChain::FindFork()` - Change `CChain::Contains()` to take reference - Change `CChain::Next()` to take reference - Change `CChain::FindFork()` to take reference - Change `pindexMostWork` parameter of `ActivateBestChainStep()` to reference - Rename changed parameters (`* pindex` --> `& index`) Alternative. A simpler change is to stick with pointers, with extra checks where needed, see #34416 . This change is remotely related to and indirectly triggered by #32875 . Further ideas, not considered in this PR: - Change `InvalidateBlock()` and `PreciousBlock()` to take references. - Change `CChain` internals to store references instead of pointers - Change CChain to always have at least one element (genesis), that way there is always genesis and tip. - Check related methods to return reference (guaranteed non-null) -- `FindFork`, `FindEarliestAtLeast`, `FindForkInGlobalIndex`, `blockman.AddToBlockIndex`, etc. ACKs for top commit: l0rinc: reACK7c75244ademaflcko: re-review ACK7c75244ade🌅 achow101: ACK7c75244adehodlinator: re-ACK7c75244adeTree-SHA512: 122f40120058f7e1f0273b3afed9c54966c05f06b6f2fee45bc48430617f24a5e4320a9bb7bb0ac986f2accfa22fabae5cc941b949758ddca2e9fcd472b46c33
This commit is contained in:
@@ -1503,7 +1503,7 @@ void PeerManagerImpl::FindNextBlocks(std::vector<const CBlockIndex*>& vBlocks, c
|
||||
return;
|
||||
}
|
||||
|
||||
if (pindex->nStatus & BLOCK_HAVE_DATA || (activeChain && activeChain->Contains(pindex))) {
|
||||
if (pindex->nStatus & BLOCK_HAVE_DATA || (activeChain && activeChain->Contains(*pindex))) {
|
||||
if (activeChain && pindex->HaveNumChainTxs()) {
|
||||
state->pindexLastCommonBlock = pindex;
|
||||
}
|
||||
@@ -1952,7 +1952,7 @@ void PeerManagerImpl::MaybePunishNodeForBlock(NodeId nodeid, const BlockValidati
|
||||
bool PeerManagerImpl::BlockRequestAllowed(const CBlockIndex& block_index)
|
||||
{
|
||||
AssertLockHeld(cs_main);
|
||||
if (m_chainman.ActiveChain().Contains(&block_index)) return true;
|
||||
if (m_chainman.ActiveChain().Contains(block_index)) return true;
|
||||
return block_index.IsValid(BLOCK_VALID_SCRIPTS) && (m_chainman.m_best_header != nullptr) &&
|
||||
(m_chainman.m_best_header->GetBlockTime() - block_index.GetBlockTime() < STALE_RELAY_AGE_LIMIT) &&
|
||||
(GetBlockProofEquivalentTime(*m_chainman.m_best_header, block_index, *m_chainman.m_best_header, m_chainparams.GetConsensus()) < STALE_RELAY_AGE_LIMIT);
|
||||
@@ -2815,7 +2815,7 @@ bool PeerManagerImpl::IsAncestorOfBestHeaderOrTip(const CBlockIndex* header)
|
||||
return false;
|
||||
} else if (m_chainman.m_best_header != nullptr && header == m_chainman.m_best_header->GetAncestor(header->nHeight)) {
|
||||
return true;
|
||||
} else if (m_chainman.ActiveChain().Contains(header)) {
|
||||
} else if (m_chainman.ActiveChain().Contains(*header)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -2849,7 +2849,7 @@ void PeerManagerImpl::HeadersDirectFetchBlocks(CNode& pfrom, const Peer& peer, c
|
||||
std::vector<const CBlockIndex*> vToFetch;
|
||||
const CBlockIndex* pindexWalk{&last_header};
|
||||
// Calculate all the blocks we'd need to switch to last_header, up to a limit.
|
||||
while (pindexWalk && !m_chainman.ActiveChain().Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
|
||||
while (pindexWalk && !m_chainman.ActiveChain().Contains(*pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
|
||||
if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) &&
|
||||
!IsBlockRequested(pindexWalk->GetBlockHash()) &&
|
||||
(!DeploymentActiveAt(*pindexWalk, m_chainman, Consensus::DEPLOYMENT_SEGWIT) || CanServeWitnesses(peer))) {
|
||||
@@ -2862,7 +2862,8 @@ void PeerManagerImpl::HeadersDirectFetchBlocks(CNode& pfrom, const Peer& peer, c
|
||||
// very large reorg at a time we think we're close to caught up to
|
||||
// the main chain -- this shouldn't really happen. Bail out on the
|
||||
// direct fetch and rely on parallel download instead.
|
||||
if (!m_chainman.ActiveChain().Contains(pindexWalk)) {
|
||||
// Common ancestor must exist (genesis).
|
||||
if (!m_chainman.ActiveChain().Contains(*Assert(pindexWalk))) {
|
||||
LogDebug(BCLog::NET, "Large reorg, won't direct fetch to %s (%d)\n",
|
||||
last_header.GetBlockHash().ToString(),
|
||||
last_header.nHeight);
|
||||
@@ -4214,10 +4215,10 @@ void PeerManagerImpl::ProcessMessage(Peer& peer, CNode& pfrom, const std::string
|
||||
|
||||
// Send the rest of the chain
|
||||
if (pindex)
|
||||
pindex = m_chainman.ActiveChain().Next(pindex);
|
||||
pindex = m_chainman.ActiveChain().Next(*pindex);
|
||||
int nLimit = 500;
|
||||
LogDebug(BCLog::NET, "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), nLimit, pfrom.GetId());
|
||||
for (; pindex; pindex = m_chainman.ActiveChain().Next(pindex))
|
||||
for (; pindex; pindex = m_chainman.ActiveChain().Next(*pindex))
|
||||
{
|
||||
if (pindex->GetBlockHash() == hashStop)
|
||||
{
|
||||
@@ -4353,14 +4354,14 @@ void PeerManagerImpl::ProcessMessage(Peer& peer, CNode& pfrom, const std::string
|
||||
// Find the last block the caller has in the main chain
|
||||
pindex = m_chainman.ActiveChainstate().FindForkInGlobalIndex(locator);
|
||||
if (pindex)
|
||||
pindex = m_chainman.ActiveChain().Next(pindex);
|
||||
pindex = m_chainman.ActiveChain().Next(*pindex);
|
||||
}
|
||||
|
||||
// we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end
|
||||
std::vector<CBlock> vHeaders;
|
||||
int nLimit = m_opts.max_headers_result;
|
||||
LogDebug(BCLog::NET, "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), pfrom.GetId());
|
||||
for (; pindex; pindex = m_chainman.ActiveChain().Next(pindex))
|
||||
for (; pindex; pindex = m_chainman.ActiveChain().Next(*pindex))
|
||||
{
|
||||
vHeaders.emplace_back(pindex->GetBlockHeader());
|
||||
if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop)
|
||||
|
||||
Reference in New Issue
Block a user