Skip to content

SSZ-REST Engine API Transport for Prysm#16784

Open
Dyslex7c wants to merge 27 commits into
OffchainLabs:developfrom
Dyslex7c:ssz
Open

SSZ-REST Engine API Transport for Prysm#16784
Dyslex7c wants to merge 27 commits into
OffchainLabs:developfrom
Dyslex7c:ssz

Conversation

@Dyslex7c

Copy link
Copy Markdown

Spec-aligned changes on top of the PR #16447

Implements SSZ-REST transport for the CL↔EL Engine API in Prysm, as specified in ethereum/execution-apis#764.

When connected to an EL that serves SSZ-REST endpoints on the Engine API port (/engine/v{N}/...), Prysm uses binary SSZ encoding instead of JSON-RPC for all Engine API calls — newPayload, forkchoiceUpdated, getPayload, getBlobs, and exchangeCapabilities.

What this PR does

  • SSZ-REST client (sszrest_client.go): HTTP client that encodes requests as SSZ (application/octet-stream) and sends them to REST endpoints on the engine port
  • SSZ encoding/decoding (sszrest_encoding.go, sszrest_types.go): SSZ container definitions for all Engine API request/response types, using List[T, 1] for nullable fields per spec
  • Transparent fallback: If any SSZ-REST call fails (network error, 4xx, encoding mismatch), Prysm falls back to JSON-RPC for that call — the chain always progresses
  • Same port: SSZ-REST is served on the existing engine port (default 8551) under /engine/* paths, no additional configuration needed
  • Fulu support: BlobsBundleV2 encoding for Fulu epoch and later
  • Dockerfiles for kurtosis devnet testing

Endpoints

Method Endpoint
new_payload POST /engine/v{N}/payloads
forkchoice_updated POST /engine/v3/forkchoice
get_payload GET /engine/v{N}/payloads/{payload_id}
get_blobs POST /engine/v1/blobs
exchange_capabilities POST /engine/v1/capabilities

Spec

  • Transport spec: ethereum/execution-apis#764
  • Benchmarks: SSZ encoding is 4–8× faster than JSON-RPC at 72 blobs, with 2× smaller wire size

Co-Authored-By: Claude noreply@anthropic.com

Giulio2002 and others added 25 commits March 2, 2026 00:21
Implements the CL side of EIP-8161, enabling Prysm to communicate with
the EL via SSZ-REST instead of JSON-RPC when the EL advertises the
ssz_rest channel via EIP-8160.

- SSZ-REST client with automatic discovery via
  engine_getClientCommunicationChannelsV1
- SSZ encode/decode for all Engine API request/response types
- Transparent fallback to JSON-RPC on network errors
- Periodic channel refresh (every 5 minutes)
- --disable-ssz-rest flag to force JSON-RPC only
- Container-aware URL resolution (handles 0.0.0.0 advertised addresses)
- BlobsBundleV2 support for Fulu epoch and later
- Unit tests for SSZ encoding round-trips

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Call engine_exchangeCapabilitiesV2 first to get both capabilities and
supportedProtocols in a single request. Falls back to V1 + separate
GetClientCommunicationChannelsV1 if V2 is not supported.

This follows the updated EIP-8160 spec where protocol discovery is
integrated into the capability exchange handshake.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove getClientCommunicationChannels, exchangeCapabilitiesV2, and
protocol discovery. Replace --disable-ssz-rest with --ssz-rest-url flag
that directly specifies the EL's SSZ-REST endpoint. Add Dockerfile and
Dockerfile.validator for kurtosis devnet support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ding

Replace hand-written binary.LittleEndian offset encoding with
ssz.WriteOffset()/ssz.ReadOffset() from fastssz library for proper
SSZ container/list encoding in EIP-8161 ExchangeCapabilities.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…SSZ-REST

- new_payload → POST /engine/v{N}/payloads
- forkchoice_updated → POST /engine/v3/forkchoice
- get_payload → GET /engine/v{N}/payloads/{payload_id}
- get_blobs → POST /engine/v1/blobs
- exchange_capabilities → POST /engine/v1/capabilities
- get_client_version → POST /engine/v1/client/version
- Error responses now text/plain instead of JSON
- Added doGetRequest/doHTTP for GET support

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Per execution-apis PR OffchainLabs#764 spec:
- PayloadStatus.latest_valid_hash: List[Hash32, 1]
- ForkchoiceUpdatedResponse.payload_id: List[Bytes8, 1]
- ForkchoiceUpdatedRequest.payload_attributes: List[PayloadAttributes, 1]

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SSZ-REST URL is now derived from the engine endpoint (same host:port).
The EL serves SSZ-REST on the engine port under /engine/* paths.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously, only network-level errors (connection refused, timeout)
triggered JSON-RPC fallback. Protocol-level errors (400 bad request,
encoding mismatches) were returned directly, which could block
slot processing entirely. Now all SSZ-REST errors fall back to
JSON-RPC, ensuring the chain progresses even if SSZ encoding is wrong.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Each engine method wrapped with SSZ-REST used to retry over JSON-RPC on
any error — including payload-status sentinels (SYNCING/ACCEPTED/INVALID)
which are valid engine responses, not transport failures. That doubled
the round-trip cost of every non-VALID response during sync and hid real
SSZ transport errors under a fallback.

Now: when SSZ-REST is active the SSZ call's result is propagated
directly — success, status sentinels, and transport errors all surface
as-is. JSON-RPC is kept only as the bootstrap path for the very first
exchange_capabilities, which fires before setupSSZRestClient runs.

Applies to NewPayload, ForkchoiceUpdated, GetPayload,
ExchangeCapabilities, GetBlobs, and GetClientVersionV1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@CLAassistant

CLAassistant commented May 13, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

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.

3 participants