engine: add Rest-SSZ spec - #793
Conversation
|
One thing that I think would make sense would be to reuse the base structure of the beacon api (https://github.com/ethereum/beacon-APIs/) - this includes several things:
|
For top-up sync we also need "current block number", similar to |
|
I opened #773 a few weeks ago with a narrower scope: adding a single new endpoint ( Since #793 is now doing a full Engine API refactor with the same REST+SSZ foundation, I think the witness endpoint fits naturally into this design. A few thoughts: The witness endpoint should be added to this new spec. The existing two-call flow ( Suggested endpoint: Benchmark data (ethrex, 203 txs, 36 Mgas, ~502 MB SSZ witness):
Happy to close #773 in support of this PR I think it's a better, more seamless flow. Would love to discuss where the witness endpoint fits best in the new endpoint table. |
|
|
||
| | Old method | New endpoint | Notes | | ||
| | - | - | - | | ||
| | `engine_newPayloadV{1..5}` | `POST /{fork}/payloads` | `parentBeaconBlockRoot` and `executionRequests` folded into the SSZ envelope; `expectedBlobVersionedHashes` removed; `INVALID_BLOCK_HASH` removed from the status enum | |
There was a problem hiding this comment.
dropping /engine/v2 prefix misleads a bit
There was a problem hiding this comment.
+1 - the beacon api lives under /eth/vX/beacon - would be good to see this api under /eth/vX/engine to unify the approaches
|
|
||
| | Resource | Endpoint | Purpose | | ||
| | - | - | - | | ||
| | Payload | `POST /engine/v2/{fork}/payloads` | Submit a payload received from the CL gossip network for the EL to validate / import. Replaces `engine_newPayload`. | |
There was a problem hiding this comment.
What does v2 mean? Was there v1? Why is fork necessary in the URL? Fork name seems to be a way to describe minor API version, but on other side blobs endpoints have it after resource name, not before and it's a number
There was a problem hiding this comment.
Also if payload did not change across forks, does it mean we will need same endpoint under different urls? Just single /vN/ looked simpler
|
|
||
| #### Transport | ||
|
|
||
| - **HTTP/2 required**, h2c (cleartext) for both TCP and IPC. No |
There was a problem hiding this comment.
the consensus REST spec does not have this requirement - we use 1.1 throughout and going to 2.0 would not be viable short-term for Nimbus.
There was a problem hiding this comment.
I've spoken with several people about developing streaming extensions for the engine API, some examples: registering to receive all txs from a particular account; receiving all cells for a set of kzg commitments; streaming EL events. For most language/library stacks http/2 is the simplest and most effective tool for this job. A lot of flexibility is introduced by giving the client/server native multiplexing, customizable framing. This could be powerful for some of the syncing options we've discussed like EL being fed blocks by CL.
As a compromise we could define these streaming methods as optional while clients work on introducing http/2 support. In practice http/2 capable libraries also support http/1.1. This is usually negotiated as ALPN during the TLS handshake, but the fallback option defined for plaintext protocols (like the engine api) is an upgrade header: Upgrade: h2c. So when the CL first connects to the EL to determine capabilities, it can make an http/1.1 request with an upgrade header (to confirm http/2 support if the EL server gives the expected upgrade response preamble). These streaming extensions would presumably be themselves represented as capabilities, potentially with a prefix that indicates the L7 protocol (http2/streamBlobsForCommitments).
There was a problem hiding this comment.
during the TLS handshake,
which tls handshake? we're talking about a private el/cl connection that in many cases does not have tls enabled (because enabling it would be an absolute mess of managing certificates etc or disabling verification which is unusual).
multiplexing
just make 2 connections in these cases?
upgrade
sure - nothing prevents an el/cl to talk http/2 - that said, this is also not a public interface serving thousands of clients but rather a private connection for driving one part of the client with another. For the public rpc, it makes sense - for the engine api that sees a few messages per second, it seems overengineered.
There was a problem hiding this comment.
which tls handshake? we're talking about a private el/cl connection that in many cases does not have tls enabled (because enabling it would be an absolute mess of managing certificates etc or disabling verification which is unusual).
I might have confused things with too much superfluous info. I'm not saying TLS is involved, I'm saying it is not involved, which is ok because there is a cleartext upgrade path. More succinctly -- there are clear standards for a single server to support both, so we can define the ssz flavored existing methods as http/1.1 while allowing http/2 capable servers to upgrade their connections to http/2 and provide additional optional methods.
For the public rpc, it makes sense - for the engine api that sees a few messages per second, it seems overengineered
The point isn't about scale/concurrency - I'm not suggesting http/3 here. The appeal is here is actually the simplicity that comes from the shape of the protocol fitting the use case, vs requiring devs to layer on additional complexity in order to compensate for the fact that http/1.1 framing and encoding was built for a different kind of request/response cycle and has acquired technical debt.
Adds the EnableEngineSSZHTTP feature flag (off by default), the gate for the REST + SSZ Engine API v2 transport (ethereum/execution-apis#793). No behavior yet; JSON-RPC engine_* stays the default transport. Wires the flag into ConfigureBeaconChain and covers it with a test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Lays out all eight REST + SSZ Engine API v2 endpoint operations (ethereum/execution-apis#793) as methods on enginehttp.Client: NewPayload, ForkchoiceUpdated, GetPayload, GetPayloadBodiesBy{Hash,Range}, GetBlobs, Capabilities, Identity. Each is a stub returning errNotImplemented with a per-endpoint TODO(ssz-over-http) comment, plus a skipped test pinning the intended call shape, so the Phase 4 empty spots are easy to find. No behavior. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds the EnableEngineSSZHTTP feature flag (off by default), the gate for the REST + SSZ Engine API v2 transport (ethereum/execution-apis#793). No behavior yet; JSON-RPC engine_* stays the default transport. Wires the flag into ConfigureBeaconChain and covers it with a test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Lays out all eight REST + SSZ Engine API v2 endpoint operations (ethereum/execution-apis#793) as methods on enginehttp.Client: NewPayload, ForkchoiceUpdated, GetPayload, GetPayloadBodiesBy{Hash,Range}, GetBlobs, Capabilities, Identity. Each is a stub returning errNotImplemented with a per-endpoint TODO(ssz-over-http) comment, plus a skipped test pinning the intended call shape, so the Phase 4 empty spots are easy to find. No behavior. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Updated the spec according to the results from the discussion |
|
In todays ssz call, we discussed that there are some axuiliary apis also exposed on the engine api. See https://github.com/ethereum/execution-apis/blob/main/src/engine/common.md#underlying-protocol |
|
Feedback from implementing this in Nethermind (our REST+SSZ surface tracks this draft; we've just re-synced against 1.
|
|
We decided its time to get this merged upstream and keep iterating on it on main if necessary |
There have been multiple attempts at this already.
Moving away from JSON-RPC to REST-SSZ.
However most kept the engine api as is.
I think we have a good shot at refactoring the engine api with this change.
This Draft does that; the move and the refactoring.
Happy for any feedback I can get!
The core of the change is:
engine_newPayloadV{1..5}POST /{fork}/payloadsparentBeaconBlockRootandexecutionRequestsfolded into the SSZ envelope;expectedBlobVersionedHashesremoved;INVALID_BLOCK_HASHremoved from the status enumengine_forkchoiceUpdatedV{1..4}POST /{fork}/forkchoicepayload_attributes, and (Amsterdam+) optionalcustody_columnsengine_getPayloadV{1..6}GET /{fork}/payloads/{id}engine_getPayloadBodiesByHashV{1,2}POST /{fork}/bodies/hash{fork}selects the response schema (not the era of requested blocks);POSTbecause hash lists are too large for URLsengine_getPayloadBodiesByRangeV{1,2}GET /{fork}/bodies?from=...&count=...{fork}selects the response schemaengine_getBlobsV1POST /blobs/v1engine_getBlobsV2POST /blobs/v2engine_getBlobsV3POST /blobs/v3engine_getBlobsV4POST /blobs/v4engine_getClientVersionV1GET /identity+X-Engine-Client-Versionrequest headerengine_exchangeCapabilitiesGET /capabilitiesengine_exchangeTransitionConfigurationV1