p2p/protocols/eth, p2p/sentry: EIP-8159 eth/71 handler + sentry dispatch (PR 2/3)#20794
p2p/protocols/eth, p2p/sentry: EIP-8159 eth/71 handler + sentry dispatch (PR 2/3)#20794
Conversation
7bc4dba to
6a4a70c
Compare
…59 Phase 3)
Server-side handler for the eth/71 GetBlockAccessLists message. Returns
positionally-aligned RLP-encoded BALs sourced from rawdb.ReadBlockAccessListBytes,
with 0xc0 (empty RLP list) as the "not available" sentinel — same convention
the spec specifies. Disambiguation between "peer doesn't have it" and
"block genuinely has an empty BAL" happens at the caller via
keccak256(payload) == header.BlockAccessListHash; the handler does not
distinguish the cases.
Changes:
- p2p/protocols/eth/handler.go: add MaxBlockAccessListsServe = 1024 limit
constant (alongside MaxBodiesServe, maxReceiptsServe).
- p2p/protocols/eth/handlers.go: add AnswerGetBlockAccessListsQuery. Mirrors
AnswerGetBlockBodiesQuery — iterates requested hashes, resolves block
number via blockReader.HeaderNumber, fetches BAL via
rawdb.ReadBlockAccessListBytes, appends the RLP bytes (or 0xc0 when
missing). Respects softResponseLimit (2 MiB) and the BAL-specific
MaxBlockAccessListsServe cap. Truncates on limit hit; never pads.
- p2p/protocols/eth/handlers_test.go:
- balHeaderReader: minimal services.HeaderReader stub (only HeaderNumber
is exercised by the handler; other methods panic).
- TestAnswerGetBlockAccessListsQuery_OrderedResponseWithMissing: verifies
positional ordering is preserved and that both "unknown block" and
"known block, no BAL" cases return 0xc0 in the correct slot.
- TestAnswerGetBlockAccessListsQuery_SoftSizeLimit: seeds five oversized
BALs and asserts the response is truncated rather than padded, and
that every returned entry is the full BAL (no partial payloads).
No sentry dispatch yet — Phase 4 wires the message codes into
sentry_grpc_server.go and libsentry protocol negotiation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…hase 4)
Wires the wire-protocol substrate (PR Phase 1+2) into the runtime sentry
layer so inbound BAL requests and responses reach subscribers, and so
eth/71 participates in capability negotiation and MinProtocol lookups.
Changes:
- p2p/sentry/libsentry/protocol.go:
- ethProtocolsByVersion: append Protocol_ETH71 so MinProtocol can find
it when resolving a message id to its minimum supporting version.
- ProtoIds: add a Protocol_ETH71 entry cloned from Protocol_ETH70 plus
the two new eth/71 message ids (GET_BLOCK_ACCESS_LISTS_71,
BLOCK_ACCESS_LISTS_71). Peers that advertise eth/71 will have these
message ids in scope for negotiation.
- p2p/sentry/sentry_grpc_server.go:
- Inbound dispatch switch: add cases for GetBlockAccessListsMsg and
BlockAccessListsMsg. Pattern mirrors GetBlockBodiesMsg / BlockBodiesMsg
exactly: no-op when no subscribers, read msg bytes, forward to the
mapped MessageId via send(). The response path (BlockAccessListsMsg)
sets givePermit=true since it completes an in-flight request.
After this commit:
- eth/71 appears in capability negotiation when both peers support it.
- GetBlockAccessLists(0x12) and BlockAccessLists(0x13) inbound messages
are de-multiplexed to the correct subscriber stream via the sentry
Messages gRPC.
- The server-side answer handler added in the previous commit
(AnswerGetBlockAccessListsQuery) can be wired to respond by
subscribing to GET_BLOCK_ACCESS_LISTS_71.
Tested: short tests pass in p2p/sentry, p2p/sentry/libsentry,
p2p/sentry/sentry_multi_client, p2p/protocols/eth; make lint clean;
make erigon builds.
No consumer-side fetcher yet — Phase 5 adds the client that subscribes
to BLOCK_ACCESS_LISTS_71, validates payloads against header-committed
BAL hashes, and applies the bad-peer penalty tracks described in the plan.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…159 update (#11553) ethereum/EIPs#11553 corrected EIP-8159: the positional "not available" sentinel in a BlockAccessLists response is now 0x80 (empty RLP string), not 0xc0 (empty RLP list). Reason: 0xc0 is the canonical RLP encoding of an empty access-list list, so on a chain without system contracts a genuinely empty BAL collides with "I don't have it" — making the two indistinguishable on the wire. 0x80 keeps them distinct. - AnswerGetBlockAccessListsQuery now returns 0x80 for both "unknown block" and "known block, no BAL stored" cases. - Comment + variable rename: emptyRLPList → notAvailableSentinel. - Test asserts 0x80 in those slots. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6a4a70c to
ffb034d
Compare
There was a problem hiding this comment.
Pull request overview
Implements the server-side eth/71 (EIP-8159) Block Access List response handler and wires the new eth/71 message codes through the sentry dispatch layer, enabling peers to request/serve BAL sidecars over p2p.
Changes:
- Add
AnswerGetBlockAccessListsQueryto serve per-hash BAL responses with a soft size limit and positional “not available” signaling. - Extend sentry inbound dispatch to forward
GetBlockAccessLists/BlockAccessListsmessages and release request permits on responses. - Add ETH71 to libsentry protocol/version whitelisting and add handler tests covering ordering, missing entries, and truncation.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| p2p/sentry/sentry_grpc_server.go | Dispatch inbound eth/71 BAL request/response messages to subscribers and manage permits for responses. |
| p2p/sentry/libsentry/protocol.go | Register ETH71 and whitelist the eth/71 BAL message IDs for sentry routing/min-protocol selection. |
| p2p/protocols/eth/handlers.go | Implement BAL query answering logic with sentinel behavior and soft response size truncation. |
| p2p/protocols/eth/handler.go | Introduce a per-request disk-lookup cap constant for BAL serving. |
| p2p/protocols/eth/handlers_test.go | Add unit tests validating ordering/missing semantics and soft-size-limit truncation for BAL responses. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // MaxBlockAccessListsServe is the maximum number of block access lists to | ||
| // serve for an eth/71 GetBlockAccessLists request (EIP-8159). The spec | ||
| // recommends a per-response cap of 2 MiB, which softResponseLimit already | ||
| // enforces; this cap limits the number of disk lookups even when | ||
| // individual BALs are small. | ||
| MaxBlockAccessListsServe = 1024 |
There was a problem hiding this comment.
MaxBlockAccessListsServe is exported, but its usage appears to be internal to the eth package (only referenced in handlers.go). Exporting it increases the public API surface unnecessarily; consider making it unexported (e.g., maxBlockAccessListsServe) unless it’s needed by other packages.
| // MaxBlockAccessListsServe is the maximum number of block access lists to | |
| // serve for an eth/71 GetBlockAccessLists request (EIP-8159). The spec | |
| // recommends a per-response cap of 2 MiB, which softResponseLimit already | |
| // enforces; this cap limits the number of disk lookups even when | |
| // individual BALs are small. | |
| MaxBlockAccessListsServe = 1024 | |
| // maxBlockAccessListsServe is the maximum number of block access lists to | |
| // serve for an eth/71 GetBlockAccessLists request (EIP-8159). The spec | |
| // recommends a per-response cap of 2 MiB, which softResponseLimit already | |
| // enforces; this cap limits the number of disk lookups even when | |
| // individual BALs are small. | |
| maxBlockAccessListsServe = 1024 |
| // MaxBlockAccessListsServe is the maximum number of block access lists to | ||
| // serve for an eth/71 GetBlockAccessLists request (EIP-8159). The spec | ||
| // recommends a per-response cap of 2 MiB, which softResponseLimit already | ||
| // enforces; this cap limits the number of disk lookups even when | ||
| // individual BALs are small. |
There was a problem hiding this comment.
The comment on softResponseLimit says it’s “Used by all protocol versions (eth/68–70)”, but this PR introduces an eth/71 handler that also relies on softResponseLimit. Please update the comment to reflect the new protocol range so the documentation stays accurate.
| // notAvailableSentinel is the RLP encoding of an empty string (0x80). EIP-8159 | ||
| // (post ethereum/EIPs#11553) uses this as the positional "not available" | ||
| // sentinel in a BlockAccessLists response. Earlier drafts used 0xc0 (empty | ||
| // RLP list), but that collides with a genuinely empty BAL — 0xc0 is the | ||
| // canonical encoding of an empty access-list list — so "unavailable" and | ||
| // "valid empty BAL" were indistinguishable on the wire. Using 0x80 keeps | ||
| // the two distinct: 0x80 = "I don't have it", 0xc0 = "the BAL really is | ||
| // empty (e.g. a chain without system contracts)". | ||
| var notAvailableSentinel = rlp.RawValue{0x80} |
There was a problem hiding this comment.
PR description says the “not available” signal is 0xc0 (empty RLP list), but the implementation and tests use 0x80 (empty RLP string) as the sentinel. Please update the PR description (or adjust the code) so the documented behavior matches what’s actually implemented.
Summary
Second of three stacked PRs implementing EIP-8159. Adds the server-side answer handler and wires eth/71 message dispatch into the sentry.
Depends on #20793 — review/merge that one first.
What lands
AnswerGetBlockAccessListsQueryin p2p/protocols/eth/handlers.go:HeaderReader.HeaderNumber.rawdb.ReadBlockAccessListBytesand appends RLP to response.0xc0) for any hash not in local rawdb — EIP-8159's "not available" signal.sentry_grpc_server.gofor the two new message codes.libsentry/protocol.go—ETH71added toethProtocolsByVersion,ProtoIdswhitelist extended;MinProtocol(GET_BLOCK_ACCESS_LISTS_71)returnsETH71so only eth/71 peers are queried.TestAnswerGetBlockAccessListsQuery_OrderedResponseWithMissing— asserts positional ordering, plus unknown-block and known-block-no-BAL both return0xc0in the correct slot.TestAnswerGetBlockAccessListsQuery_SoftSizeLimit— seeds 5 oversized BALs, asserts truncation (no partial payloads in the response).What's NOT here
No consumer-side fetcher yet — peers that speak eth/71 can answer our queries, but we don't send any. That's PR 3.
Stack
Test plan
go test -short ./p2p/protocols/eth/... ./p2p/sentry/...make lint(0 issues)make erigon integration