Skip to content

Lookup sync with blocks_by_head#81

Closed
dapplion wants to merge 26 commits into
unstablefrom
lookup-ancestor-blocks
Closed

Lookup sync with blocks_by_head#81
dapplion wants to merge 26 commits into
unstablefrom
lookup-ancestor-blocks

Conversation

@dapplion

@dapplion dapplion commented Jun 11, 2026

Copy link
Copy Markdown
Owner

Part of tree sync (sigp#7678).

Wires lookup sync to fetch over beacon_blocks_by_head (ethereum/consensus-specs#5181; responder landed in sigp#9237). Instead of walking an unknown parent chain one block at a time over blocks_by_root, a peer returns a block plus a run of its ancestors in one response, so a deep chain resolves in a single round-trip.

  • Lookups prefer peers advertising by_head (parsed from identify), fall back to by_root, and skip peers already at MAX_CONCURRENT_REQUESTS for that protocol.
  • A by_head response is cached and threaded through parent-lookup creation, so the whole chain is seeded with no follow-up requests. by_root shares the same path.
  • Genesis/finalized blocks count as parent-imported; blocks below finality that aren't on our chain are dropped.

Tests run the whole lookup suite against both by_root and by_head peers, plus one asserting a 32-block chain resolves with a single by_head request and zero by_root.

Built with Claude (Opus 4.8), reviewed line by line.

dapplion added 13 commits June 11, 2026 15:47
Block lookups now prefer peers that advertise beacon_blocks_by_head and
fetch the block (plus a run of its ancestors) via that protocol, falling
back to beacon_blocks_by_root otherwise. on_block_download_response now
receives a Vec of blocks; the ancestors beyond the requested root are not
yet consumed (header-backfill step).
Implement Serialize for the rpc Protocol enum (as its canonical name) so
PeerInfo.supported_protocols is surfaced in the peers API, matching how
subnet fields are serialized, rather than skipped.
Move the identify protocol-id parsing into a standalone
rpc_protocol_from_id function and add a unit test covering ReqResp,
non-ReqResp, and unknown protocol ids.
- AncestorBlocks::from_vec consumes an iterator (no remove(0))
- BlocksByHeadRequestItems::add uses if-let instead of match
- reduce rpc_protocol_from_id test to two cases
- Split block_lookup_request send paths into send_block_by_root_request
  and send_blocks_by_head, both taking the selected PeerId
- on_{single_block,blocks_by_head}_response return AncestorBlocks so the
  sync manager does no transformation
- query peer_supports_blocks_by_head after selection instead of carrying it
Add the beacon_blocks_by_head support flag as the first tuple element and
keep unstable's .min()/peer-in-tuple shape instead of restructuring.
Strictly de-prioritize peers already at MAX_CONCURRENT_REQUESTS for the
protocol we would use (by_head if supported, else by_root), counting only
that protocol's in-flight requests per the spec's per-protocol-ID limit.
Expose MAX_CONCURRENT_REQUESTS and add ActiveRequests::active_request_count_of_peer.
Build per-peer blocks_by_root / blocks_by_head count maps once instead of
scanning per candidate peer; gate selection on the relevant protocol's count
>= MAX_CONCURRENT_REQUESTS.
…ctors

- send_block_by_root_request/send_blocks_by_head take lookup_id, allocate
  their own req_id, and return it
- replace the two per-peer count helpers with an ActiveRequestsPerPeer struct
  exposing at_concurrency_limit(peer)
- give AncestorBlocks from_single/from_vec constructors and inline
  into_ancestor_blocks into the response handlers
…elper

get_parent_import_status now reports an already-imported block (genesis, or a
finalized block whose parent was pruned) as having an imported parent, so a
lookup for such an anchor is processed rather than awaiting a missing parent.
Block lookups use cx.conflicts_with_finality (returning a reason String) to
drop blocks at/below finalization that are off the finalized chain.
Pass known_blocks down search_parent_of_child -> new_current_lookup ->
on_lookup_result so every ancestor seeded from a single beacon_blocks_by_head
response is served from the cache, rather than only the first parent level.
dapplion added 5 commits June 11, 2026 18:26
- size PARENT_DEPTH_TOLERANCE to BLOCKS_BY_HEAD_REQUEST_COUNT + 1 so one
  by-head batch can seed a full chain without tripping the depth limit
- rig: serve BlocksByHead (walk parent chain) and a peerdb test setter +
  rig helper to mark a peer as advertising beacon_blocks_by_head
- add a test asserting a 32-block chain resolves with 1 by-head request and
  0 blocks-by-root, all blocks cached
is_parent_imported now also returns true when the block is itself in fork
choice (contains_block), covering genesis and finalized blocks whose anchor
proto-node isn't queryable via get_block. This lets a lookup for such a block
be processed instead of dropped as conflicting with finality.
Serve beacon_blocks_by_head in the test rig (honouring the empty/wrong-block
failure strategies), add a PeerSupport enum threaded into TestRig::default /
new_after_fulu, and nest the depth macro so every lookup test runs as a
by_root and a by_head variant.
… fetch a batch

A lookup that nothing is waiting on is a lone island and requests a small count
(4); once a child is awaiting it the lookup is part of a chain proven deep and
requests a large batch (32). Derived from the lookup graph at creation, no
escalation state. Tune the counts against mainnet metrics.
dapplion added 4 commits June 11, 2026 23:17
The subject (beacon_blocks_by_head) now lives in the type name, so the variants
read clearly on their own instead of the ambiguous DoesNotSupport.
SingleLookupRequestState now records the peers that failed a download for the
request, and block_lookup_request de-prioritizes them in peer selection (just
below the concurrency-limit check). This stops a faulty by_head peer from being
re-picked on every retry in a mixed peer set, letting the lookup fall back to a
by_root peer instead of exhausting its attempts and dropping. Found in review.
Add a rig test asserting a faulty by_head peer's failed download forces the
retry to fall back to a by_root peer (1 BlocksByHead + 1 BlocksByRoot, lookup
succeeds). Also restore the set_peer_supports_by_head helper name, mangled by
the earlier ByHeadSupport rename.
# Conflicts:
#	beacon_node/network/src/sync/block_lookups/single_block_lookup.rs
#	beacon_node/network/src/sync/manager.rs
#	beacon_node/network/src/sync/network_context.rs
#	beacon_node/network/src/sync/tests/lookups.rs
#	beacon_node/network/src/sync/tests/range.rs
@dapplion dapplion requested a review from michaelsproul as a code owner June 25, 2026 14:21
dapplion added 4 commits June 25, 2026 18:34
…chor handling into get_parent_import_status

- Remove the out-of-scope failed_peers peer-fallback (block lookups no longer
  de-prioritize peers that failed a download); it can be a separate PR.
- Reject a downloaded block more than SLOT_IMPORT_TOLERANCE ahead of the current
  slot before creating a parent lookup, so a peer can't make us walk ancestors or
  force range sync to a bogus future head (cx.block_too_far_in_future).
- Move the genesis/anchor 'block is itself in fork choice' case into
  get_parent_import_status via a proto-less ParentImportStatus::AlreadyImported,
  so is_parent_imported is a plain matches!.
Revert the consensus-side move from the previous commit: get_parent_import_status
returns to pure parent-only semantics (no AlreadyImported variant) and
block_verification.rs is restored byte-identical to upstream. The genesis/anchor
'block is itself imported' case lives only in the sync-facing is_parent_imported
via '|| contains_block', so consensus block-import paths are untouched.
…aker, finality uses Failed, lookup tidy-ups

- SyncRequestId::BlocksByHead uses a tuple variant.
- Drop the per-peer request-count tiebreaker from lookup peer selection
  (and the now-unused active_request_count_by_peer helper).
- Reuse LookupRequestError::Failed for finality-conflicting blocks; remove
  the unused ConflictsWithFinality variant.
- Collect known ancestors via iterator; tidy two comments.
@dapplion

Copy link
Copy Markdown
Owner Author

@dapplion dapplion closed this Jun 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant