Skip to content

Add beacon_blocks_by_head ReqResp#5181

Merged
jtraglia merged 5 commits into
ethereum:masterfrom
dapplion:blocks-by-head-gloas
May 11, 2026
Merged

Add beacon_blocks_by_head ReqResp#5181
jtraglia merged 5 commits into
ethereum:masterfrom
dapplion:blocks-by-head-gloas

Conversation

@dapplion
Copy link
Copy Markdown
Member

@dapplion dapplion commented Apr 28, 2026

Adds a new req/resp route beacon_blocks_by_head to Fulu. The request is (beacon_root, count); the responder walks the parent chain of beacon_root (inclusive) and emits up to count blocks in descending slot order, one per response_chunk in the same shape as BeaconBlocksByRange v2. Capped at MAX_REQUEST_BLOCKS_DENEB (= 128).

Same motivation as #5179 (header-tree forward sync, sigp/lighthouse#7678) — backfill a chain segment from a known head. The trade-off vs. #5179:

  • This route streams full blocks (~average ~150 KiB/slot post-Deneb), so it's heavier on the wire.
  • It reuses the existing blocks-by-root store. No new DB index required on the server side.

#5179 is the bandwidth-optimal version (only headers, single chunk, 2,048 per request) but requires clients to maintain a new headers-by-root DB column. This PR is the lower-cost-to-ship alternative for clients that don't want to add storage.

The two routes are not mutually exclusive — a client can serve either or both.

@dapplion dapplion changed the title Add beacon_blocks_by_head req/resp on Gloas Add beacon_blocks_by_head ReqResp Apr 28, 2026
@github-actions github-actions Bot added fulu and removed gloas labels Apr 28, 2026
Copy link
Copy Markdown
Member

@jtraglia jtraglia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! I debated whether or not this should be backported to phase0, but that would require a V2 endpoint in Deneb (due to the updated max request blocks config) and this would cause unnecessary implementation overhead. Since this is expected to be used before Gloas (in Fulu) I believe introducing this in Fulu is an acceptable compromise.

```
(
beacon_root: Root
count: uint64
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could consider making this slot-based so that if we request a finalized block, we end up by construction on an other epoch boundary by choosing an appropriate count (ie divisble by slots_per_epoch).

Not sure if this matters or not but it might make it easier to reason about overlapping ranges in parallel requests (request slots 0-1023 from one peer and 1024-2047 in another).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One of the points of this request style is to not think in slots so the handling of large ranges of skip slots is not an issue anymore. For the headers variant I added a min_slot parameter, so that the stream stops at that slot. Since the max count of this route is small that feels unnecessary, but I can add it.

Copy link
Copy Markdown
Contributor

@arnetheduck arnetheduck May 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, the way this request is done prevents issuing parallel requests along the same history branch - ie consider:

------A-----B----C

when you request blocks along the C history, you don't know B and A but you still want to refer to them "logically" - so the kind of request you would want to make is "give me 64 blocks from the C history" and in parallel "give be 64 blocks starting at C-64, still from the C history".

For this, we need the request to look something like:

{
  beacon_root: Root
  slot_count: uint64
  slot_offset: uint64
}

where slot_offset moves backwards in time along the history chosen by beacon_root - with the above, if I'm connected to N peers I can issue N parallel request that are guaranteed to be non-overlapping - as they arrive, I can also patch up any holes.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is to not think in slots so the handling of large ranges of skip slots is not an issue anymore

Skip slots are not really an issue with the suggested replacement - consider that you get at least beacon_root from your request - if there's a big gap, you'll find out about the parent_root from that one block that you get and thus skip any number of slots to reach the next parent - since this case is exceedingly rare, I think the ability to do parallel range downloads in the normal case.

Also, if there is a significant gap, a request for beacon_root and slot_offset >= 1 will normally give you a block after that gap - we can mandate the behavior that you send at least one block in this scenario, even if it would violate slot_count - this would be a small overlap loss but would speed up slot skipping.

Copy link
Copy Markdown
Contributor

@arnetheduck arnetheduck May 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually, we can have the cake and eat it:

{
  beacon_root: Root
  count: uint64
  offset: uint64
}

if we base the offset off the block count instead of slots we get the skip slot propery and the ability to ask for overlapping ranges.

this has the downside that we will overshoot our own finality point (which is known by slot and normally acts as a boundary on requests - so keeping a slot limit also has some utility)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we do your modifications this route becomes mut and it will just be a beacon_blocks_by_range but with the capacity to choose what head to choose from. If your sync benefits significantly from this sort of parallel requests can you propose to modify beacon_blocks_by_range and add a beacon_root?

I feel this discussion is very subjective on how you and I plan to implement sync. With your suggestion I can not implement the algorithm I plan to do, and viceversa for you

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#5265 - with offset, min_slot=0, this request is equivalent to the existing request - however, the additional parameters allow some parallelism.

Comment thread specs/fulu/p2p-interface.md Outdated
Copy link
Copy Markdown
Member

@jtraglia jtraglia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. @dapplion will merge after more CL clients approve the PR.

@jtraglia
Copy link
Copy Markdown
Member

These two reviews plus the reactions feel sufficient to me. Thanks 😄

image

@jtraglia jtraglia merged commit 945a70a into ethereum:master May 11, 2026
15 checks passed
nflaig pushed a commit to ChainSafe/lodestar that referenced this pull request May 13, 2026
**Motivation**

- ethereum/consensus-specs#5181
- We want to support better syncing strategies in nonfinality

**Description**

- Add beacon_blocks_by_head serving and requesting code (requesting is
currently unused)

**AI Assistance Disclosure**

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants