Skip to content

feat: prepare snap v2 vision#9

Draft
lean-apple wants to merge 16 commits into
mainfrom
feat/snap2-protocol
Draft

feat: prepare snap v2 vision#9
lean-apple wants to merge 16 commits into
mainfrom
feat/snap2-protocol

Conversation

@lean-apple

@lean-apple lean-apple commented Jun 23, 2026

Copy link
Copy Markdown
Owner

Maps the reth-side integration path for EIP-8189 snap/2.

The intended direction is v2-first: typed snap/2 message validity, a dedicated eth + snap/2 negotiated stream, SnapClient routed through the session manager, response correlation in ActiveSession, BAL verification, and the first server behavior for GetBlockAccessLists.

snap/2 integration map

  • Protocol validity and wire typing

    • snap/2 keeps message IDs 0x00..0x05;
    • snap/2 rejects removed trie-node messages 0x06/0x07;
    • snap/2 adds BAL messages 0x08/0x09;
    • invalid IDs and malformed payloads should be rejected explicitly;
    • message validity should live in one place so snap/1 and snap/2 rules cannot bleed together.
  • Dedicated eth + snap/2 transport

    • snap/2 is negotiated as an RLPx capability next to eth;
    • reth uses a typed EthSnapStream for exactly eth + snap/2;
    • peers with additional satellite protocols should stay on the generic satellite path;
    • snap/2 advertisement remains default-off while the implementation is incomplete.
  • Client request routing

    • SnapClient routes through SessionManager;
    • ActiveSession owns the actual connection and response correlation;
    • outbound snap requests get connection-local request IDs;
    • inflight requests need timeouts;
    • this avoids a separate snap peer registry and keeps request ownership in the session layer.
  • BAL verification

    • received BALs are decoded canonically;
    • verification uses block_access_list_hash, not raw-byte hashing;
    • BALs must be applied in strict block order;
    • missing required BALs are hard failures for sync progress;
    • truncated response prefixes are valid and should be retried from the remaining tail.
  • First server behavior: BAL serving

    • inbound GetBlockAccessLists is the first useful interop target;
    • serving uses BalStoreHandle;
    • responses preserve request order;
    • unavailable BALs are returned as empty entries;
    • responses may truncate from the tail by byte/QoS limits;
    • the peer's request ID is echoed.
  • Bulk-state serving is intentionally separate

    • GetAccountRange, GetStorageRanges, and GetByteCodes require state range iteration and proof handling;
    • these should not be mixed into the BAL server step;
    • snap/2 should not serve snap/1 trie-node messages.
  • Sync stage boundary

    • the snap sync stage should not be wired into the default pipeline yet;
    • if a scaffold exists, it must fail explicitly while unimplemented;
    • it should not silently return success;
    • real stage wiring belongs after transport, client routing, BAL exchange, proof verification, persistence, BAL application, and final root verification are defined.
  • Later cleanup

    • remove or isolate old snap/1-only leftovers;
    • keep only shared bulk-state wire types that snap/2 actually uses;

Already done

  • Extracts shared BAL response construction for ETH/71.
  • Preserves request order for BAL responses.
  • Returns empty entries for missing BALs.
  • Applies MAX_BLOCK_ACCESS_LISTS_SERVE.
  • Applies the soft response byte limit.
  • Logs BAL store read errors.
  • Adds focused tests for response byte cap, normal missing-BAL response, and max-serve truncation.
  • Prepares the helper that snap/2 GetBlockAccessLists serving can reuse later.

