Skip to content

SSZ-REST Engine API Transport for Prysm#16447

Draft
Giulio2002 wants to merge 11 commits intoOffchainLabs:developfrom
Giulio2002:eip-8161-ssz-rest
Draft

SSZ-REST Engine API Transport for Prysm#16447
Giulio2002 wants to merge 11 commits intoOffchainLabs:developfrom
Giulio2002:eip-8161-ssz-rest

Conversation

@Giulio2002
Copy link
Copy Markdown
Contributor

@Giulio2002 Giulio2002 commented Mar 1, 2026

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

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>
@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@Giulio2002 Giulio2002 changed the title [WIP] EIP-8161: SSZ-REST Engine API transport [WIP] Experiment Mar 1, 2026
@Giulio2002 Giulio2002 changed the title [WIP] Experiment [WIP] Big secret Mar 1, 2026
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>
Copy link
Copy Markdown
Contributor

@potuz potuz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! is this something EL clients will be moving to? We'd be happy to move the engine calls to SSZ!

type sszRestClient struct {
baseURL string
httpClient *http.Client
mu sync.RWMutex
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.

Is this mutex even used? I couldn't find a guard and there seem to be plenty of races that could be preventing by using this very mutex. Looks like this was probably coded by an AI?

}

// Reconstruct the proto types from the SSZ data.
payload := &pb.ExecutionPayloadDeneb{}
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.

This should probably not be hardcoded to a Deneb Payload.

}
// Check for common network error patterns.
errStr := err.Error()
for _, pattern := range []string{
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.

Could we do this with errors.Is or similar? string matching smells like an easy regret to me.

Giulio2002 and others added 2 commits March 5, 2026 16:57
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>
@Giulio2002 Giulio2002 marked this pull request as draft March 5, 2026 22:49
Giulio2002 and others added 7 commits March 6, 2026 00:01
…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>
@Giulio2002 Giulio2002 changed the title [WIP] Big secret SSZ-REST Engine API Transport for Prysm Mar 8, 2026
@Giulio2002
Copy link
Copy Markdown
Contributor Author

Implements the SSZ-REST Engine API transport spec: ethereum/execution-apis#764

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