Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
505b458
wip
james-prysm May 20, 2026
d099a7d
allowing ssz for proposer envelope submission
james-prysm May 20, 2026
01839f4
make sure the validator side uses ssz for default for payload envelope
james-prysm May 21, 2026
25f7246
Merge branch 'develop' into rest-gloas-validation
james-prysm May 21, 2026
b644330
fixing proto gen
james-prysm May 21, 2026
b46ab77
changelog
james-prysm May 21, 2026
50aee36
hard error on cache miss
james-prysm May 21, 2026
a508cd3
Merge branch 'develop' into rest-gloas-validation
james-prysm May 21, 2026
a299148
fixing proto generated file
james-prysm May 21, 2026
cbbb87b
terence's comments
james-prysm May 26, 2026
9e81229
Merge branch 'develop' into rest-gloas-validation
james-prysm May 26, 2026
aebb6fe
adding header to api call + reverting unneeded fast path on state by …
james-prysm May 26, 2026
e87e9c3
require check against head state as this is submission path
james-prysm May 26, 2026
9ddc67e
Merge branch 'develop' into rest-gloas-validation
james-prysm May 26, 2026
76bdd01
fix test
james-prysm May 26, 2026
f774a0e
commenting gossip default as a todo for another pr
james-prysm May 26, 2026
6daf544
gaz
james-prysm May 26, 2026
969294f
Merge branch 'develop' into rest-gloas-validation
james-prysm May 26, 2026
fb3b1c0
Merge branch 'develop' into rest-gloas-validation
james-prysm May 27, 2026
80b8ccf
Update handlers_gloas.go
james-prysm May 27, 2026
cfca674
gofmt
james-prysm May 27, 2026
02d55e2
fixing tests
james-prysm May 27, 2026
7df1423
based on latest changes in https://github.com/ethereum/beacon-APIs/pu…
james-prysm May 28, 2026
f55d283
fixing tracing add skip lint
james-prysm May 28, 2026
f641f7d
fixing security flag on span attribute
james-prysm May 28, 2026
75f0026
pluralizing endpoints based on https://github.com/ethereum/beacon-AP…
james-prysm May 29, 2026
c1633d7
Merge branch 'develop' into rest-gloas-validation
james-prysm May 29, 2026
0d5cb60
fixing comment
james-prysm May 29, 2026
b72deaa
self review matching recent pr updates to include blinded execution p…
james-prysm May 29, 2026
271b0dc
linting
james-prysm May 29, 2026
86e4b7b
Merge branch 'develop' into rest-gloas-validation
james-prysm Jun 1, 2026
6dc65e8
Merge branch 'develop' into rest-gloas-validation
james-prysm Jun 8, 2026
d3323c8
Update proto/prysm/v1alpha1/gloas.proto
james-prysm Jun 8, 2026
432041d
jun's feedback
james-prysm Jun 8, 2026
c957444
Merge branch 'develop' into rest-gloas-validation
james-prysm Jun 8, 2026
7b0affd
missed proto update
james-prysm Jun 8, 2026
5a7c178
Merge branch 'develop' into rest-gloas-validation
james-prysm Jun 9, 2026
9cc939b
Merge branch 'develop' into rest-gloas-validation
james-prysm Jun 9, 2026
b95871b
addressing jun and satyajit's comments
james-prysm Jun 9, 2026
d4f0f07
Merge branch 'develop' into rest-gloas-validation
james-prysm Jun 10, 2026
02023db
Merge branch 'develop' into rest-gloas-validation
james-prysm Jun 10, 2026
8a1a608
Merge branch 'develop' into rest-gloas-validation
james-prysm Jun 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions beacon-chain/rpc/endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ func (s *Service) validatorEndpoints(
template: "/eth/v1/validator/execution_payload_envelope/{slot}",
name: namespace + ".ExecutionPayloadEnvelope",
middleware: []middleware.Middleware{
middleware.AcceptHeaderHandler([]string{api.JsonMediaType}),
middleware.AcceptHeaderHandler([]string{api.JsonMediaType, api.OctetStreamMediaType}),
},
handler: server.ExecutionPayloadEnvelope,
methods: []string{http.MethodGet},
Expand Down Expand Up @@ -935,7 +935,7 @@ func (s *Service) beaconEndpoints(
template: "/eth/v1/beacon/execution_payload_envelope",
name: namespace + ".PublishExecutionPayloadEnvelope",
middleware: []middleware.Middleware{
middleware.ContentTypeHandler([]string{api.JsonMediaType}),
middleware.ContentTypeHandler([]string{api.JsonMediaType, api.OctetStreamMediaType}),
middleware.AcceptHeaderHandler([]string{api.JsonMediaType}),
},
handler: server.PublishExecutionPayloadEnvelope,
Expand Down
2 changes: 2 additions & 0 deletions beacon-chain/rpc/eth/beacon/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ go_library(
"//beacon-chain/core/feed:go_default_library",
"//beacon-chain/core/feed/block:go_default_library",
"//beacon-chain/core/feed/operation:go_default_library",
"//beacon-chain/core/gloas:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/peerdas:go_default_library",
"//beacon-chain/core/transition:go_default_library",
Expand Down Expand Up @@ -111,6 +112,7 @@ go_test(
"//beacon-chain/rpc/testutil:go_default_library",
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/state-native:go_default_library",
"//beacon-chain/state/stategen/mock:go_default_library",
"//beacon-chain/sync/initial-sync/testing:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
Expand Down
1 change: 1 addition & 0 deletions beacon-chain/rpc/eth/beacon/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import (

const (
broadcastValidationQueryParam = "broadcast_validation"
broadcastValidationGossip = "gossip"
broadcastValidationConsensus = "consensus"
broadcastValidationConsensusAndEquivocation = "consensus_and_equivocation"
)
Expand Down
131 changes: 109 additions & 22 deletions beacon-chain/rpc/eth/beacon/handlers_gloas.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package beacon

import (
"bytes"
"context"
"encoding/json"
"io"
Expand All @@ -9,6 +10,7 @@ import (
"github.com/OffchainLabs/prysm/v7/api"
"github.com/OffchainLabs/prysm/v7/api/server/structs"
"github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain/kzg"
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/gloas"
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/peerdas"
"github.com/OffchainLabs/prysm/v7/beacon-chain/db"
"github.com/OffchainLabs/prysm/v7/beacon-chain/rpc/eth/shared"
Expand Down Expand Up @@ -104,27 +106,51 @@ func (s *Server) PublishExecutionPayloadEnvelope(w http.ResponseWriter, r *http.
return
}

// Contents wraps the signed envelope alongside blobs/kzg_proofs; the
// wrapper key distinguishes it from a bare envelope body.
var probe map[string]json.RawMessage
if err := json.Unmarshal(body, &probe); err != nil {
httputil.HandleError(w, "could not decode request body: "+err.Error(), http.StatusBadRequest)
return
}
if _, isContents := probe["signed_execution_payload_envelope"]; isContents {
s.publishExecutionPayloadEnvelopeContents(ctx, w, body)
return
}
var consensus *eth.SignedExecutionPayloadEnvelope
if httputil.IsRequestSsz(r) {
// Dispatch by SSZ lead offset: 12 = Contents, 100 = bare envelope.
contentsLeadOffset := []byte{12, 0, 0, 0}
if bytes.HasPrefix(body, contentsLeadOffset) {
contents := &eth.SignedExecutionPayloadEnvelopeContents{}
if err := contents.UnmarshalSSZ(body); err != nil {
httputil.HandleError(w, "could not decode SSZ envelope contents: "+err.Error(), http.StatusBadRequest)
return
}
s.publishExecutionPayloadEnvelopeContentsSSZ(ctx, w, r, contents)
return
}
consensus = &eth.SignedExecutionPayloadEnvelope{}
if err := consensus.UnmarshalSSZ(body); err != nil {
httputil.HandleError(w, "could not decode SSZ envelope: "+err.Error(), http.StatusBadRequest)
return
}
} else {
// Contents wraps the signed envelope alongside blobs/kzg_proofs; the
// wrapper key distinguishes it from a bare envelope body.
var probe map[string]json.RawMessage
if err := json.Unmarshal(body, &probe); err != nil {
httputil.HandleError(w, "could not decode request body: "+err.Error(), http.StatusBadRequest)
return
}
if _, isContents := probe["signed_execution_payload_envelope"]; isContents {
s.publishExecutionPayloadEnvelopeContents(ctx, w, r, body)
return
}

var jsonEnvelope structs.SignedExecutionPayloadEnvelope
if err := json.Unmarshal(body, &jsonEnvelope); err != nil {
httputil.HandleError(w, "could not decode request body: "+err.Error(), http.StatusBadRequest)
return
var jsonEnvelope structs.SignedExecutionPayloadEnvelope
if err := json.Unmarshal(body, &jsonEnvelope); err != nil {
httputil.HandleError(w, "could not decode request body: "+err.Error(), http.StatusBadRequest)
return
}
consensus, err = jsonEnvelope.ToConsensus()
if err != nil {
httputil.HandleError(w, "invalid signed execution payload envelope: "+err.Error(), http.StatusBadRequest)
return
}
}

consensus, err := jsonEnvelope.ToConsensus()
if err != nil {
httputil.HandleError(w, "invalid signed execution payload envelope: "+err.Error(), http.StatusBadRequest)
if err := s.validateEnvelopeBroadcast(ctx, r, consensus); err != nil {
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
Comment thread
syjn99 marked this conversation as resolved.
Outdated
return
}

Expand All @@ -145,10 +171,8 @@ func (s *Server) PublishExecutionPayloadEnvelope(w http.ResponseWriter, r *http.
w.WriteHeader(http.StatusOK)
}

// publishExecutionPayloadEnvelopeContents handles the stateless variant:
// verifies caller-supplied blobs/proofs, broadcasts derived sidecars, then
// delegates the envelope to the bare publish path.
func (s *Server) publishExecutionPayloadEnvelopeContents(ctx context.Context, w http.ResponseWriter, body []byte) {
// publishExecutionPayloadEnvelopeContents handles the JSON stateless variant.
func (s *Server) publishExecutionPayloadEnvelopeContents(ctx context.Context, w http.ResponseWriter, r *http.Request, body []byte) {
var contents structs.SignedExecutionPayloadEnvelopeContents
if err := json.Unmarshal(body, &contents); err != nil {
httputil.HandleError(w, "could not decode envelope contents: "+err.Error(), http.StatusBadRequest)
Expand All @@ -159,6 +183,25 @@ func (s *Server) publishExecutionPayloadEnvelopeContents(ctx context.Context, w
httputil.HandleError(w, "invalid signed execution payload envelope contents: "+err.Error(), http.StatusBadRequest)
return
}
s.processEnvelopeContents(ctx, w, r, signed, kzgProofs, blobs)
}

// publishExecutionPayloadEnvelopeContentsSSZ handles the SSZ stateless variant.
func (s *Server) publishExecutionPayloadEnvelopeContentsSSZ(ctx context.Context, w http.ResponseWriter, r *http.Request, contents *eth.SignedExecutionPayloadEnvelopeContents) {
if contents == nil || contents.SignedExecutionPayloadEnvelope == nil {
httputil.HandleError(w, "nil signed execution payload envelope contents", http.StatusBadRequest)
return
}
s.processEnvelopeContents(ctx, w, r, contents.SignedExecutionPayloadEnvelope, contents.KzgProofs, contents.Blobs)
}

// processEnvelopeContents verifies caller-supplied blobs/proofs, broadcasts
// derived sidecars, then delegates the envelope to the bare publish path.
func (s *Server) processEnvelopeContents(ctx context.Context, w http.ResponseWriter, r *http.Request, signed *eth.SignedExecutionPayloadEnvelope, kzgProofs, blobs [][]byte) {
if err := s.validateEnvelopeBroadcast(ctx, r, signed); err != nil {
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
return
}

if len(blobs) > 0 {
blockRoot := bytesutil.ToBytes32(signed.Message.BeaconBlockRoot)
Expand Down Expand Up @@ -207,6 +250,50 @@ func (s *Server) publishExecutionPayloadEnvelopeContents(ctx context.Context, w
w.WriteHeader(http.StatusOK)
}

// validateEnvelopeBroadcast applies broadcast_validation semantics to an
// envelope publish before it is broadcast to gossip. Spec: beacon-APIs #580.
// - gossip (default): no extra checks.
// - consensus: run full envelope consensus checks (signature + payload
// consistency against the pre-state at envelope.beacon_block_root).
// - consensus_and_equivocation: consensus + reject if a different beacon
// block at the envelope's slot has already been received.
func (s *Server) validateEnvelopeBroadcast(ctx context.Context, r *http.Request, signed *eth.SignedExecutionPayloadEnvelope) error {
level := r.URL.Query().Get(broadcastValidationQueryParam)
switch level {
case "", broadcastValidationGossip:
return nil
case broadcastValidationConsensus, broadcastValidationConsensusAndEquivocation:
default:
return errors.Errorf("invalid %s value: %q", broadcastValidationQueryParam, level)
}

envSlot := primitives.Slot(signed.Message.Payload.SlotNumber)
envRoot := bytesutil.ToBytes32(signed.Message.BeaconBlockRoot)

if level == broadcastValidationConsensusAndEquivocation {
if s.ForkchoiceFetcher.HighestReceivedBlockSlot() == envSlot &&

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

what happens if you submit a payload late?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

changed is this more appropriate?

s.ForkchoiceFetcher.HighestReceivedBlockRoot() != envRoot {
return errors.Wrapf(errEquivocatedBlock, "another block for slot %d already exists in fork choice", envSlot)
}
}

st, err := s.StateGenService.StateByRoot(ctx, envRoot)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is going to be slow right

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

you're right, it's probably wrong too, we should have a hard requirement that it's head, is a race condition an issue? if we haven't processed block yet for state?

if err != nil {
return errors.Wrap(err, "could not get state for envelope beacon block root")
}
if st == nil || st.IsNil() {
return errors.Errorf("could not get state for envelope beacon block root %#x", envRoot)
}
roSigned, err := consensusblocks.WrappedROSignedExecutionPayloadEnvelope(signed)
if err != nil {
return errors.Wrap(err, "could not wrap signed envelope")
}
if err := gloas.VerifyExecutionPayloadEnvelope(ctx, st, roSigned); err != nil {
return errors.Wrap(err, "consensus validation failed")
}
return nil
}

// verifyCellProofs batch-verifies cell proofs against commitments derived
// from the supplied blobs. Does not tie data to a specific block — that needs
// the block's BlobKzgCommitments which a stateless receiver may not have.
Expand Down
Loading
Loading