Skip to content

fix(node): wire inbound ReqResp block serving into the running node#1193

Open
adust09 wants to merge 1 commit into
leanEthereum:mainfrom
adust09:fix/wire-reqresp-block-lookups
Open

fix(node): wire inbound ReqResp block serving into the running node#1193
adust09 wants to merge 1 commit into
leanEthereum:mainfrom
adust09:fix/wire-reqresp-block-lookups

Conversation

@adust09

@adust09 adust09 commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Closes #1192.

A running node answered every BlocksByRoot and BlocksByRange request with SERVER_ERROR because the handler's block lookup callbacks were only ever assigned in tests. The client side is wired into the production path, so two leanSpec nodes issued range requests neither could answer and backfill sync between them could not complete.

What this does

Retain signed blocks for serving (node/sync/service.py). The forkchoice store keeps unsigned blocks only, and a requesting peer verifies each served block's proof on import (fork_choice.py calls signature verification inside block import). Serving store blocks rewrapped with an empty proof would therefore be rejected at the receiver. Block processing now retains each signed block, keyed by root, and prunes entries below the sliding history window that MIN_SLOTS_FOR_BLOCK_REQUESTS defines, matching the window the responder already enforces. Two lookups sit on top: one by root, one by canonical slot (a parent-link walk from the head, so reorgs resolve at request time).

Wire the lookups into the responder (event_source/live.py, cli/run.py). The event source gains a by-slot setter to match the existing by-root one, and the serving bring-up now takes both lookups as required arguments, setting them before any peer can reach the responder. The boot sequence passes the sync service's lookups through.

The retention is in-memory, so a restarted node serves nothing until new blocks arrive. Persisting signed blocks in the database would close that gap and can come separately.

Testing

  • New unit tests cover retention by root, canonical-slot lookup (filled slot, empty slot, slot above head), window pruning, and setter propagation to the handler.
  • uv run pytest tests/node/sync/test_service.py tests/node/networking/client/event_source/test_live.py tests/cli/test_run.py: 73 passed.
  • Lint, format, and type checks pass.

The block lookup callbacks on the inbound request handler were never
assigned outside tests, so a running node answered every BlocksByRoot
and BlocksByRange request with SERVER_ERROR while its own client side
kept issuing range requests no leanSpec peer could answer.

The forkchoice store keeps unsigned blocks only, and a requesting peer
verifies each served block's proof on import, so serving store blocks
rewrapped with an empty proof would be rejected at the receiver. The
sync service now retains processed signed blocks inside the sliding
serving-history window and exposes root and canonical-slot lookups.
The boot sequence wires those lookups into the event source before it
starts serving.

Closes leanEthereum#1192
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.

Inbound ReqResp serving is never wired into the running node

1 participant