feat: add stateless path to publishExecutionPayloadEnvelope#9401
feat: add stateless path to publishExecutionPayloadEnvelope#9401ensi321 wants to merge 7 commits into
Conversation
External builders and stateless validator clients (multi-BN, DVT, failover) submit the envelope wrapped with blobs and KZG proofs per beacon-APIs PR #580. Lodestar previously parsed the body as a bare SignedExecutionPayloadEnvelope, so the wrapped request failed with "JSON is not an array" and the resulting payloads were orphaned because no data column sidecars were gossiped. - Add Gloas.SignedExecutionPayloadEnvelopeContents SSZ container - Extend POST /eth/v1/beacon/execution_payload_envelope to accept either shape (JSON discriminates by `signed_execution_payload_envelope` key; SSZ tries the wrapper first then falls back to the bare envelope) and forward an optional broadcast_validation query - Wire supplied blobs + kzg_proofs through getGloasDataColumnSidecars in the publish handler so data columns are derived and gossiped on the external-builder path 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request implements the stateless mode for the publishExecutionPayloadEnvelope endpoint, allowing validator clients to supply blobs and KZG proofs alongside the execution payload envelope. Key changes include updating the API route definitions to handle the new SignedExecutionPayloadEnvelopeContents wrapper in both JSON and SSZ formats, adding a new test suite for these routes, and updating the beacon node implementation to process supplied blobs by computing cells and generating data column sidecars. Feedback suggests including the broadcastValidation parameter in the implementation for consistency with the API definition and switching to asynchronous cell computation to prevent blocking the event loop during intensive operations.
Match the route's exposed argument shape so the handler signature remains stable once broadcast_validation enforcement is implemented. Behavior unchanged (still defaults to gossip-level validation). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Performance Report✔️ no performance regression detected Full benchmark results
|
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…al wrapper inputs - publishExecutionPayloadEnvelope handler now checks supplied blob count matches bid.blobKzgCommitments before building data column sidecars - writeReqJson/writeReqSsz throw when only one of blobs/kzgProofs is supplied Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
@gemini-code-assist can you do another round of review please? |
There was a problem hiding this comment.
Code Review
This pull request implements stateless publishing for the POST /eth/v1/beacon/execution_payload_envelope endpoint per beacon-APIs PR #580, allowing clients to supply blobs and kzgProofs alongside the envelope. It introduces the SignedExecutionPayloadEnvelopeContents wrapper, updates route serialization/deserialization logic for both JSON and SSZ formats, and implements the stateless publishing path in the beacon node API. Feedback focuses on optimizing SSZ deserialization by replacing a slow try-catch block with a deterministic offset check, and adding explicit validation to reject requests that supply only one of blobs or kzgProofs.
…t partial blob inputs in handler - parseReqSsz checks first 4 bytes (offset 12 = Contents, 100 = bare envelope) instead of try/catch deserialize for faster, deterministic dispatch - publishExecutionPayloadEnvelope handler throws on partial blobs/kzgProofs so a client that supplies only one field fails fast instead of silently falling back to the stateful path Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
publishExecutionPayloadEnvelope handler now calls validateCellsAndKzgCommitments against payloadInput.getBlobKzgCommitments() before building data column sidecars. Previously only counts were checked, so malformed proofs could be added to local state and gossiped. Crypto verification now matches the gossip ingest path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## unstable #9401 +/- ##
============================================
+ Coverage 52.46% 52.56% +0.09%
============================================
Files 848 848
Lines 62953 60937 -2016
Branches 4658 4485 -173
============================================
- Hits 33030 32031 -999
+ Misses 29855 28844 -1011
+ Partials 68 62 -6 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fd38e7c2be
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const payloadInput = chain.seenPayloadEnvelopeInputCache.get(blockRootHex); | ||
| if (!payloadInput) { | ||
| throw new ApiError(404, `PayloadEnvelopeInput not found for block root ${blockRootHex}`); |
There was a problem hiding this comment.
Defer payload cache lookup until after slot-alignment wait
Move the seenPayloadEnvelopeInputCache lookup back to after the msToBlockSlot sleep; doing it here makes publishExecutionPayloadEnvelope fail fast with 404 in near-slot submissions where the block/payload pipelines race by a few hundred milliseconds. In the previous ordering, the handler could wait up to MAX_API_CLOCK_DISPARITY_MS and then find the cache entry, but this change rejects those same requests before that grace window, which can break the new stateless external-builder path under normal timing jitter.
Useful? React with 👍 / 👎.
| // sits after a 96-byte signature plus the 4-byte offset header, so its first | ||
| // offset is 100. | ||
| const firstOffset = body.length >= 4 ? body[0] | (body[1] << 8) | (body[2] << 16) | (body[3] << 24) : 0; | ||
| if (firstOffset === 12) { |
There was a problem hiding this comment.
this doesn't make any sense, we need a metadata header to differentiate this, the spec is not complete, left a comment here ethereum/beacon-APIs#580 (comment), I think we should push for introducing a BlindedExecutionPayloadEnvelope for the stateful flow
There was a problem hiding this comment.
I agree. I believe other clients implemented this way. The original plan was we just need something transient so we can test external bids with buildoor in devnet 4 which is now dead.
External builders and stateless validator clients (multi-BN, DVT, failover) submit the envelope wrapped with blobs and KZG proofs per ethereum/beacon-APIs#580.
signed_execution_payload_envelopekey; SSZ tries the wrapper first then falls back to the bare envelope) and forward an optional broadcast_validation query