TODO/ to continue in prs

  • Add snap/2 capability advertisement and negotiation.
  • Add the dedicated eth + snap/2 stream/session path.
  • Route a SnapClient through the session manager.
  • Add request-id correlation and timeout handling for snap responses.
  • Add snap/2 message validity checks for removed 0x06/0x07.
  • Add BAL client fetch and verification against block_access_list_hash.
  • Add tests covering non-empty BAL store responses through this helper.
  • Add bulk-state snap/2 client requests for account ranges, storage ranges, and bytecodes.
  • Add bulk-state snap/2 server handlers for account ranges, storage ranges, and bytecodes.
  • Add proof verification for account and storage range responses.
  • Persist verified state ranges.
  • Apply verified BAL diffs in strict block order.
  • Implement a real snap sync stage.
  • Wire snap sync into the node pipeline.

Decode received BALs with DecodedBal::from_rlp_bytes and compare against the
header's block_access_list_hash using the canonical EIP-7928 hash of the
decoded list, instead of keccak256 of the raw bytes. Surface distinct missing,
malformed, and hash-mismatch errors and return the decoded BAL. Aligns snap/2
verification with execution/payload validation.
poll_flush now drains the eth/snap proxy channels onto the wire before
reporting flushed, and poll_next surfaces flush errors instead of
dropping them. decode_versioned returns a typed SnapProtocolError
(surfaced as EthStreamError::InvalidSnapMessage), so malformed bodies are
distinguished from invalid or removed (0x06/0x07) message ids.
…ted stream

SnapClient now forwards requests over an mpsc channel to the SessionManager,
which routes each to the first snap-capable active session. The session sends
it via EthRlpxConnection::start_send_snap, assigns a connection-unique
request_id, tracks it in an inflight map with a deadline, and resolves the
client's future when the matching response arrives (or on timeout).

Removes the temporary SnapPeers registry and the SnapProtocolHandler /
SnapConnection satellite path; snap is now driven entirely through the
dedicated EthSnapStream. Inbound snap requests are not served yet.
Thread a BalStoreHandle from NetworkConfig through the SessionManager into
each ActiveSession (default no-op store; set via NetworkConfigBuilder::
with_bal_store). Inbound snap/2 requests are now served by a pure helper in
snap/server.rs: GetBlockAccessLists is answered from the BAL store with
missing entries as empty, request order preserved, the tail truncated at the
response-byte/QoS limit, and the peer's request_id echoed.

Bulk-state ranges (GetAccountRange / GetStorageRanges / GetByteCodes) are not
served yet; they need range iteration and boundary proofs.
… guard

- Served snap responses now count toward request-response backpressure:
  OutgoingMessage::Snap delegates to SnapProtocolMessage::is_response, so a
  peer can't grow the outgoing queue past the existing response throttle.
- fetch_and_verify_bals validates the response itself (it is generic over any
  SnapClient): rejects a mismatched request_id and a response carrying more
  entries than requested (tail truncation is still allowed).
- SnapSyncStage::execute fails explicitly instead of reporting a no-op
  done: true, so the scaffolding can't be wired in as a silent success.
- BAL server logs store-lookup errors instead of silently returning empty.

Adds tests for response classification, BAL response validation, and the
existing serve behavior.
- e2e: a GetBlockAccessLists request and its BlockAccessLists response
  round-trip over two live EthSnapStreams (real handshake + demux).
- make serve_snap_request's match exhaustive (no catch-all) so a new snap
  message forces a decision, and update the SnapSyncStage module doc to
  reflect that execute now fails explicitly.
- unit tests: SnapProtocolMessage::set_request_id, serve hash-count
  truncation to the per-request limit, and ignoring an unknown snap
  response id without panicking or queuing output.
Lightweight workflow on ubuntu-latest that builds and runs the snap,
eth-wire and session tests (including the live EthSnapStream e2e) and
checks the snap sync stage compiles. Faster feedback than the full unit
matrix for this branch.
Rely on the inherited reth workflows (which fall back to the GitHub-native
ubuntu-latest runner on forks) instead of a dedicated snap job.
Adds an in-memory BalStore returning real bytes for known hashes so the
serve helper is exercised against a populated store (present hash returns
its bytes, missing hash is an empty entry, request order preserved), not
just the no-op store.
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