Skip to content

feat(p2p): add inbound BlocksByRange req/resp support#348

Open
dicethedev wants to merge 2 commits intolambdaclass:mainfrom
dicethedev:feat/blocks-by-range-inbound-support
Open

feat(p2p): add inbound BlocksByRange req/resp support#348
dicethedev wants to merge 2 commits intolambdaclass:mainfrom
dicethedev:feat/blocks-by-range-inbound-support

Conversation

@dicethedev
Copy link
Copy Markdown
Contributor

🗒️ Description / Motivation

This PR adds inbound BlocksByRange request-response support to the P2P req/resp protocol implementation.

The change follows the recently merged spec update:

This is needed so peers can request canonical blocks by slot range, similar to the existing BlocksByRoot protocol.

The implementation:

  • registers the new protocol
  • adds SSZ request/response handling
  • supports serving canonical blocks from local storage
  • validates malformed requests

This improves interoperability with other clients implementing the updated spec.


What Changed

Req/Resp Protocol

  • Added BlocksByRangeRequest
  • Added BlocksByRange response payload variant
  • Added protocol ID:
    • /leanconsensus/req/blocks_by_range/1/ssz_snappy

Codec

  • Updated request/response codec read paths
  • Updated request/response codec write paths

Behaviour Registration

  • Registered BlocksByRange in the libp2p request-response behaviour

Inbound Request Handling

  • Added inbound request handler for BlocksByRange
  • Serves canonical blocks by walking backward from the current fork-choice head
  • Skips:
    • empty slots
    • side forks

Validation

Added validation for:

  • step == 0
  • count > 1024

Invalid requests return protocol error responses.

Tests

  • Added unit test for canonical range selection and ordering

Correctness / Behavior Guarantees

Preserved Invariants

  • Only canonical blocks are returned
  • Returned blocks preserve requested slot ordering
  • Empty slots are skipped
  • Non-canonical side forks are ignored

Behavior Notes

  • Invalid requests are rejected early with error responses
  • Maximum request size is capped at 1024 blocks
  • Implementation mirrors existing BlocksByRoot handling patterns for consistency

Tests Added / Run

Added

  • blocks_by_range_returns_canonical_blocks_in_requested_order

Verified With

cargo fmt --check
cargo check -p ethlambda-p2p
cargo test -p ethlambda-p2p blocks_by_range_returns_canonical_blocks_in_requested_order
git diff --check

Related Issues / PRs

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 7, 2026

Greptile Summary

This PR implements inbound BlocksByRange request-response support in the P2P layer, mirroring the existing BlocksByRoot pattern. The codec, protocol registration, and canonical-chain-walking logic are all well-structured.

  • The core canonical_blocks_by_range function correctly walks the chain backward from the fork-choice head, skips side forks and empty slots, and reconstructs the response in ascending slot order using a slot→root map.
  • The codec refactor (decode_blocks_response shared helper) cleanly eliminates duplication between BlocksByRoot and BlocksByRange decode paths.
  • The inbound BlocksByRange response handler is currently a logging stub; outbound request support is not yet wired up.

Confidence Score: 3/5

The serving path is mostly correct but will send a successful empty response to a peer that sends count=0, rather than rejecting the request — a one-line fix is needed before merging.

The chain-walking logic, codec integration, and protocol registration are solid. The one gap is the missing count==0 guard: a peer can elicit a success response with an empty block list where the protocol expects an error, which is a real behavioural defect on the changed request-handling path.

handlers.rs — specifically the validation block in handle_blocks_by_range_request and the stub response handler for inbound BlocksByRange responses.

Important Files Changed

Filename Overview
crates/net/p2p/src/req_resp/handlers.rs Adds inbound BlocksByRange handler with chain-walking logic and a unit test; missing count==0 validation and the inbound response path is a no-op stub.
crates/net/p2p/src/req_resp/messages.rs Adds BlocksByRangeRequest struct, MAX_REQUEST_BLOCKS constant, protocol ID, and BlocksByRange response payload variant — straightforward and correct.
crates/net/p2p/src/req_resp/codec.rs Correctly refactors block-response decoding into a shared helper and adds BlocksByRange codec paths for both read and write directions.
crates/net/p2p/src/req_resp/mod.rs Re-exports new BlocksByRange symbols; no logic changes.
crates/net/p2p/src/lib.rs Registers the BlocksByRange protocol in the libp2p request-response behaviour with Full support; straightforward and correct.
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
crates/net/p2p/src/req_resp/handlers.rs:174
Missing validation for `count == 0`. A peer sending `count=0` bypasses this guard and receives a `Response::Success` with an empty block list rather than an `INVALID_REQUEST` error. The `canonical_blocks_by_range` function short-circuits cleanly, but the caller should enforce the protocol-level constraint and reject the request explicitly, consistent with how `step == 0` is handled.

```suggestion
    if request.step == 0 || request.count == 0 || request.count > MAX_REQUEST_BLOCKS {
```

### Issue 2 of 2
crates/net/p2p/src/req_resp/handlers.rs:75-82
**BlocksByRange response handler is a no-op stub**

When this node receives a `BlocksByRange` response from a peer, the blocks are logged and then silently dropped — no processing, storage, or forwarding takes place. This is fine for now since the node doesn't yet issue outbound `BlocksByRange` requests, but if a future caller sends such a request, all returned blocks will be discarded without any diagnostic. Consider adding a `warn!` that makes the "not yet implemented" intent explicit.

Reviews (1): Last reviewed commit: "feat(p2p): add inbound BlocksByRange req..." | Re-trigger Greptile

Comment thread crates/net/p2p/src/req_resp/handlers.rs Outdated
Comment on lines +75 to +82
ResponsePayload::BlocksByRange(blocks) => {
info!(
kind = "blocks_by_range_response",
peer_count,
count = blocks.len(),
"P2P message received"
);
}
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.

P2 BlocksByRange response handler is a no-op stub

When this node receives a BlocksByRange response from a peer, the blocks are logged and then silently dropped — no processing, storage, or forwarding takes place. This is fine for now since the node doesn't yet issue outbound BlocksByRange requests, but if a future caller sends such a request, all returned blocks will be discarded without any diagnostic. Consider adding a warn! that makes the "not yet implemented" intent explicit.

Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/net/p2p/src/req_resp/handlers.rs
Line: 75-82

Comment:
**BlocksByRange response handler is a no-op stub**

When this node receives a `BlocksByRange` response from a peer, the blocks are logged and then silently dropped — no processing, storage, or forwarding takes place. This is fine for now since the node doesn't yet issue outbound `BlocksByRange` requests, but if a future caller sends such a request, all returned blocks will be discarded without any diagnostic. Consider adding a `warn!` that makes the "not yet implemented" intent explicit.

How can I resolve this? If you propose a fix, please make it concise.

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
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.

Add inbound BlocksByRange request support

1 participant