Skip to content

refactor: extract BlockDownloadManager from PeerManagerImpl#34565

Open
w0xlt wants to merge 3 commits intobitcoin:masterfrom
w0xlt:refactor/extract-block-download-manager
Open

refactor: extract BlockDownloadManager from PeerManagerImpl#34565
w0xlt wants to merge 3 commits intobitcoin:masterfrom
w0xlt:refactor/extract-block-download-manager

Conversation

@w0xlt
Copy link
Contributor

@w0xlt w0xlt commented Feb 11, 2026

Motivation

net_processing.cpp is the largest file in the codebase (~6200 lines) and PeerManagerImpl mixes several largely independent subsystems into a single class: transaction relay, address gossip, headers sync, compactblocks, and block download. This makes the file difficult to review, test in isolation, and reason about lock ordering.

#30110 successfully extracted transaction download logic into TxDownloadManager. This PR applies the same approach to block download, continuing the incremental decomposition of PeerManagerImpl.

What this PR does

Extract all block download state and scheduling logic into a new BlockDownloadManager module:

Global state moved (from PeerManagerImpl): mapBlocksInFlight, mapBlockSource, nSyncStarted, m_block_stalling_timeout, m_last_tip_update, m_num_preferred_download_peers, m_peers_downloading_from.

Per-peer state moved (from CNodeState): pindexBestKnownBlock, hashLastUnknownBlock, pindexLastCommonBlock, pindexBestHeaderSent, fSyncStarted, vBlocksInFlight, m_downloading_since, m_stalling_since, fPreferredDownload.

Methods moved (from PeerManagerImpl): FindNextBlocksToDownload, TryDownloadingHistoricalBlocks, ProcessBlockAvailability, UpdateBlockAvailability, BlockRequested, RemoveBlockRequest, IsBlockRequested, IsBlockRequestedFromOutbound, TipMayBeStale.

The result:

  • net_processing.cpp shrinks from 6193 to 5751 lines (−442 net)
  • CNodeState loses 10 fields, retaining only compact block relay and chain sync timeout state
  • Block download logic becomes unit-testable in isolation (8 test cases added)

Design

Follows the TxDownloadManager pimpl pattern:

  • blockdownloadman.h — public interface
  • blockdownloadman_impl.h — implementation class with per-peer state
  • blockdownloadman_impl.cpp — implementation

Unlike TxDownloadManager, no new mutex is introduced. Block download is inherently tied to chain state through CBlockIndex* pointers, so cs_main remains the synchronizing lock. The extraction is for code organization and testability, not lock granularity.

Commits

  1. Add BlockDownloadManager class with pimpl skeleton: all new module files, no call sites changed
  2. Add unit tests: exercises the module in isolation
  3. Migrate call sites: ~150 call sites in net_processing.cpp rewired through m_blockdownloadman, pure mechanical replacement

Create a new BlockDownloadManager module following the established
TxDownloadManager pimpl pattern:

- blockdownloadman.h: public interface with QueuedBlock, options/info
  structs, and full BlockDownloadManager API
- blockdownloadman_impl.h: BlockDownloadManagerImpl with
  PeerBlockDownloadState and per-peer map
- blockdownloadman_impl.cpp: forwarding wrappers and implementations
  for block request tracking, availability processing, download
  scheduling, stalling detection, and tip staleness checks

The module manages all block download state: in-flight tracking
(mapBlocksInFlight), block source attribution (mapBlockSource),
per-peer download state, sync coordination, and scheduling algorithms
(FindNextBlocksToDownload, TryDownloadingHistoricalBlocks).

Key design decisions:
- cs_main provides synchronization (no separate mutex), since block
  download is inherently tied to chain state via CBlockIndex pointers
- Two-phase peer registration: ConnectedPeer handles both initial
  registration (conservative defaults) and re-registration (real
  connection properties from VERSION handshake)
- TipMayBeStale preserves lazy initialization of m_last_tip_update
- CompareExchangeBlockStallingTimeout preserves CAS atomicity for
  the stalling timeout accessed from multiple threads

No call sites are migrated yet; that happens in a subsequent commit.
Exercise BlockDownloadManager in isolation:

- peer_lifecycle: registration, sync state (including idempotency
  and explicit unsetting), blocks in flight, and disconnect cleanup
  for all counters (preferred, sync, downloading)
- block_source_tracking: set/get/erase block sources
- stalling_and_tip_staleness: last tip update get/set, and
  TipMayBeStale lazy initialization and staleness detection
- block_requested_basic: request/remove lifecycle, outbound
  detection, duplicate rejection, in-flight counts
- remove_block_request_from_specific_peer: peer-targeted removal
  leaves other peers' requests intact
- find_block_in_flight: verify BlockInFlightInfo fields across
  no-flight, single-peer, and multi-peer scenarios with and
  without partialBlock
- tip_not_stale_with_blocks_in_flight: blocks in flight suppress
  staleness even with an old tip update
- connected_peer_reregistration: two-phase ConnectedPeer upgrade
  from preferred=false to preferred=true without double-counting
- compare_exchange_stalling_timeout: CAS success/failure semantics
@DrahtBot
Copy link
Contributor

DrahtBot commented Feb 11, 2026

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Reviews

See the guideline for information on the review process.

Type Reviewers
Concept ACK sedited, 0xbrito

If your review is incorrectly listed, please copy-paste <!--meta-tag:bot-skip--> into the comment that the bot should ignore.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #34743 (p2p: protect manual evictions by willcl-ark)
  • #34588 (refactor: decompose Peer struct into focused sub-components by w0xlt)
  • #34389 (net/log: standardize peer+addr log formatting via LogPeer by l0rinc)
  • #33854 (fix assumevalid is ignored during reindex by Eunovo)
  • #27052 (test: rpc: add last block announcement time to getpeerinfo result by LarryRuane)

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

LLM Linter (✨ experimental)

Possible typos and grammar issues:

  • "the actual timeout is increased temporarily if peers are disconnected for hitting the timeout" -> "The actual timeout is increased temporarily if peers are disconnected for hitting the timeout" [Sentence starts with lowercase "the"; capitalize to form a proper sentence.]
  • "Number of preferable block download peers." -> "Number of preferred block download peers." ["preferable" is incorrect word choice here; "preferred" matches the code and meaning.]

Possible places where named args for integral literals may be used (e.g. func(x, /*named_arg=*/0) in C++, and func(x, named_arg=0) in Python):

  • m_blockdownloadman.SetBlockSource(block_transactions.blockhash, pfrom.GetId(), false) in src/net_processing.cpp
  • m_blockdownloadman.SetBlockSource(hash, pfrom.GetId(), true) in src/net_processing.cpp
  • bdm.SetBlockSource(hash1, peer1, true) in src/test/blockdownload_tests.cpp

2026-02-17 19:29:10

@sedited
Copy link
Contributor

sedited commented Feb 11, 2026

Concept ACK

1 similar comment
@0xbrito
Copy link

0xbrito commented Feb 12, 2026

Concept ACK

@Crypt-iQ
Copy link
Contributor

Only glanced at the changes -- does this change make it easier to fuzz individual components?

…Manager

Move all block download state and logic from PeerManagerImpl and
CNodeState into BlockDownloadManager, completing the extraction.

Migrated from PeerManagerImpl:
- mapBlocksInFlight, mapBlockSource, nSyncStarted
- m_block_stalling_timeout, m_last_tip_update
- m_num_preferred_download_peers, m_peers_downloading_from
- IsBlockRequested, IsBlockRequestedFromOutbound
- RemoveBlockRequest, BlockRequested, TipMayBeStale
- ProcessBlockAvailability, UpdateBlockAvailability
- FindNextBlocksToDownload, TryDownloadingHistoricalBlocks

Migrated from CNodeState:
- pindexBestKnownBlock, hashLastUnknownBlock
- pindexLastCommonBlock, pindexBestHeaderSent
- fSyncStarted, vBlocksInFlight, m_downloading_since
- m_stalling_since, fPreferredDownload

~150 call sites updated to go through m_blockdownloadman.
@w0xlt w0xlt force-pushed the refactor/extract-block-download-manager branch from 6578aa8 to a24d36e Compare February 17, 2026 19:28
@w0xlt
Copy link
Contributor Author

w0xlt commented Feb 17, 2026

Only glanced at the changes -- does this change make it easier to fuzz individual components?

Yes, this is one of the main benefits. Currently block download logic is embedded in PeerManagerImpl and CNodeState, which require standing up the full net_processing stack (connman, addrman, mempool, etc.) to test. There is no way to exercise block scheduling, request tracking, or stalling detection without that heavy setup.

After this PR, BlockDownloadManager can be constructed with just a ChainstateManager reference:

BlockDownloadManager bdm(BlockDownloadOptions{*m_node.chainman});
bdm.ConnectedPeer(peer1, info);
bdm.BlockRequested(peer1, *genesis);

The fuzz target itself isn't included here (keeping scope focused on the extraction), but the architecture now makes it straightforward to add as a follow-up.

@DrahtBot
Copy link
Contributor

🐙 This pull request conflicts with the target branch and needs rebase.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants