diff --git a/api/server/structs/BUILD.bazel b/api/server/structs/BUILD.bazel index 6f6e678b6a12..22f23a26e055 100644 --- a/api/server/structs/BUILD.bazel +++ b/api/server/structs/BUILD.bazel @@ -9,6 +9,7 @@ go_library( "conversions_blob.go", "conversions_block.go", "conversions_block_execution.go", + "conversions_block_gloas.go", "conversions_gloas.go", "conversions_lightclient.go", "conversions_state.go", diff --git a/api/server/structs/block.go b/api/server/structs/block.go index 6bfafa69a65f..026baadd7106 100644 --- a/api/server/structs/block.go +++ b/api/server/structs/block.go @@ -614,3 +614,17 @@ type SignedExecutionPayloadEnvelopeContents struct { KzgProofs []string `json:"kzg_proofs"` Blobs []string `json:"blobs"` } + +// BlindedExecutionPayloadEnvelope replaces the full payload with payload_root so its HTR matches the full envelope. +type BlindedExecutionPayloadEnvelope struct { + PayloadRoot string `json:"payload_root"` + ExecutionRequests *ExecutionRequests `json:"execution_requests"` + BuilderIndex string `json:"builder_index"` + BeaconBlockRoot string `json:"beacon_block_root"` + ParentBeaconBlockRoot string `json:"parent_beacon_block_root"` +} + +type SignedBlindedExecutionPayloadEnvelope struct { + Message *BlindedExecutionPayloadEnvelope `json:"message"` + Signature string `json:"signature"` +} diff --git a/api/server/structs/conversions_block.go b/api/server/structs/conversions_block.go index 35227f23971a..6d06cc1140b6 100644 --- a/api/server/structs/conversions_block.go +++ b/api/server/structs/conversions_block.go @@ -3342,168 +3342,3 @@ func (p *PayloadAttestationMessage) ToConsensus() (*eth.PayloadAttestationMessag Signature: sig, }, nil } - -// ExecutionPayloadEnvelopeFromConsensus converts a proto envelope to the API struct. -func ExecutionPayloadEnvelopeFromConsensus(e *eth.ExecutionPayloadEnvelope) (*ExecutionPayloadEnvelope, error) { - payload, err := ExecutionPayloadGloasFromConsensus(e.Payload) - if err != nil { - return nil, err - } - var requests *ExecutionRequests - if e.ExecutionRequests != nil { - requests = ExecutionRequestsFromConsensus(e.ExecutionRequests) - } - return &ExecutionPayloadEnvelope{ - Payload: payload, - ExecutionRequests: requests, - BuilderIndex: fmt.Sprintf("%d", e.BuilderIndex), - BeaconBlockRoot: hexutil.Encode(e.BeaconBlockRoot), - ParentBeaconBlockRoot: hexutil.Encode(e.ParentBeaconBlockRoot), - }, nil -} - -// SignedExecutionPayloadEnvelopeFromConsensus converts a signed proto envelope to the API struct. -func SignedExecutionPayloadEnvelopeFromConsensus(e *eth.SignedExecutionPayloadEnvelope) (*SignedExecutionPayloadEnvelope, error) { - envelope, err := ExecutionPayloadEnvelopeFromConsensus(e.Message) - if err != nil { - return nil, err - } - return &SignedExecutionPayloadEnvelope{ - Message: envelope, - Signature: hexutil.Encode(e.Signature), - }, nil -} - -// BlockContentsGloasFromConsensus converts a proto Gloas block, envelope, and -// blob data to the API struct. -func BlockContentsGloasFromConsensus(block *eth.BeaconBlockGloas, envelope *eth.ExecutionPayloadEnvelope, kzgProofs [][]byte, blobs [][]byte) (*BlockContentsGloas, error) { - b, err := BeaconBlockGloasFromConsensus(block) - if err != nil { - return nil, err - } - env, err := ExecutionPayloadEnvelopeFromConsensus(envelope) - if err != nil { - return nil, err - } - encodedProofs := make([]string, len(kzgProofs)) - for i, p := range kzgProofs { - encodedProofs[i] = hexutil.Encode(p) - } - encodedBlobs := make([]string, len(blobs)) - for i, b := range blobs { - encodedBlobs[i] = hexutil.Encode(b) - } - return &BlockContentsGloas{ - Block: b, - ExecutionPayloadEnvelope: env, - KzgProofs: encodedProofs, - Blobs: encodedBlobs, - }, nil -} - -// ToConsensus converts the API struct to a proto ExecutionPayloadEnvelope. -func (e *ExecutionPayloadEnvelope) ToConsensus() (*eth.ExecutionPayloadEnvelope, error) { - if e == nil { - return nil, server.NewDecodeError(errNilValue, "ExecutionPayloadEnvelope") - } - payload, err := e.Payload.ToConsensus() - if err != nil { - return nil, server.NewDecodeError(err, "Payload") - } - var requests *enginev1.ExecutionRequests - if e.ExecutionRequests != nil { - requests, err = e.ExecutionRequests.ToConsensus() - if err != nil { - return nil, server.NewDecodeError(err, "ExecutionRequests") - } - } - builderIndex, err := strconv.ParseUint(e.BuilderIndex, 10, 64) - if err != nil { - return nil, server.NewDecodeError(err, "BuilderIndex") - } - beaconBlockRoot, err := bytesutil.DecodeHexWithLength(e.BeaconBlockRoot, fieldparams.RootLength) - if err != nil { - return nil, server.NewDecodeError(err, "BeaconBlockRoot") - } - parentBeaconBlockRoot, err := bytesutil.DecodeHexWithLength(e.ParentBeaconBlockRoot, fieldparams.RootLength) - if err != nil { - return nil, server.NewDecodeError(err, "ParentBeaconBlockRoot") - } - return ð.ExecutionPayloadEnvelope{ - Payload: payload, - ExecutionRequests: requests, - BuilderIndex: primitives.BuilderIndex(builderIndex), - BeaconBlockRoot: beaconBlockRoot, - ParentBeaconBlockRoot: parentBeaconBlockRoot, - }, nil -} - -// ToConsensus converts the API struct to a proto SignedExecutionPayloadEnvelope. -func (e *SignedExecutionPayloadEnvelope) ToConsensus() (*eth.SignedExecutionPayloadEnvelope, error) { - if e == nil { - return nil, server.NewDecodeError(errNilValue, "SignedExecutionPayloadEnvelope") - } - msg, err := e.Message.ToConsensus() - if err != nil { - return nil, server.NewDecodeError(err, "Message") - } - sig, err := bytesutil.DecodeHexWithLength(e.Signature, fieldparams.BLSSignatureLength) - if err != nil { - return nil, server.NewDecodeError(err, "Signature") - } - return ð.SignedExecutionPayloadEnvelope{ - Message: msg, - Signature: sig, - }, nil -} - -// SignedExecutionPayloadEnvelopeContentsFromConsensus builds the API struct -// used for stateless envelope publishing from native components. -func SignedExecutionPayloadEnvelopeContentsFromConsensus(signed *eth.SignedExecutionPayloadEnvelope, kzgProofs [][]byte, blobs [][]byte) (*SignedExecutionPayloadEnvelopeContents, error) { - signedJSON, err := SignedExecutionPayloadEnvelopeFromConsensus(signed) - if err != nil { - return nil, err - } - encodedProofs := make([]string, len(kzgProofs)) - for i, p := range kzgProofs { - encodedProofs[i] = hexutil.Encode(p) - } - encodedBlobs := make([]string, len(blobs)) - for i, b := range blobs { - encodedBlobs[i] = hexutil.Encode(b) - } - return &SignedExecutionPayloadEnvelopeContents{ - SignedExecutionPayloadEnvelope: signedJSON, - KzgProofs: encodedProofs, - Blobs: encodedBlobs, - }, nil -} - -// ToConsensus decodes the API struct into the signed envelope plus raw blob and -// KZG proof bytes used by the stateless publish path. -func (c *SignedExecutionPayloadEnvelopeContents) ToConsensus() (*eth.SignedExecutionPayloadEnvelope, [][]byte, [][]byte, error) { - if c == nil { - return nil, nil, nil, server.NewDecodeError(errNilValue, "SignedExecutionPayloadEnvelopeContents") - } - signed, err := c.SignedExecutionPayloadEnvelope.ToConsensus() - if err != nil { - return nil, nil, nil, server.NewDecodeError(err, "SignedExecutionPayloadEnvelope") - } - proofs := make([][]byte, len(c.KzgProofs)) - for i, p := range c.KzgProofs { - proof, err := bytesutil.DecodeHexWithLength(p, 48) - if err != nil { - return nil, nil, nil, server.NewDecodeError(err, fmt.Sprintf("KzgProofs[%d]", i)) - } - proofs[i] = proof - } - blobs := make([][]byte, len(c.Blobs)) - for i, b := range c.Blobs { - blob, err := bytesutil.DecodeHexWithLength(b, fieldparams.BlobSize) - if err != nil { - return nil, nil, nil, server.NewDecodeError(err, fmt.Sprintf("Blobs[%d]", i)) - } - blobs[i] = blob - } - return signed, proofs, blobs, nil -} diff --git a/api/server/structs/conversions_block_gloas.go b/api/server/structs/conversions_block_gloas.go new file mode 100644 index 000000000000..0e6592701aad --- /dev/null +++ b/api/server/structs/conversions_block_gloas.go @@ -0,0 +1,285 @@ +package structs + +import ( + "fmt" + "strconv" + + "github.com/OffchainLabs/prysm/v7/api/server" + fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams" + "github.com/OffchainLabs/prysm/v7/consensus-types/primitives" + "github.com/OffchainLabs/prysm/v7/encoding/bytesutil" + enginev1 "github.com/OffchainLabs/prysm/v7/proto/engine/v1" + eth "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +// ExecutionPayloadEnvelopeFromConsensus converts a proto envelope to the API struct. +func ExecutionPayloadEnvelopeFromConsensus(e *eth.ExecutionPayloadEnvelope) (*ExecutionPayloadEnvelope, error) { + payload, err := ExecutionPayloadGloasFromConsensus(e.Payload) + if err != nil { + return nil, err + } + var requests *ExecutionRequests + if e.ExecutionRequests != nil { + requests = ExecutionRequestsFromConsensus(e.ExecutionRequests) + } + return &ExecutionPayloadEnvelope{ + Payload: payload, + ExecutionRequests: requests, + BuilderIndex: fmt.Sprintf("%d", e.BuilderIndex), + BeaconBlockRoot: hexutil.Encode(e.BeaconBlockRoot), + ParentBeaconBlockRoot: hexutil.Encode(e.ParentBeaconBlockRoot), + }, nil +} + +// SignedExecutionPayloadEnvelopeFromConsensus converts a signed proto envelope to the API struct. +func SignedExecutionPayloadEnvelopeFromConsensus(e *eth.SignedExecutionPayloadEnvelope) (*SignedExecutionPayloadEnvelope, error) { + envelope, err := ExecutionPayloadEnvelopeFromConsensus(e.Message) + if err != nil { + return nil, err + } + return &SignedExecutionPayloadEnvelope{ + Message: envelope, + Signature: hexutil.Encode(e.Signature), + }, nil +} + +// BlockContentsGloasFromConsensus converts a proto Gloas block, envelope, and +// blob data to the API struct. +func BlockContentsGloasFromConsensus(block *eth.BeaconBlockGloas, envelope *eth.ExecutionPayloadEnvelope, kzgProofs [][]byte, blobs [][]byte) (*BlockContentsGloas, error) { + b, err := BeaconBlockGloasFromConsensus(block) + if err != nil { + return nil, err + } + env, err := ExecutionPayloadEnvelopeFromConsensus(envelope) + if err != nil { + return nil, err + } + encodedProofs := make([]string, len(kzgProofs)) + for i, p := range kzgProofs { + encodedProofs[i] = hexutil.Encode(p) + } + encodedBlobs := make([]string, len(blobs)) + for i, b := range blobs { + encodedBlobs[i] = hexutil.Encode(b) + } + return &BlockContentsGloas{ + Block: b, + ExecutionPayloadEnvelope: env, + KzgProofs: encodedProofs, + Blobs: encodedBlobs, + }, nil +} + +// ToConsensus converts the API struct to a proto ExecutionPayloadEnvelope. +func (e *ExecutionPayloadEnvelope) ToConsensus() (*eth.ExecutionPayloadEnvelope, error) { + if e == nil { + return nil, server.NewDecodeError(errNilValue, "ExecutionPayloadEnvelope") + } + payload, err := e.Payload.ToConsensus() + if err != nil { + return nil, server.NewDecodeError(err, "Payload") + } + var requests *enginev1.ExecutionRequests + if e.ExecutionRequests != nil { + requests, err = e.ExecutionRequests.ToConsensus() + if err != nil { + return nil, server.NewDecodeError(err, "ExecutionRequests") + } + } + builderIndex, err := strconv.ParseUint(e.BuilderIndex, 10, 64) + if err != nil { + return nil, server.NewDecodeError(err, "BuilderIndex") + } + beaconBlockRoot, err := bytesutil.DecodeHexWithLength(e.BeaconBlockRoot, fieldparams.RootLength) + if err != nil { + return nil, server.NewDecodeError(err, "BeaconBlockRoot") + } + parentBeaconBlockRoot, err := bytesutil.DecodeHexWithLength(e.ParentBeaconBlockRoot, fieldparams.RootLength) + if err != nil { + return nil, server.NewDecodeError(err, "ParentBeaconBlockRoot") + } + return ð.ExecutionPayloadEnvelope{ + Payload: payload, + ExecutionRequests: requests, + BuilderIndex: primitives.BuilderIndex(builderIndex), + BeaconBlockRoot: beaconBlockRoot, + ParentBeaconBlockRoot: parentBeaconBlockRoot, + }, nil +} + +// ToConsensus converts the API struct to a proto SignedExecutionPayloadEnvelope. +func (e *SignedExecutionPayloadEnvelope) ToConsensus() (*eth.SignedExecutionPayloadEnvelope, error) { + if e == nil { + return nil, server.NewDecodeError(errNilValue, "SignedExecutionPayloadEnvelope") + } + msg, err := e.Message.ToConsensus() + if err != nil { + return nil, server.NewDecodeError(err, "Message") + } + sig, err := bytesutil.DecodeHexWithLength(e.Signature, fieldparams.BLSSignatureLength) + if err != nil { + return nil, server.NewDecodeError(err, "Signature") + } + return ð.SignedExecutionPayloadEnvelope{ + Message: msg, + Signature: sig, + }, nil +} + +func BlindedExecutionPayloadEnvelopeFromConsensus(b *eth.WireBlindedExecutionPayloadEnvelope) (*BlindedExecutionPayloadEnvelope, error) { + if b == nil { + return nil, errNilValue + } + var requests *ExecutionRequests + if b.ExecutionRequests != nil { + requests = ExecutionRequestsFromConsensus(b.ExecutionRequests) + } + return &BlindedExecutionPayloadEnvelope{ + PayloadRoot: hexutil.Encode(b.PayloadRoot), + ExecutionRequests: requests, + BuilderIndex: fmt.Sprintf("%d", b.BuilderIndex), + BeaconBlockRoot: hexutil.Encode(b.BeaconBlockRoot), + ParentBeaconBlockRoot: hexutil.Encode(b.ParentBeaconBlockRoot), + }, nil +} + +func (b *BlindedExecutionPayloadEnvelope) ToConsensus() (*eth.WireBlindedExecutionPayloadEnvelope, error) { + if b == nil { + return nil, server.NewDecodeError(errNilValue, "BlindedExecutionPayloadEnvelope") + } + payloadRoot, err := bytesutil.DecodeHexWithLength(b.PayloadRoot, fieldparams.RootLength) + if err != nil { + return nil, server.NewDecodeError(err, "PayloadRoot") + } + var requests *enginev1.ExecutionRequests + if b.ExecutionRequests != nil { + requests, err = b.ExecutionRequests.ToConsensus() + if err != nil { + return nil, server.NewDecodeError(err, "ExecutionRequests") + } + } + builderIndex, err := strconv.ParseUint(b.BuilderIndex, 10, 64) + if err != nil { + return nil, server.NewDecodeError(err, "BuilderIndex") + } + beaconBlockRoot, err := bytesutil.DecodeHexWithLength(b.BeaconBlockRoot, fieldparams.RootLength) + if err != nil { + return nil, server.NewDecodeError(err, "BeaconBlockRoot") + } + parentBeaconBlockRoot, err := bytesutil.DecodeHexWithLength(b.ParentBeaconBlockRoot, fieldparams.RootLength) + if err != nil { + return nil, server.NewDecodeError(err, "ParentBeaconBlockRoot") + } + return ð.WireBlindedExecutionPayloadEnvelope{ + PayloadRoot: payloadRoot, + ExecutionRequests: requests, + BuilderIndex: primitives.BuilderIndex(builderIndex), + BeaconBlockRoot: beaconBlockRoot, + ParentBeaconBlockRoot: parentBeaconBlockRoot, + }, nil +} + +func (s *SignedBlindedExecutionPayloadEnvelope) ToConsensus() (*eth.SignedWireBlindedExecutionPayloadEnvelope, error) { + if s == nil { + return nil, server.NewDecodeError(errNilValue, "SignedBlindedExecutionPayloadEnvelope") + } + msg, err := s.Message.ToConsensus() + if err != nil { + return nil, server.NewDecodeError(err, "Message") + } + sig, err := bytesutil.DecodeHexWithLength(s.Signature, fieldparams.BLSSignatureLength) + if err != nil { + return nil, server.NewDecodeError(err, "Signature") + } + return ð.SignedWireBlindedExecutionPayloadEnvelope{ + Message: msg, + Signature: sig, + }, nil +} + +// SignedExecutionPayloadEnvelopeContentsFromConsensus builds the API struct +// used for stateless envelope publishing from native components. +func SignedExecutionPayloadEnvelopeContentsFromConsensus(signed *eth.SignedExecutionPayloadEnvelope, kzgProofs [][]byte, blobs [][]byte) (*SignedExecutionPayloadEnvelopeContents, error) { + signedJSON, err := SignedExecutionPayloadEnvelopeFromConsensus(signed) + if err != nil { + return nil, err + } + encodedProofs := make([]string, len(kzgProofs)) + for i, p := range kzgProofs { + encodedProofs[i] = hexutil.Encode(p) + } + encodedBlobs := make([]string, len(blobs)) + for i, b := range blobs { + encodedBlobs[i] = hexutil.Encode(b) + } + return &SignedExecutionPayloadEnvelopeContents{ + SignedExecutionPayloadEnvelope: signedJSON, + KzgProofs: encodedProofs, + Blobs: encodedBlobs, + }, nil +} + +// ToConsensus decodes the API struct into the signed envelope plus raw blob and +// KZG proof bytes used by the stateless publish path. +func (c *SignedExecutionPayloadEnvelopeContents) ToConsensus() (*eth.SignedExecutionPayloadEnvelope, [][]byte, [][]byte, error) { + if c == nil { + return nil, nil, nil, server.NewDecodeError(errNilValue, "SignedExecutionPayloadEnvelopeContents") + } + signed, err := c.SignedExecutionPayloadEnvelope.ToConsensus() + if err != nil { + return nil, nil, nil, server.NewDecodeError(err, "SignedExecutionPayloadEnvelope") + } + proofs := make([][]byte, len(c.KzgProofs)) + for i, p := range c.KzgProofs { + proof, err := bytesutil.DecodeHexWithLength(p, 48) + if err != nil { + return nil, nil, nil, server.NewDecodeError(err, fmt.Sprintf("KzgProofs[%d]", i)) + } + proofs[i] = proof + } + blobs := make([][]byte, len(c.Blobs)) + for i, b := range c.Blobs { + blob, err := bytesutil.DecodeHexWithLength(b, fieldparams.BlobSize) + if err != nil { + return nil, nil, nil, server.NewDecodeError(err, fmt.Sprintf("Blobs[%d]", i)) + } + blobs[i] = blob + } + return signed, proofs, blobs, nil +} + +// WireBlindedFromFull derives the spec-wire blinded envelope from a full one: payload_root is +// HashTreeRoot(payload), so HashTreeRoot(blinded) == HashTreeRoot(full) and a validator signature +// over either form is valid against the other. +func WireBlindedFromFull(full *eth.ExecutionPayloadEnvelope) (*eth.WireBlindedExecutionPayloadEnvelope, error) { + if full == nil { + return nil, nil + } + payloadRoot, err := full.Payload.HashTreeRoot() + if err != nil { + return nil, err + } + return ð.WireBlindedExecutionPayloadEnvelope{ + PayloadRoot: payloadRoot[:], + ExecutionRequests: full.ExecutionRequests, + BuilderIndex: full.BuilderIndex, + BeaconBlockRoot: bytesutil.SafeCopyBytes(full.BeaconBlockRoot), + ParentBeaconBlockRoot: bytesutil.SafeCopyBytes(full.ParentBeaconBlockRoot), + }, nil +} + +// SignedWireBlindedFromFull lifts a signed envelope to its blinded form, preserving the signature. +func SignedWireBlindedFromFull(full *eth.SignedExecutionPayloadEnvelope) (*eth.SignedWireBlindedExecutionPayloadEnvelope, error) { + if full == nil { + return nil, nil + } + msg, err := WireBlindedFromFull(full.Message) + if err != nil { + return nil, err + } + return ð.SignedWireBlindedExecutionPayloadEnvelope{ + Message: msg, + Signature: bytesutil.SafeCopyBytes(full.Signature), + }, nil +} diff --git a/api/server/structs/conversions_block_gloas_test.go b/api/server/structs/conversions_block_gloas_test.go index 1788c674aaa1..3f3771cebcf7 100644 --- a/api/server/structs/conversions_block_gloas_test.go +++ b/api/server/structs/conversions_block_gloas_test.go @@ -5,6 +5,7 @@ import ( "testing" fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams" + "github.com/OffchainLabs/prysm/v7/consensus-types/primitives" enginev1 "github.com/OffchainLabs/prysm/v7/proto/engine/v1" eth "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1" "github.com/OffchainLabs/prysm/v7/testing/require" @@ -54,6 +55,118 @@ func TestExecutionPayloadEnvelopeFromConsensus_NilRequests(t *testing.T) { require.Equal(t, (*ExecutionRequests)(nil), result.ExecutionRequests) } +func testWireBlindedProto() *eth.WireBlindedExecutionPayloadEnvelope { + return ð.WireBlindedExecutionPayloadEnvelope{ + PayloadRoot: fillByteSlice(32, 0x55), + ExecutionRequests: &enginev1.ExecutionRequests{}, + BuilderIndex: 7, + BeaconBlockRoot: fillByteSlice(32, 0x33), + ParentBeaconBlockRoot: fillByteSlice(32, 0x44), + } +} + +// HTR(blinded) must equal HTR(full) so the validator signature stays valid against either form. +func TestWireBlindedHTRMatchesFull(t *testing.T) { + full := ð.ExecutionPayloadEnvelope{ + Payload: &enginev1.ExecutionPayloadGloas{ + ParentHash: fillByteSlice(32, 0x01), + FeeRecipient: fillByteSlice(20, 0x02), + StateRoot: fillByteSlice(32, 0x03), + ReceiptsRoot: fillByteSlice(32, 0x04), + LogsBloom: fillByteSlice(256, 0x05), + PrevRandao: fillByteSlice(32, 0x06), + BaseFeePerGas: fillByteSlice(32, 0x07), + BlockHash: fillByteSlice(32, 0x08), + Transactions: [][]byte{[]byte("tx1"), []byte("tx2")}, + Withdrawals: []*enginev1.Withdrawal{}, + SlotNumber: primitives.Slot(100), + }, + ExecutionRequests: &enginev1.ExecutionRequests{}, + BuilderIndex: primitives.BuilderIndex(42), + BeaconBlockRoot: fillByteSlice(32, 0x09), + ParentBeaconBlockRoot: fillByteSlice(32, 0x0a), + } + + blinded, err := WireBlindedFromFull(full) + require.NoError(t, err) + fullHTR, err := full.HashTreeRoot() + require.NoError(t, err) + blindedHTR, err := blinded.HashTreeRoot() + require.NoError(t, err) + require.Equal(t, fullHTR, blindedHTR) + + // SSZ roundtrip. + enc, err := blinded.MarshalSSZ() + require.NoError(t, err) + decoded := ð.WireBlindedExecutionPayloadEnvelope{} + require.NoError(t, decoded.UnmarshalSSZ(enc)) + rtHTR, err := decoded.HashTreeRoot() + require.NoError(t, err) + require.Equal(t, fullHTR, rtHTR) + + // Signed wrapper SSZ roundtrip. + signedBlinded, err := SignedWireBlindedFromFull(ð.SignedExecutionPayloadEnvelope{ + Message: full, + Signature: fillByteSlice(96, 0x0b), + }) + require.NoError(t, err) + signedEnc, err := signedBlinded.MarshalSSZ() + require.NoError(t, err) + decodedSigned := ð.SignedWireBlindedExecutionPayloadEnvelope{} + require.NoError(t, decodedSigned.UnmarshalSSZ(signedEnc)) + rtBlindedHTR, err := decodedSigned.Message.HashTreeRoot() + require.NoError(t, err) + require.Equal(t, fullHTR, rtBlindedHTR) +} + +func TestBlindedExecutionPayloadEnvelopeFromConsensus(t *testing.T) { + b := testWireBlindedProto() + result, err := BlindedExecutionPayloadEnvelopeFromConsensus(b) + require.NoError(t, err) + require.Equal(t, hexutil.Encode(b.PayloadRoot), result.PayloadRoot) + require.Equal(t, "7", result.BuilderIndex) + require.Equal(t, hexutil.Encode(b.BeaconBlockRoot), result.BeaconBlockRoot) + require.Equal(t, hexutil.Encode(b.ParentBeaconBlockRoot), result.ParentBeaconBlockRoot) + require.NotNil(t, result.ExecutionRequests) +} + +func TestBlindedExecutionPayloadEnvelopeFromConsensus_Nil(t *testing.T) { + _, err := BlindedExecutionPayloadEnvelopeFromConsensus(nil) + require.NotNil(t, err) +} + +func TestBlindedExecutionPayloadEnvelope_ToConsensusRoundTrip(t *testing.T) { + b := testWireBlindedProto() + api, err := BlindedExecutionPayloadEnvelopeFromConsensus(b) + require.NoError(t, err) + back, err := api.ToConsensus() + require.NoError(t, err) + require.DeepEqual(t, b.PayloadRoot, back.PayloadRoot) + require.Equal(t, b.BuilderIndex, back.BuilderIndex) + require.DeepEqual(t, b.BeaconBlockRoot, back.BeaconBlockRoot) + require.DeepEqual(t, b.ParentBeaconBlockRoot, back.ParentBeaconBlockRoot) + require.NotNil(t, back.ExecutionRequests) +} + +func TestSignedBlindedExecutionPayloadEnvelope_ToConsensus(t *testing.T) { + msg, err := BlindedExecutionPayloadEnvelopeFromConsensus(testWireBlindedProto()) + require.NoError(t, err) + sig := fillByteSlice(96, 0x66) + signed := &SignedBlindedExecutionPayloadEnvelope{Message: msg, Signature: hexutil.Encode(sig)} + result, err := signed.ToConsensus() + require.NoError(t, err) + require.NotNil(t, result.Message) + require.DeepEqual(t, sig, result.Signature) +} + +func TestSignedBlindedExecutionPayloadEnvelope_ToConsensus_BadSignature(t *testing.T) { + msg, err := BlindedExecutionPayloadEnvelopeFromConsensus(testWireBlindedProto()) + require.NoError(t, err) + signed := &SignedBlindedExecutionPayloadEnvelope{Message: msg, Signature: "0xdead"} + _, err = signed.ToConsensus() + require.NotNil(t, err) +} + func TestBlockContentsGloasFromConsensus(t *testing.T) { block := util.NewBeaconBlockGloas().Block env := testEnvelopeProto() diff --git a/api/server/structs/endpoints_validator.go b/api/server/structs/endpoints_validator.go index 52a33aea5ec2..8db73167b0a6 100644 --- a/api/server/structs/endpoints_validator.go +++ b/api/server/structs/endpoints_validator.go @@ -164,9 +164,9 @@ type ValidatorParticipation struct { PreviousEpochHeadAttestingGwei string `json:"previous_epoch_head_attesting_gwei"` } -type GetValidatorExecutionPayloadEnvelopeResponse struct { - Version string `json:"version"` - Data *ExecutionPayloadEnvelope `json:"data"` +type GetValidatorBlindedExecutionPayloadEnvelopeResponse struct { + Version string `json:"version"` + Data *BlindedExecutionPayloadEnvelope `json:"data"` } type ActiveSetChanges struct { diff --git a/beacon-chain/blockchain/testing/mock.go b/beacon-chain/blockchain/testing/mock.go index 456586462675..1a0ecae4b902 100644 --- a/beacon-chain/blockchain/testing/mock.go +++ b/beacon-chain/blockchain/testing/mock.go @@ -400,6 +400,9 @@ func (s *ChainService) HeadBlock(context.Context) (interfaces.ReadOnlySignedBeac // HeadState mocks HeadState method in chain service. func (s *ChainService) HeadState(context.Context) (state.BeaconState, error) { + if s.HeadStateErr != nil { + return nil, s.HeadStateErr + } return s.State, nil } diff --git a/beacon-chain/rpc/endpoints.go b/beacon-chain/rpc/endpoints.go index 97e72ab656f1..bd56e2bbc5c9 100644 --- a/beacon-chain/rpc/endpoints.go +++ b/beacon-chain/rpc/endpoints.go @@ -445,10 +445,10 @@ func (s *Service) validatorEndpoints( methods: []string{http.MethodGet}, }, { - template: "/eth/v1/validator/execution_payload_envelope/{slot}", + template: "/eth/v1/validator/execution_payload_envelopes/{slot}/{beacon_block_root}", 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}, @@ -564,34 +564,35 @@ func (s *Service) beaconEndpoints( coreService *core.Service, ) []endpoint { server := &beacon.Server{ - CanonicalHistory: ch, - BeaconDB: s.cfg.BeaconDB, - AttestationCache: s.cfg.AttestationCache, - AttestationsPool: s.cfg.AttestationsPool, - SlashingsPool: s.cfg.SlashingsPool, - ChainInfoFetcher: s.cfg.ChainInfoFetcher, - GenesisTimeFetcher: s.cfg.GenesisTimeFetcher, - BlockNotifier: s.cfg.BlockNotifier, - OperationNotifier: s.cfg.OperationNotifier, - Broadcaster: s.cfg.Broadcaster, - BlockReceiver: s.cfg.BlockReceiver, - StateGenService: s.cfg.StateGen, - Stater: stater, - Blocker: blocker, - OptimisticModeFetcher: s.cfg.OptimisticModeFetcher, - HeadFetcher: s.cfg.HeadFetcher, - TimeFetcher: s.cfg.GenesisTimeFetcher, - VoluntaryExitsPool: s.cfg.ExitPool, - V1Alpha1ValidatorServer: validatorServer, - DataColumnReceiver: s.cfg.DataColumnReceiver, - SyncChecker: s.cfg.SyncService, - ExecutionReconstructor: s.cfg.ExecutionReconstructor, - BLSChangesPool: s.cfg.BLSChangesPool, - PayloadAttestationPool: s.cfg.PayloadAttestationPool, - FinalizationFetcher: s.cfg.FinalizationFetcher, - ForkchoiceFetcher: s.cfg.ForkchoiceFetcher, - CoreService: coreService, - AttestationStateFetcher: s.cfg.AttestationReceiver, + CanonicalHistory: ch, + BeaconDB: s.cfg.BeaconDB, + AttestationCache: s.cfg.AttestationCache, + AttestationsPool: s.cfg.AttestationsPool, + SlashingsPool: s.cfg.SlashingsPool, + ChainInfoFetcher: s.cfg.ChainInfoFetcher, + GenesisTimeFetcher: s.cfg.GenesisTimeFetcher, + BlockNotifier: s.cfg.BlockNotifier, + OperationNotifier: s.cfg.OperationNotifier, + Broadcaster: s.cfg.Broadcaster, + BlockReceiver: s.cfg.BlockReceiver, + StateGenService: s.cfg.StateGen, + Stater: stater, + Blocker: blocker, + OptimisticModeFetcher: s.cfg.OptimisticModeFetcher, + HeadFetcher: s.cfg.HeadFetcher, + TimeFetcher: s.cfg.GenesisTimeFetcher, + VoluntaryExitsPool: s.cfg.ExitPool, + V1Alpha1ValidatorServer: validatorServer, + DataColumnReceiver: s.cfg.DataColumnReceiver, + SyncChecker: s.cfg.SyncService, + ExecutionReconstructor: s.cfg.ExecutionReconstructor, + BLSChangesPool: s.cfg.BLSChangesPool, + PayloadAttestationPool: s.cfg.PayloadAttestationPool, + FinalizationFetcher: s.cfg.FinalizationFetcher, + ForkchoiceFetcher: s.cfg.ForkchoiceFetcher, + CoreService: coreService, + AttestationStateFetcher: s.cfg.AttestationReceiver, + ExecutionPayloadEnvelopeCache: s.cfg.ExecutionPayloadEnvelopeCache, } const namespace = "beacon" @@ -967,7 +968,7 @@ func (s *Service) beaconEndpoints( methods: []string{http.MethodPost}, }, { - template: "/eth/v1/beacon/execution_payload_envelope/{block_id}", + template: "/eth/v1/beacon/execution_payload_envelopes/{block_id}", name: namespace + ".GetExecutionPayloadEnvelope", middleware: []middleware.Middleware{ middleware.AcceptHeaderHandler([]string{api.JsonMediaType, api.OctetStreamMediaType}), @@ -976,17 +977,17 @@ func (s *Service) beaconEndpoints( methods: []string{http.MethodGet}, }, { - template: "/eth/v1/beacon/execution_payload_envelope", + template: "/eth/v1/beacon/execution_payload_envelopes", 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, methods: []string{http.MethodPost}, }, { - template: "/eth/v1/beacon/execution_payload_bid", + template: "/eth/v1/beacon/execution_payload_bids", name: namespace + ".PublishSignedExecutionPayloadBid", middleware: []middleware.Middleware{ middleware.ContentTypeHandler([]string{api.JsonMediaType, api.OctetStreamMediaType}), diff --git a/beacon-chain/rpc/endpoints_test.go b/beacon-chain/rpc/endpoints_test.go index e0ec1fcf344d..e48389fc9250 100644 --- a/beacon-chain/rpc/endpoints_test.go +++ b/beacon-chain/rpc/endpoints_test.go @@ -33,9 +33,9 @@ func Test_endpoints(t *testing.T) { "/eth/v1/beacon/states/{state_id}/pending_partial_withdrawals": {http.MethodGet}, "/eth/v1/beacon/states/{state_id}/pending_consolidations": {http.MethodGet}, "/eth/v1/beacon/states/{state_id}/proposer_lookahead": {http.MethodGet}, - "/eth/v1/beacon/execution_payload_envelope/{block_id}": {http.MethodGet}, - "/eth/v1/beacon/execution_payload_envelope": {http.MethodPost}, - "/eth/v1/beacon/execution_payload_bid": {http.MethodPost}, + "/eth/v1/beacon/execution_payload_envelopes/{block_id}": {http.MethodGet}, + "/eth/v1/beacon/execution_payload_envelopes": {http.MethodPost}, + "/eth/v1/beacon/execution_payload_bids": {http.MethodPost}, "/eth/v1/beacon/headers": {http.MethodGet}, "/eth/v1/beacon/headers/{block_id}": {http.MethodGet}, "/eth/v2/beacon/blinded_blocks": {http.MethodPost}, @@ -97,28 +97,28 @@ func Test_endpoints(t *testing.T) { } validatorRoutes := map[string][]string{ - "/eth/v1/validator/duties/attester/{epoch}": {http.MethodPost}, - "/eth/v1/validator/duties/proposer/{epoch}": {http.MethodGet}, - "/eth/v2/validator/duties/proposer/{epoch}": {http.MethodGet}, - "/eth/v1/validator/duties/sync/{epoch}": {http.MethodPost}, - "/eth/v1/validator/duties/ptc/{epoch}": {http.MethodPost}, - "/eth/v3/validator/blocks/{slot}": {http.MethodGet}, - "/eth/v4/validator/blocks/{slot}": {http.MethodGet}, - "/eth/v1/validator/attestation_data": {http.MethodGet}, - "/eth/v2/validator/aggregate_attestation": {http.MethodGet}, - "/eth/v2/validator/aggregate_and_proofs": {http.MethodPost}, - "/eth/v1/validator/beacon_committee_subscriptions": {http.MethodPost}, - "/eth/v1/validator/sync_committee_subscriptions": {http.MethodPost}, - "/eth/v1/validator/beacon_committee_selections": {http.MethodPost}, - "/eth/v1/validator/sync_committee_selections": {http.MethodPost}, - "/eth/v1/validator/execution_payload_envelope/{slot}": {http.MethodGet}, - "/eth/v1/validator/sync_committee_contribution": {http.MethodGet}, - "/eth/v1/validator/contribution_and_proofs": {http.MethodPost}, - "/eth/v1/validator/prepare_beacon_proposer": {http.MethodPost}, - "/eth/v1/validator/proposer_preferences": {http.MethodPost}, - "/eth/v1/validator/register_validator": {http.MethodPost}, - "/eth/v1/validator/liveness/{epoch}": {http.MethodPost}, - "/eth/v1/validator/payload_attestation_data/{slot}": {http.MethodGet}, + "/eth/v1/validator/duties/attester/{epoch}": {http.MethodPost}, + "/eth/v1/validator/duties/proposer/{epoch}": {http.MethodGet}, + "/eth/v2/validator/duties/proposer/{epoch}": {http.MethodGet}, + "/eth/v1/validator/duties/sync/{epoch}": {http.MethodPost}, + "/eth/v1/validator/duties/ptc/{epoch}": {http.MethodPost}, + "/eth/v3/validator/blocks/{slot}": {http.MethodGet}, + "/eth/v4/validator/blocks/{slot}": {http.MethodGet}, + "/eth/v1/validator/attestation_data": {http.MethodGet}, + "/eth/v2/validator/aggregate_attestation": {http.MethodGet}, + "/eth/v2/validator/aggregate_and_proofs": {http.MethodPost}, + "/eth/v1/validator/beacon_committee_subscriptions": {http.MethodPost}, + "/eth/v1/validator/sync_committee_subscriptions": {http.MethodPost}, + "/eth/v1/validator/beacon_committee_selections": {http.MethodPost}, + "/eth/v1/validator/sync_committee_selections": {http.MethodPost}, + "/eth/v1/validator/execution_payload_envelopes/{slot}/{beacon_block_root}": {http.MethodGet}, + "/eth/v1/validator/sync_committee_contribution": {http.MethodGet}, + "/eth/v1/validator/contribution_and_proofs": {http.MethodPost}, + "/eth/v1/validator/prepare_beacon_proposer": {http.MethodPost}, + "/eth/v1/validator/proposer_preferences": {http.MethodPost}, + "/eth/v1/validator/register_validator": {http.MethodPost}, + "/eth/v1/validator/liveness/{epoch}": {http.MethodPost}, + "/eth/v1/validator/payload_attestation_data/{slot}": {http.MethodGet}, } prysmBeaconRoutes := map[string][]string{ diff --git a/beacon-chain/rpc/eth/beacon/BUILD.bazel b/beacon-chain/rpc/eth/beacon/BUILD.bazel index 112cf0d76aec..5d6d45476332 100644 --- a/beacon-chain/rpc/eth/beacon/BUILD.bazel +++ b/beacon-chain/rpc/eth/beacon/BUILD.bazel @@ -92,6 +92,7 @@ go_test( "//api/server/structs:go_default_library", "//beacon-chain/blockchain/kzg:go_default_library", "//beacon-chain/blockchain/testing:go_default_library", + "//beacon-chain/cache:go_default_library", "//beacon-chain/core/signing:go_default_library", "//beacon-chain/core/time:go_default_library", "//beacon-chain/core/transition:go_default_library", diff --git a/beacon-chain/rpc/eth/beacon/handlers.go b/beacon-chain/rpc/eth/beacon/handlers.go index e043214984d4..8386ecc60a34 100644 --- a/beacon-chain/rpc/eth/beacon/handlers.go +++ b/beacon-chain/rpc/eth/beacon/handlers.go @@ -38,6 +38,7 @@ import ( const ( broadcastValidationQueryParam = "broadcast_validation" + broadcastValidationGossip = "gossip" broadcastValidationConsensus = "consensus" broadcastValidationConsensusAndEquivocation = "consensus_and_equivocation" ) diff --git a/beacon-chain/rpc/eth/beacon/handlers_gloas.go b/beacon-chain/rpc/eth/beacon/handlers_gloas.go index 3c6532bb6ff5..bf02b7f0cb67 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_gloas.go +++ b/beacon-chain/rpc/eth/beacon/handlers_gloas.go @@ -1,14 +1,18 @@ package beacon import ( + "bytes" "context" "encoding/json" + "fmt" "io" "net/http" + "strconv" "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" @@ -90,13 +94,32 @@ func (s *Server) GetExecutionPayloadEnvelope(w http.ResponseWriter, r *http.Requ }) } -// PublishExecutionPayloadEnvelope broadcasts a signed envelope. Body may be -// either SignedExecutionPayloadEnvelope (stateful) or -// SignedExecutionPayloadEnvelopeContents (stateless, with blobs+proofs). -// Endpoint: POST /eth/v1/beacon/execution_payload_envelope +// PublishExecutionPayloadEnvelope broadcasts a signed envelope. Eth-Execution-Payload-Blinded +// selects the body: true=blinded (stateful, BN reconstructs from cache), false=contents (stateless). +// Endpoint: POST /eth/v1/beacon/execution_payload_envelopes func (s *Server) PublishExecutionPayloadEnvelope(w http.ResponseWriter, r *http.Request) { ctx, span := trace.StartSpan(r.Context(), "beacon.PublishExecutionPayloadEnvelope") defer span.End() + versionHeader := r.Header.Get(api.VersionHeader) + if versionHeader == "" { + httputil.HandleError(w, api.VersionHeader+" header is required", http.StatusBadRequest) + return + } + v, err := version.FromString(versionHeader) + if err != nil || v < version.Gloas { + httputil.HandleError(w, api.VersionHeader+" header must be gloas or later", http.StatusBadRequest) + return + } + blindedHeader := r.Header.Get(api.ExecutionPayloadBlindedHeader) + if blindedHeader == "" { + httputil.HandleError(w, api.ExecutionPayloadBlindedHeader+" header is required", http.StatusBadRequest) + return + } + isBlinded, err := strconv.ParseBool(blindedHeader) + if err != nil { + httputil.HandleError(w, "invalid "+api.ExecutionPayloadBlindedHeader+" value: "+err.Error(), http.StatusBadRequest) + return + } body, err := io.ReadAll(r.Body) if err != nil { @@ -104,51 +127,113 @@ 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) + if isBlinded { + s.publishBlindedEnvelope(ctx, w, r, body) return } - if _, isContents := probe["signed_execution_payload_envelope"]; isContents { - s.publishExecutionPayloadEnvelopeContents(ctx, w, body) + s.publishEnvelopeContents(ctx, w, r, body) +} + +// publishBlindedEnvelope reconstructs the full envelope from cache by beacon_block_root. +// HTR(blinded) == HTR(full), so the validator signature stays valid. +func (s *Server) publishBlindedEnvelope(ctx context.Context, w http.ResponseWriter, r *http.Request, body []byte) { + signedBlinded := ð.SignedWireBlindedExecutionPayloadEnvelope{} + if httputil.IsRequestSsz(r) { + if err := signedBlinded.UnmarshalSSZ(body); err != nil { + httputil.HandleError(w, "could not decode SSZ blinded envelope: "+err.Error(), http.StatusBadRequest) + return + } + } else { + var jsonBlinded structs.SignedBlindedExecutionPayloadEnvelope + if err := json.Unmarshal(body, &jsonBlinded); err != nil { + httputil.HandleError(w, "could not decode JSON blinded envelope: "+err.Error(), http.StatusBadRequest) + return + } + consensus, err := jsonBlinded.ToConsensus() + if err != nil { + httputil.HandleError(w, "invalid signed blinded envelope: "+err.Error(), http.StatusBadRequest) + return + } + signedBlinded = consensus + } + if signedBlinded.Message == nil { + httputil.HandleError(w, "blinded envelope message is nil", 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) + cached, ok := s.ExecutionPayloadEnvelopeCache.Contents() + if !ok || cached.Envelope == nil { + httputil.HandleError(w, "no cached execution payload envelope to reconstruct from", http.StatusBadRequest) return } - - consensus, err := jsonEnvelope.ToConsensus() + if !bytes.Equal(cached.Envelope.BeaconBlockRoot, signedBlinded.Message.BeaconBlockRoot) { + httputil.HandleError(w, "cached envelope beacon_block_root does not match blinded envelope", http.StatusBadRequest) + return + } + blindedRoot, err := signedBlinded.Message.HashTreeRoot() + if err != nil { + httputil.HandleError(w, "could not hash blinded envelope: "+err.Error(), http.StatusInternalServerError) + return + } + cachedRoot, err := cached.Envelope.HashTreeRoot() if err != nil { - httputil.HandleError(w, "invalid signed execution payload envelope: "+err.Error(), http.StatusBadRequest) + httputil.HandleError(w, "could not hash cached envelope: "+err.Error(), http.StatusInternalServerError) + return + } + if blindedRoot != cachedRoot { + httputil.HandleError(w, "cached envelope hash tree root does not match blinded envelope", http.StatusBadRequest) return } - if _, err := s.V1Alpha1ValidatorServer.PublishExecutionPayloadEnvelope(ctx, consensus); err != nil { - if st, ok := status.FromError(err); ok { - switch st.Code() { - case codes.InvalidArgument: - httputil.HandleError(w, st.Message(), http.StatusBadRequest) - default: - httputil.HandleError(w, st.Message(), http.StatusInternalServerError) - } - return - } - httputil.HandleError(w, "could not publish execution payload envelope: "+err.Error(), http.StatusInternalServerError) + full := ð.SignedExecutionPayloadEnvelope{ + Message: cached.Envelope, + Signature: signedBlinded.Signature, + } + + if !s.validateEnvelopeBroadcast(ctx, w, r, full) { return } + if _, err := s.V1Alpha1ValidatorServer.PublishExecutionPayloadEnvelope(ctx, full); err != nil { + writeEnvelopePublishError(w, err) + return + } 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) { +// writeEnvelopePublishError maps the v1alpha1 publish outcome to the spec status +// codes: InvalidArgument -> 400, Aborted -> 202 (broadcast ok, import failed). +func writeEnvelopePublishError(w http.ResponseWriter, err error) { + if st, ok := status.FromError(err); ok { + switch st.Code() { + case codes.InvalidArgument: + httputil.HandleError(w, st.Message(), http.StatusBadRequest) + case codes.Aborted: + httputil.HandleError(w, st.Message(), http.StatusAccepted) + default: + httputil.HandleError(w, st.Message(), http.StatusInternalServerError) + } + return + } + httputil.HandleError(w, "could not publish execution payload envelope: "+err.Error(), http.StatusInternalServerError) +} + +// publishEnvelopeContents handles the stateless flow (header=false). +func (s *Server) publishEnvelopeContents(ctx context.Context, w http.ResponseWriter, r *http.Request, body []byte) { + if httputil.IsRequestSsz(r) { + contents := ð.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 + } + s.publishExecutionPayloadEnvelopeContents(ctx, w, r, body) +} + +// 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) @@ -159,6 +244,24 @@ 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 !s.validateEnvelopeBroadcast(ctx, w, r, signed) { + return + } if len(blobs) > 0 { blockRoot := bytesutil.ToBytes32(signed.Message.BeaconBlockRoot) @@ -192,21 +295,75 @@ func (s *Server) publishExecutionPayloadEnvelopeContents(ctx context.Context, w } if _, err := s.V1Alpha1ValidatorServer.PublishExecutionPayloadEnvelope(ctx, signed); err != nil { - if st, ok := status.FromError(err); ok { - switch st.Code() { - case codes.InvalidArgument: - httputil.HandleError(w, st.Message(), http.StatusBadRequest) - default: - httputil.HandleError(w, st.Message(), http.StatusInternalServerError) - } - return - } - httputil.HandleError(w, "could not publish execution payload envelope contents: "+err.Error(), http.StatusInternalServerError) + writeEnvelopePublishError(w, err) return } w.WriteHeader(http.StatusOK) } +// validateEnvelopeBroadcast applies broadcast_validation semantics to an +// envelope publish before it is broadcast to gossip. Spec: beacon-APIs #580. +// Writes the HTTP error and returns false on failure: 400 for validation +// failures, 500 for internal errors. +// - gossip (default): no extra REST-layer checks — the downstream gossip +// pipeline performs validation. +// - consensus: full envelope consensus checks against the head state. Submission +// path requires envRoot to equal head. +// - 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, w http.ResponseWriter, r *http.Request, signed *eth.SignedExecutionPayloadEnvelope) bool { + level := r.URL.Query().Get(broadcastValidationQueryParam) + switch level { + case "", broadcastValidationGossip: + // TODO: run lightweight gossip checks (sig + bid consistency) here — beacon-APIs #580. + return true + case broadcastValidationConsensus, broadcastValidationConsensusAndEquivocation: + default: + httputil.HandleError(w, fmt.Sprintf("invalid %s value: %q", broadcastValidationQueryParam, level), http.StatusBadRequest) + return false + } + + envSlot := signed.Message.Payload.SlotNumber + envRoot := bytesutil.ToBytes32(signed.Message.BeaconBlockRoot) + + if level == broadcastValidationConsensusAndEquivocation { + // CanonicalNodeAtSlot's bool means "payload full", not "node found" — at the + // wall clock slot it is always false. A non-zero root is the found signal. + canonRoot, _ := s.ForkchoiceFetcher.CanonicalNodeAtSlot(envSlot) + if canonRoot != ([32]byte{}) && canonRoot != envRoot { + err := errors.Wrapf(errEquivocatedBlock, "another block for slot %d already exists in fork choice", envSlot) + httputil.HandleError(w, err.Error(), http.StatusBadRequest) + return false + } + } + + // Submission path: envelope must be for the current head. + headRoot, err := s.HeadFetcher.HeadRoot(ctx) + if err != nil { + httputil.HandleError(w, "could not get head root: "+err.Error(), http.StatusInternalServerError) + return false + } + if !bytes.Equal(headRoot, envRoot[:]) { + httputil.HandleError(w, fmt.Sprintf("envelope beacon block root %#x is not canonical head", envRoot), http.StatusBadRequest) + return false + } + st, err := s.HeadFetcher.HeadState(ctx) + if err != nil { + httputil.HandleError(w, "could not get head state: "+err.Error(), http.StatusInternalServerError) + return false + } + roSigned, err := consensusblocks.WrappedROSignedExecutionPayloadEnvelope(signed) + if err != nil { + httputil.HandleError(w, "could not wrap signed envelope: "+err.Error(), http.StatusInternalServerError) + return false + } + if err := gloas.VerifyExecutionPayloadEnvelope(ctx, st, roSigned); err != nil { + httputil.HandleError(w, "consensus validation failed: "+err.Error(), http.StatusBadRequest) + return false + } + return true +} + // 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. diff --git a/beacon-chain/rpc/eth/beacon/handlers_gloas_test.go b/beacon-chain/rpc/eth/beacon/handlers_gloas_test.go index 5960b51c03f1..fd79d21ed811 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_gloas_test.go +++ b/beacon-chain/rpc/eth/beacon/handlers_gloas_test.go @@ -3,18 +3,22 @@ package beacon import ( "bytes" "encoding/json" + "errors" "net/http" "net/http/httptest" "testing" + "github.com/OffchainLabs/prysm/v7/api" "github.com/OffchainLabs/prysm/v7/api/server/structs" "github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain/kzg" chainMock "github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain/testing" + "github.com/OffchainLabs/prysm/v7/beacon-chain/cache" dbTest "github.com/OffchainLabs/prysm/v7/beacon-chain/db/testing" executiontesting "github.com/OffchainLabs/prysm/v7/beacon-chain/execution/testing" mockp2p "github.com/OffchainLabs/prysm/v7/beacon-chain/p2p/testing" "github.com/OffchainLabs/prysm/v7/beacon-chain/rpc/lookup" "github.com/OffchainLabs/prysm/v7/beacon-chain/rpc/testutil" + "github.com/OffchainLabs/prysm/v7/beacon-chain/state" fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams" "github.com/OffchainLabs/prysm/v7/config/params" "github.com/OffchainLabs/prysm/v7/consensus-types/primitives" @@ -26,12 +30,33 @@ import ( mock2 "github.com/OffchainLabs/prysm/v7/testing/mock" "github.com/OffchainLabs/prysm/v7/testing/require" "github.com/OffchainLabs/prysm/v7/testing/util" + "github.com/ethereum/go-ethereum/common/hexutil" "go.uber.org/mock/gomock" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/emptypb" ) +func envelopeCacheFor(signed *ethpb.SignedExecutionPayloadEnvelope) *cache.ExecutionPayloadEnvelopeCache { + c := cache.NewExecutionPayloadEnvelopeCache() + c.Set(&cache.ExecutionPayloadContents{Envelope: signed.Message}) + return c +} + +func blindedJSONBody(t *testing.T, signed *ethpb.SignedExecutionPayloadEnvelope) []byte { + t.Helper() + blinded, err := structs.SignedWireBlindedFromFull(signed) + require.NoError(t, err) + msg, err := structs.BlindedExecutionPayloadEnvelopeFromConsensus(blinded.Message) + require.NoError(t, err) + body, err := json.Marshal(&structs.SignedBlindedExecutionPayloadEnvelope{ + Message: msg, + Signature: hexutil.Encode(blinded.Signature), + }) + require.NoError(t, err) + return body +} + func TestGetExecutionPayloadEnvelope_AcceptsSlotID(t *testing.T) { ctx := t.Context() beaconDB := dbTest.SetupDB(t) @@ -143,7 +168,8 @@ func testSignedEnvelope() *ethpb.SignedExecutionPayloadEnvelope { } } -func TestPublishExecutionPayloadEnvelope_OK(t *testing.T) { +// Stateful: body is the spec-wire blinded envelope; BN reconstructs full from cache. +func TestPublishExecutionPayloadEnvelope_StatefulBlinded_OK(t *testing.T) { params.SetupTestConfigCleanup(t) cfg := params.BeaconConfig().Copy() cfg.GloasForkEpoch = 0 @@ -157,13 +183,15 @@ func TestPublishExecutionPayloadEnvelope_OK(t *testing.T) { gomock.Any(), gomock.Any(), ).Return(&emptypb.Empty{}, nil) - jsonEnvelope, err := structs.SignedExecutionPayloadEnvelopeFromConsensus(signed) - require.NoError(t, err) - body, err := json.Marshal(jsonEnvelope) - require.NoError(t, err) + body := blindedJSONBody(t, signed) - s := &Server{V1Alpha1ValidatorServer: v1alpha1Server} + s := &Server{ + V1Alpha1ValidatorServer: v1alpha1Server, + ExecutionPayloadEnvelopeCache: envelopeCacheFor(signed), + } req := httptest.NewRequest(http.MethodPost, "/eth/v1/beacon/execution_payload_envelope", bytes.NewReader(body)) + req.Header.Set(api.VersionHeader, version.String(version.Gloas)) + req.Header.Set(api.ExecutionPayloadBlindedHeader, "true") w := httptest.NewRecorder() w.Body = &bytes.Buffer{} @@ -171,9 +199,24 @@ func TestPublishExecutionPayloadEnvelope_OK(t *testing.T) { require.Equal(t, http.StatusOK, w.Code) } +// Missing Eth-Execution-Payload-Blinded header must be a 400. +func TestPublishExecutionPayloadEnvelope_MissingBlindedHeader(t *testing.T) { + s := &Server{} + req := httptest.NewRequest(http.MethodPost, "/eth/v1/beacon/execution_payload_envelope", bytes.NewReader([]byte("{}"))) + req.Header.Set(api.VersionHeader, version.String(version.Gloas)) + w := httptest.NewRecorder() + w.Body = &bytes.Buffer{} + + s.PublishExecutionPayloadEnvelope(w, req) + require.Equal(t, http.StatusBadRequest, w.Code) + assert.Equal(t, true, bytes.Contains(w.Body.Bytes(), []byte(api.ExecutionPayloadBlindedHeader))) +} + func TestPublishExecutionPayloadEnvelope_InvalidBody(t *testing.T) { s := &Server{} req := httptest.NewRequest(http.MethodPost, "/eth/v1/beacon/execution_payload_envelope", bytes.NewReader([]byte("not json"))) + req.Header.Set(api.VersionHeader, version.String(version.Gloas)) + req.Header.Set(api.ExecutionPayloadBlindedHeader, "false") w := httptest.NewRecorder() w.Body = &bytes.Buffer{} @@ -203,6 +246,8 @@ func TestPublishExecutionPayloadEnvelope_StatelessContents_NoBlobs(t *testing.T) // skipped, so the handler does not need a Broadcaster or DataColumnReceiver. s := &Server{V1Alpha1ValidatorServer: v1alpha1Server} req := httptest.NewRequest(http.MethodPost, "/eth/v1/beacon/execution_payload_envelope", bytes.NewReader(body)) + req.Header.Set(api.VersionHeader, version.String(version.Gloas)) + req.Header.Set(api.ExecutionPayloadBlindedHeader, "false") w := httptest.NewRecorder() w.Body = &bytes.Buffer{} @@ -210,6 +255,31 @@ func TestPublishExecutionPayloadEnvelope_StatelessContents_NoBlobs(t *testing.T) require.Equal(t, http.StatusOK, w.Code) } +// DB integration failed -> Aborted maps to 202. +func TestPublishExecutionPayloadEnvelope_ImportFailureReturns202(t *testing.T) { + ctrl := gomock.NewController(t) + signed := testSignedEnvelope() + contents, err := structs.SignedExecutionPayloadEnvelopeContentsFromConsensus(signed, nil, nil) + require.NoError(t, err) + body, err := json.Marshal(contents) + require.NoError(t, err) + + v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl) + v1alpha1Server.EXPECT().PublishExecutionPayloadEnvelope( + gomock.Any(), gomock.Any(), + ).Return(nil, status.Error(codes.Aborted, "import failed")) + + s := &Server{V1Alpha1ValidatorServer: v1alpha1Server} + req := httptest.NewRequest(http.MethodPost, "/eth/v1/beacon/execution_payload_envelope", bytes.NewReader(body)) + req.Header.Set(api.VersionHeader, version.String(version.Gloas)) + req.Header.Set(api.ExecutionPayloadBlindedHeader, "false") + w := httptest.NewRecorder() + w.Body = &bytes.Buffer{} + + s.PublishExecutionPayloadEnvelope(w, req) + require.Equal(t, http.StatusAccepted, w.Code) +} + // statelessContentsBody builds a SignedExecutionPayloadEnvelopeContents JSON // body with real blobs+proofs, returning the body bytes and the signed // envelope used to construct it. blobMutator runs against the flat proofs @@ -266,6 +336,8 @@ func TestPublishExecutionPayloadEnvelope_StatelessContents_WithBlobs(t *testing. DataColumnReceiver: &chainMock.ChainService{}, } req := httptest.NewRequest(http.MethodPost, "/eth/v1/beacon/execution_payload_envelope", bytes.NewReader(body)) + req.Header.Set(api.VersionHeader, version.String(version.Gloas)) + req.Header.Set(api.ExecutionPayloadBlindedHeader, "false") w := httptest.NewRecorder() w.Body = &bytes.Buffer{} @@ -289,6 +361,8 @@ func TestPublishExecutionPayloadEnvelope_StatelessContents_RejectsBadProofs(t *t DataColumnReceiver: &chainMock.ChainService{}, } req := httptest.NewRequest(http.MethodPost, "/eth/v1/beacon/execution_payload_envelope", bytes.NewReader(body)) + req.Header.Set(api.VersionHeader, version.String(version.Gloas)) + req.Header.Set(api.ExecutionPayloadBlindedHeader, "false") w := httptest.NewRecorder() w.Body = &bytes.Buffer{} @@ -311,16 +385,257 @@ func TestPublishExecutionPayloadEnvelope_ServerError(t *testing.T) { ).Return(nil, status.Error(codes.Internal, "broadcast failed")) signed := testSignedEnvelope() - jsonEnvelope, err := structs.SignedExecutionPayloadEnvelopeFromConsensus(signed) + body := blindedJSONBody(t, signed) + + s := &Server{ + V1Alpha1ValidatorServer: v1alpha1Server, + ExecutionPayloadEnvelopeCache: envelopeCacheFor(signed), + } + req := httptest.NewRequest(http.MethodPost, "/eth/v1/beacon/execution_payload_envelope", bytes.NewReader(body)) + req.Header.Set(api.VersionHeader, version.String(version.Gloas)) + req.Header.Set(api.ExecutionPayloadBlindedHeader, "true") + w := httptest.NewRecorder() + w.Body = &bytes.Buffer{} + + s.PublishExecutionPayloadEnvelope(w, req) + require.Equal(t, http.StatusInternalServerError, w.Code) +} + +// SSZ stateful: send SignedWireBlindedExecutionPayloadEnvelope, header=true. +func TestPublishExecutionPayloadEnvelope_SSZ_StatefulBlinded(t *testing.T) { + params.SetupTestConfigCleanup(t) + cfg := params.BeaconConfig().Copy() + cfg.GloasForkEpoch = 0 + params.OverrideBeaconConfig(cfg) + + ctrl := gomock.NewController(t) + signed := testSignedEnvelope() + blinded, err := structs.SignedWireBlindedFromFull(signed) require.NoError(t, err) - body, err := json.Marshal(jsonEnvelope) + sszBody, err := blinded.MarshalSSZ() require.NoError(t, err) - s := &Server{V1Alpha1ValidatorServer: v1alpha1Server} + v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl) + v1alpha1Server.EXPECT().PublishExecutionPayloadEnvelope( + gomock.Any(), gomock.Any(), + ).Return(&emptypb.Empty{}, nil) + + s := &Server{ + V1Alpha1ValidatorServer: v1alpha1Server, + ExecutionPayloadEnvelopeCache: envelopeCacheFor(signed), + } + req := httptest.NewRequest(http.MethodPost, "/eth/v1/beacon/execution_payload_envelope", bytes.NewReader(sszBody)) + req.Header.Set("Content-Type", "application/octet-stream") + req.Header.Set(api.VersionHeader, version.String(version.Gloas)) + req.Header.Set(api.ExecutionPayloadBlindedHeader, "true") + w := httptest.NewRecorder() + w.Body = &bytes.Buffer{} + + s.PublishExecutionPayloadEnvelope(w, req) + require.Equal(t, http.StatusOK, w.Code) +} + +// SSZ stateful with no cache entry must fail (cannot reconstruct full). +func TestPublishExecutionPayloadEnvelope_SSZ_StatefulBlinded_CacheMiss(t *testing.T) { + params.SetupTestConfigCleanup(t) + cfg := params.BeaconConfig().Copy() + cfg.GloasForkEpoch = 0 + params.OverrideBeaconConfig(cfg) + + signed := testSignedEnvelope() + blinded, err := structs.SignedWireBlindedFromFull(signed) + require.NoError(t, err) + sszBody, err := blinded.MarshalSSZ() + require.NoError(t, err) + + s := &Server{ + ExecutionPayloadEnvelopeCache: cache.NewExecutionPayloadEnvelopeCache(), + } + req := httptest.NewRequest(http.MethodPost, "/eth/v1/beacon/execution_payload_envelope", bytes.NewReader(sszBody)) + req.Header.Set("Content-Type", "application/octet-stream") + req.Header.Set(api.VersionHeader, version.String(version.Gloas)) + req.Header.Set(api.ExecutionPayloadBlindedHeader, "true") + w := httptest.NewRecorder() + w.Body = &bytes.Buffer{} + + s.PublishExecutionPayloadEnvelope(w, req) + require.Equal(t, http.StatusBadRequest, w.Code) + assert.Equal(t, true, bytes.Contains(w.Body.Bytes(), []byte("no cached execution payload envelope"))) +} + +// A cached envelope whose HTR differs from the signed blinded envelope must be rejected, +// even when the beacon_block_root matches. +func TestPublishExecutionPayloadEnvelope_StatefulBlinded_HTRMismatch(t *testing.T) { + params.SetupTestConfigCleanup(t) + cfg := params.BeaconConfig().Copy() + cfg.GloasForkEpoch = 0 + params.OverrideBeaconConfig(cfg) + + signed := testSignedEnvelope() + body := blindedJSONBody(t, signed) + + tampered := testSignedEnvelope() + tampered.Message.BuilderIndex = signed.Message.BuilderIndex + 1 + + s := &Server{ + ExecutionPayloadEnvelopeCache: envelopeCacheFor(tampered), + } req := httptest.NewRequest(http.MethodPost, "/eth/v1/beacon/execution_payload_envelope", bytes.NewReader(body)) + req.Header.Set(api.VersionHeader, version.String(version.Gloas)) + req.Header.Set(api.ExecutionPayloadBlindedHeader, "true") w := httptest.NewRecorder() w.Body = &bytes.Buffer{} s.PublishExecutionPayloadEnvelope(w, req) - require.Equal(t, http.StatusInternalServerError, w.Code) + require.Equal(t, http.StatusBadRequest, w.Code) + assert.Equal(t, true, bytes.Contains(w.Body.Bytes(), []byte("hash tree root does not match"))) +} + +func TestPublishExecutionPayloadEnvelope_SSZ_Contents(t *testing.T) { + params.SetupTestConfigCleanup(t) + cfg := params.BeaconConfig().Copy() + cfg.GloasForkEpoch = 0 + params.OverrideBeaconConfig(cfg) + + ctrl := gomock.NewController(t) + signed := testSignedEnvelope() + contents := ðpb.SignedExecutionPayloadEnvelopeContents{ + SignedExecutionPayloadEnvelope: signed, + } + sszBody, err := contents.MarshalSSZ() + require.NoError(t, err) + + v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl) + v1alpha1Server.EXPECT().PublishExecutionPayloadEnvelope( + gomock.Any(), gomock.Any(), + ).Return(&emptypb.Empty{}, nil) + + s := &Server{V1Alpha1ValidatorServer: v1alpha1Server} + req := httptest.NewRequest(http.MethodPost, "/eth/v1/beacon/execution_payload_envelope", bytes.NewReader(sszBody)) + req.Header.Set("Content-Type", "application/octet-stream") + req.Header.Set(api.VersionHeader, version.String(version.Gloas)) + req.Header.Set(api.ExecutionPayloadBlindedHeader, "false") + w := httptest.NewRecorder() + w.Body = &bytes.Buffer{} + + s.PublishExecutionPayloadEnvelope(w, req) + require.Equal(t, http.StatusOK, w.Code) +} + +func TestPublishExecutionPayloadEnvelope_BroadcastValidation(t *testing.T) { + params.SetupTestConfigCleanup(t) + cfg := params.BeaconConfig().Copy() + cfg.GloasForkEpoch = 0 + params.OverrideBeaconConfig(cfg) + + signed := testSignedEnvelope() + envRoot := bytesutil.ToBytes32(signed.Message.BeaconBlockRoot) + envSlot := primitives.Slot(signed.Message.Payload.SlotNumber) + body := blindedJSONBody(t, signed) + + // State that fails gloas.VerifyExecutionPayloadEnvelope (slot mismatch is + // enough). Lets us exercise the consensus path and assert it actually runs. + failingState, err := util.NewBeaconStateGloas() + require.NoError(t, err) + + otherRoot := bytesutil.ToBytes32(bytesutil.PadTo([]byte("other-root"), 32)) + + cases := []struct { + name string + query string + headRoot [32]byte + headState state.BeaconState + headStateErr error + canonicalAtEnvSlt *[32]byte // nil → CanonicalNodeAtSlot returns a zero root + expectPublish bool + expectedStatus int + expectedBody string + }{ + {name: "default (gossip)", query: "", expectPublish: true, expectedStatus: http.StatusOK}, + {name: "explicit gossip", query: "?broadcast_validation=gossip", expectPublish: true, expectedStatus: http.StatusOK}, + { + name: "consensus envRoot not head", + query: "?broadcast_validation=consensus", + headRoot: otherRoot, + expectedStatus: http.StatusBadRequest, + expectedBody: "is not canonical head", + }, + { + name: "consensus verification fails", + query: "?broadcast_validation=consensus", + headRoot: envRoot, + headState: failingState, + expectedStatus: http.StatusBadRequest, + expectedBody: "consensus validation failed", + }, + { + name: "consensus_and_equivocation equivocation detected", + query: "?broadcast_validation=consensus_and_equivocation", + canonicalAtEnvSlt: &otherRoot, + expectedStatus: http.StatusBadRequest, + expectedBody: "block is equivocated", + }, + { + name: "consensus_and_equivocation no equivocation runs consensus check", + query: "?broadcast_validation=consensus_and_equivocation", + headRoot: envRoot, + headState: failingState, + expectedStatus: http.StatusBadRequest, + expectedBody: "consensus validation failed", + }, + { + name: "consensus head state error is internal", + query: "?broadcast_validation=consensus", + headRoot: envRoot, + headStateErr: errors.New("state unavailable"), + expectedStatus: http.StatusInternalServerError, + expectedBody: "could not get head state", + }, + { + name: "invalid value", + query: "?broadcast_validation=bogus", + expectedStatus: http.StatusBadRequest, + expectedBody: "invalid broadcast_validation value", + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + ctrl := gomock.NewController(t) + v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl) + if tc.expectPublish { + v1alpha1Server.EXPECT().PublishExecutionPayloadEnvelope( + gomock.Any(), gomock.Any(), + ).Return(&emptypb.Empty{}, nil) + } + + chainSvc := &chainMock.ChainService{ + Root: tc.headRoot[:], + State: tc.headState, + HeadStateErr: tc.headStateErr, + } + if tc.canonicalAtEnvSlt != nil { + chainSvc.MockCanonicalRoots = map[primitives.Slot][32]byte{envSlot: *tc.canonicalAtEnvSlt} + // full=false mirrors the wall clock slot case; the root alone must trip the check. + chainSvc.MockCanonicalFull = map[primitives.Slot]bool{envSlot: false} + } + s := &Server{ + V1Alpha1ValidatorServer: v1alpha1Server, + ForkchoiceFetcher: chainSvc, + HeadFetcher: chainSvc, + ExecutionPayloadEnvelopeCache: envelopeCacheFor(signed), + } + req := httptest.NewRequest(http.MethodPost, "/eth/v1/beacon/execution_payload_envelope"+tc.query, bytes.NewReader(body)) + req.Header.Set(api.VersionHeader, version.String(version.Gloas)) + req.Header.Set(api.ExecutionPayloadBlindedHeader, "true") + w := httptest.NewRecorder() + w.Body = &bytes.Buffer{} + + s.PublishExecutionPayloadEnvelope(w, req) + require.Equal(t, tc.expectedStatus, w.Code) + if tc.expectedBody != "" { + assert.Equal(t, true, bytes.Contains(w.Body.Bytes(), []byte(tc.expectedBody))) + } + }) + } } diff --git a/beacon-chain/rpc/eth/beacon/server.go b/beacon-chain/rpc/eth/beacon/server.go index b28e871391f1..9130c2d1cb11 100644 --- a/beacon-chain/rpc/eth/beacon/server.go +++ b/beacon-chain/rpc/eth/beacon/server.go @@ -54,4 +54,6 @@ type Server struct { ForkchoiceFetcher blockchain.ForkchoiceFetcher CoreService *core.Service AttestationStateFetcher blockchain.AttestationStateFetcher + // ExecutionPayloadEnvelopeCache reconstructs the full envelope in the blinded publish flow. + ExecutionPayloadEnvelopeCache *cache.ExecutionPayloadEnvelopeCache } diff --git a/beacon-chain/rpc/eth/validator/handlers_block_gloas.go b/beacon-chain/rpc/eth/validator/handlers_block_gloas.go index 442b723e03b9..38a80f4a9fb8 100644 --- a/beacon-chain/rpc/eth/validator/handlers_block_gloas.go +++ b/beacon-chain/rpc/eth/validator/handlers_block_gloas.go @@ -1,6 +1,7 @@ package validator import ( + "bytes" "encoding/json" "fmt" "net/http" @@ -114,6 +115,12 @@ func (s *Server) ProduceBlockV4(w http.ResponseWriter, r *http.Request) { consensusBlockValue = "0" } + // External builder bids reveal their payload separately, so only self-built + // blocks carry an inline envelope regardless of include_payload (beacon-APIs #580). + if includePayload && !gloasBlockSelfBuilt(gloasBlock.Gloas) { + includePayload = false + } + w.Header().Set(api.VersionHeader, version.String(version.Gloas)) w.Header().Set(api.ConsensusBlockValueHeader, consensusBlockValue) w.Header().Set(api.ExecutionPayloadIncludedHeader, fmt.Sprintf("%v", includePayload)) @@ -201,9 +208,16 @@ func (s *Server) ProduceBlockV4(w http.ResponseWriter, r *http.Request) { }) } -// ExecutionPayloadEnvelope retrieves a cached execution payload envelope. -// -// Endpoint: GET /eth/v1/validator/execution_payload_envelope/{slot} +// gloasBlockSelfBuilt reports whether the block's bid is the proposer's own +// self-built payload rather than an external builder's. +func gloasBlockSelfBuilt(b *eth.BeaconBlockGloas) bool { + bid := b.GetBody().GetSignedExecutionPayloadBid().GetMessage() + return bid != nil && bid.BuilderIndex == params.BeaconConfig().BuilderIndexSelfBuild +} + +// ExecutionPayloadEnvelope returns the cached envelope in blinded form (payload_root); +// HTR equivalence lets the VC sign the blinded form for the full envelope. +// Endpoint: GET /eth/v1/validator/execution_payload_envelopes/{slot}/{beacon_block_root} func (s *Server) ExecutionPayloadEnvelope(w http.ResponseWriter, r *http.Request) { ctx, span := trace.StartSpan(r.Context(), "validator.ExecutionPayloadEnvelope") defer span.End() @@ -218,6 +232,16 @@ func (s *Server) ExecutionPayloadEnvelope(w http.ResponseWriter, r *http.Request httputil.HandleError(w, "invalid slot: "+err.Error(), http.StatusBadRequest) return } + rawBeaconBlockRoot := r.PathValue("beacon_block_root") + if rawBeaconBlockRoot == "" { + httputil.HandleError(w, "beacon_block_root is required in URL params", http.StatusBadRequest) + return + } + beaconBlockRoot, err := bytesutil.DecodeHexWithLength(rawBeaconBlockRoot, fieldparams.RootLength) + if err != nil { + httputil.HandleError(w, "invalid beacon_block_root: "+err.Error(), http.StatusBadRequest) + return + } resp, err := s.V1Alpha1Server.GetExecutionPayloadEnvelope(ctx, ð.ExecutionPayloadEnvelopeRequest{ Slot: primitives.Slot(slot), @@ -237,14 +261,35 @@ func (s *Server) ExecutionPayloadEnvelope(w http.ResponseWriter, r *http.Request httputil.HandleError(w, "could not get execution payload envelope: "+err.Error(), http.StatusInternalServerError) return } + if !bytes.Equal(resp.Envelope.BeaconBlockRoot, beaconBlockRoot) { + httputil.HandleError(w, "cached envelope beacon_block_root does not match request", http.StatusNotFound) + return + } - jsonEnvelope, err := structs.ExecutionPayloadEnvelopeFromConsensus(resp.Envelope) + blinded, err := structs.WireBlindedFromFull(resp.Envelope) if err != nil { - httputil.HandleError(w, "could not convert envelope to JSON: "+err.Error(), http.StatusInternalServerError) + httputil.HandleError(w, "could not build blinded envelope: "+err.Error(), http.StatusInternalServerError) return } + w.Header().Set(api.VersionHeader, version.String(version.Gloas)) - httputil.WriteJson(w, &structs.GetValidatorExecutionPayloadEnvelopeResponse{ + + if httputil.RespondWithSsz(r) { + sszBytes, err := blinded.MarshalSSZ() + if err != nil { + httputil.HandleError(w, "could not marshal blinded envelope to SSZ: "+err.Error(), http.StatusInternalServerError) + return + } + httputil.WriteSsz(w, sszBytes) + return + } + + jsonEnvelope, err := structs.BlindedExecutionPayloadEnvelopeFromConsensus(blinded) + if err != nil { + httputil.HandleError(w, "could not convert envelope to JSON: "+err.Error(), http.StatusInternalServerError) + return + } + httputil.WriteJson(w, &structs.GetValidatorBlindedExecutionPayloadEnvelopeResponse{ Version: version.String(version.Gloas), Data: jsonEnvelope, }) diff --git a/beacon-chain/rpc/eth/validator/handlers_block_gloas_test.go b/beacon-chain/rpc/eth/validator/handlers_block_gloas_test.go index 9ce08967e825..89779b61495f 100644 --- a/beacon-chain/rpc/eth/validator/handlers_block_gloas_test.go +++ b/beacon-chain/rpc/eth/validator/handlers_block_gloas_test.go @@ -22,10 +22,12 @@ import ( "github.com/OffchainLabs/prysm/v7/encoding/bytesutil" enginev1 "github.com/OffchainLabs/prysm/v7/proto/engine/v1" eth "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1" + "github.com/OffchainLabs/prysm/v7/runtime/version" "github.com/OffchainLabs/prysm/v7/testing/assert" mock2 "github.com/OffchainLabs/prysm/v7/testing/mock" "github.com/OffchainLabs/prysm/v7/testing/require" "github.com/OffchainLabs/prysm/v7/testing/util" + "github.com/ethereum/go-ethereum/common/hexutil" "go.uber.org/mock/gomock" ) @@ -47,6 +49,7 @@ func testEnvelope() *eth.ExecutionPayloadEnvelope { BlockHash: make([]byte, 32), SlotNumber: 1, }, + ExecutionRequests: &enginev1.ExecutionRequests{}, BuilderIndex: 0, BeaconBlockRoot: make([]byte, 32), ParentBeaconBlockRoot: make([]byte, 32), @@ -54,10 +57,14 @@ func testEnvelope() *eth.ExecutionPayloadEnvelope { } func gloasGenericBlock() *eth.GenericBeaconBlock { + return gloasGenericBlockWithBuilder(params.BeaconConfig().BuilderIndexSelfBuild) +} + +func gloasGenericBlockWithBuilder(builderIndex primitives.BuilderIndex) *eth.GenericBeaconBlock { + blk := util.NewBeaconBlockGloas().Block + blk.Body.SignedExecutionPayloadBid.Message.BuilderIndex = builderIndex return ð.GenericBeaconBlock{ - Block: ð.GenericBeaconBlock_Gloas{ - Gloas: util.NewBeaconBlockGloas().Block, - }, + Block: ð.GenericBeaconBlock_Gloas{Gloas: blk}, } } @@ -198,6 +205,41 @@ func TestProduceBlockV4_IncludePayloadFalse(t *testing.T) { require.Equal(t, "false", writer.Header().Get(api.ExecutionPayloadIncludedHeader)) } +// An external builder bid returns only the block, even with include_payload=true. +func TestProduceBlockV4_BuilderBidExcludesPayload(t *testing.T) { + params.SetupTestConfigCleanup(t) + cfg := params.BeaconConfig().Copy() + cfg.GloasForkEpoch = 0 + params.OverrideBeaconConfig(cfg) + + ctrl := gomock.NewController(t) + v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl) + // Builder index != self-build, so GetExecutionPayloadEnvelope must not be called. + v1alpha1Server.EXPECT().GetBeaconBlock(gomock.Any(), gomock.Any()).Return(gloasGenericBlockWithBuilder(3), nil) + + server := &Server{ + V1Alpha1Server: v1alpha1Server, + SyncChecker: &mockSync.Sync{IsSyncing: false}, + OptimisticModeFetcher: &blockchainTesting.ChainService{}, + BlockRewardFetcher: &rewardtesting.MockBlockRewardFetcher{Rewards: &structs.BlockRewards{Total: "10"}}, + } + request := httptest.NewRequest(http.MethodGet, fmt.Sprintf("http://foo.example/eth/v4/validator/blocks/1?randao_reveal=%s&graffiti=%s", testRandao, testGraffiti), nil) + request.SetPathValue("slot", "1") + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + server.ProduceBlockV4(writer, request) + require.Equal(t, http.StatusOK, writer.Code) + + var resp structs.ProduceBlockV4Response + require.NoError(t, json.Unmarshal(writer.Body.Bytes(), &resp)) + assert.Equal(t, false, resp.ExecutionPayloadIncluded) + require.Equal(t, "false", writer.Header().Get(api.ExecutionPayloadIncludedHeader)) + + var block structs.BeaconBlockGloas + require.NoError(t, json.Unmarshal(resp.Data, &block)) + assert.NotNil(t, block.Body) +} + func TestProduceBlockV4_PreGloasSlotRejected(t *testing.T) { params.SetupTestConfigCleanup(t) cfg := params.BeaconConfig().Copy() @@ -273,6 +315,69 @@ func TestProduceBlockV4_SSZ_IncludePayloadTrue(t *testing.T) { assert.Equal(t, true, writer.Body.Len() > 0) } +// GET returns blinded SSZ that must roundtrip with HTR matching the full envelope. +func TestExecutionPayloadEnvelope_SSZ(t *testing.T) { + params.SetupTestConfigCleanup(t) + cfg := params.BeaconConfig().Copy() + cfg.GloasForkEpoch = 0 + params.OverrideBeaconConfig(cfg) + + ctrl := gomock.NewController(t) + envelope := testEnvelope() + v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl) + v1alpha1Server.EXPECT().GetExecutionPayloadEnvelope(gomock.Any(), gomock.Any()).Return( + ð.ExecutionPayloadEnvelopeResponse{Envelope: envelope}, nil, + ) + + server := &Server{V1Alpha1Server: v1alpha1Server} + bbrHex := hexutil.Encode(envelope.BeaconBlockRoot) + request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/validator/execution_payload_envelope/1/"+bbrHex, nil) + request.SetPathValue("slot", "1") + request.SetPathValue("beacon_block_root", bbrHex) + request.Header.Set("Accept", "application/octet-stream") + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + server.ExecutionPayloadEnvelope(writer, request) + assert.Equal(t, http.StatusOK, writer.Code) + assert.Equal(t, "application/octet-stream", writer.Header().Get("Content-Type")) + assert.Equal(t, version.String(version.Gloas), writer.Header().Get("Eth-Consensus-Version")) + + blinded := ð.WireBlindedExecutionPayloadEnvelope{} + require.NoError(t, blinded.UnmarshalSSZ(writer.Body.Bytes())) + wantHTR, err := envelope.HashTreeRoot() + require.NoError(t, err) + gotHTR, err := blinded.HashTreeRoot() + require.NoError(t, err) + assert.Equal(t, wantHTR, gotHTR) +} + +func TestExecutionPayloadEnvelope_BeaconBlockRootMismatch(t *testing.T) { + params.SetupTestConfigCleanup(t) + cfg := params.BeaconConfig().Copy() + cfg.GloasForkEpoch = 0 + params.OverrideBeaconConfig(cfg) + + ctrl := gomock.NewController(t) + envelope := testEnvelope() + v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl) + v1alpha1Server.EXPECT().GetExecutionPayloadEnvelope(gomock.Any(), gomock.Any()).Return( + ð.ExecutionPayloadEnvelopeResponse{Envelope: envelope}, nil, + ) + + server := &Server{V1Alpha1Server: v1alpha1Server} + requested := make([]byte, 32) + requested[0] = 1 // differs from the cached envelope's zero root + bbrHex := hexutil.Encode(requested) + request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/validator/execution_payload_envelope/1/"+bbrHex, nil) + request.SetPathValue("slot", "1") + request.SetPathValue("beacon_block_root", bbrHex) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + server.ExecutionPayloadEnvelope(writer, request) + assert.Equal(t, http.StatusNotFound, writer.Code) + assert.StringContains(t, "does not match", writer.Body.String()) +} + func TestProduceBlockV4_SSZ_IncludePayloadFalse(t *testing.T) { params.SetupTestConfigCleanup(t) cfg := params.BeaconConfig().Copy() diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_payload_envelope.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_payload_envelope.go index 292903700a32..003fb7462d4b 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_payload_envelope.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_payload_envelope.go @@ -90,7 +90,7 @@ func (vs *Server) GetExecutionPayloadEnvelope( if req == nil { return nil, status.Error(codes.InvalidArgument, "request cannot be nil") } - span.SetAttributes(trace.Int64Attribute("slot", int64(req.Slot))) + span.SetAttributes(trace.StringAttribute("slot", fmt.Sprintf("%d", req.Slot))) if slots.ToEpoch(req.Slot) < params.BeaconConfig().GloasForkEpoch { return nil, status.Errorf(codes.InvalidArgument, @@ -131,8 +131,8 @@ func (vs *Server) PublishExecutionPayloadEnvelope( beaconBlockRoot := bytesutil.ToBytes32(req.Message.BeaconBlockRoot) span.SetAttributes( - trace.Int64Attribute("slot", int64(envSlot)), // lint:ignore uintcast -- safe for tracing. - trace.Int64Attribute("builderIndex", int64(req.Message.BuilderIndex)), + trace.StringAttribute("slot", fmt.Sprintf("%d", envSlot)), + trace.StringAttribute("builderIndex", fmt.Sprintf("%d", req.Message.BuilderIndex)), trace.StringAttribute("beaconBlockRoot", fmt.Sprintf("%#x", beaconBlockRoot[:8])), ) @@ -162,7 +162,8 @@ func (vs *Server) PublishExecutionPayloadEnvelope( return nil, status.Errorf(codes.Internal, "could not wrap signed envelope: %v", err) } if err := vs.ExecutionPayloadEnvelopeReceiver.ReceiveExecutionPayloadEnvelope(ctx, roSigned); err != nil { - return nil, status.Errorf(codes.Internal, "failed to receive execution payload envelope: %v", err) + // Broadcast already succeeded; import failed. REST maps Aborted -> 202 (beacon-APIs #580). + return nil, status.Errorf(codes.Aborted, "failed to receive execution payload envelope: %v", err) } log.Info("Successfully published execution payload envelope") diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_payload_envelope_test.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_payload_envelope_test.go index 564180e781bd..78172df49471 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_payload_envelope_test.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_payload_envelope_test.go @@ -15,6 +15,9 @@ import ( ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1" "github.com/OffchainLabs/prysm/v7/testing/require" "github.com/OffchainLabs/prysm/v7/testing/util" + "github.com/pkg/errors" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) func testGloasBlock(t *testing.T) (*consensusblocks.GetPayloadResponse, interfaces.SignedBeaconBlock) { @@ -207,11 +210,53 @@ func TestPublishExecutionPayloadEnvelope_Success(t *testing.T) { require.Equal(t, 1, receiver.calls) } +func TestPublishExecutionPayloadEnvelope_ImportFailureIsAborted(t *testing.T) { + params.SetupTestConfigCleanup(t) + cfg := params.BeaconConfig().Copy() + cfg.GloasForkEpoch = 0 + params.OverrideBeaconConfig(cfg) + + broadcaster := &mockp2p.MockBroadcaster{} + receiver := &mockExecutionPayloadEnvelopeReceiver{err: errors.New("import failed")} + vs := &Server{ + P2P: broadcaster, + ExecutionPayloadEnvelopeReceiver: receiver, + } + + req := ðpb.SignedExecutionPayloadEnvelope{ + Message: ðpb.ExecutionPayloadEnvelope{ + Payload: &enginev1.ExecutionPayloadGloas{ + ParentHash: make([]byte, 32), + FeeRecipient: make([]byte, 20), + StateRoot: make([]byte, 32), + ReceiptsRoot: make([]byte, 32), + LogsBloom: make([]byte, 256), + PrevRandao: make([]byte, 32), + BaseFeePerGas: make([]byte, 32), + BlockHash: make([]byte, 32), + ExtraData: make([]byte, 0), + SlotNumber: 1, + }, + ExecutionRequests: &enginev1.ExecutionRequests{}, + BeaconBlockRoot: make([]byte, 32), + ParentBeaconBlockRoot: make([]byte, 32), + }, + Signature: make([]byte, 96), + } + + _, err := vs.PublishExecutionPayloadEnvelope(t.Context(), req) + require.NotNil(t, err) + // Broadcast must have happened before the import failure (spec 202). + require.Equal(t, true, broadcaster.BroadcastCalled.Load()) + require.Equal(t, codes.Aborted, status.Code(err)) +} + type mockExecutionPayloadEnvelopeReceiver struct { calls int + err error } func (m *mockExecutionPayloadEnvelopeReceiver) ReceiveExecutionPayloadEnvelope(_ context.Context, _ interfaces.ROSignedExecutionPayloadEnvelope) error { m.calls++ - return nil + return m.err } diff --git a/changelog/james-prysm_rest-gloas-validation.md b/changelog/james-prysm_rest-gloas-validation.md new file mode 100644 index 000000000000..f9d47bd0c45b --- /dev/null +++ b/changelog/james-prysm_rest-gloas-validation.md @@ -0,0 +1,32 @@ +### Added + +- SSZ support for GET and POST of execution payload envelope and envelope contents. +- `broadcast_validation` query parameter on POST execution payload envelope. +- Spec-wire `WireBlindedExecutionPayloadEnvelope` types and `Eth-Execution-Payload-Blinded` + header for the stateful publish path (beacon-APIs #580). +- `202` response on POST execution payload envelope when the envelope is broadcast + but fails database integration (beacon-APIs #580). +- `ProduceBlockV4` returns only the beacon block when the produced block uses an + external builder bid, regardless of `include_payload` (beacon-APIs #580). + +### Changed + +- `GET /eth/v1/validator/execution_payload_envelope/{slot}` → + `GET /eth/v1/validator/execution_payload_envelopes/{slot}/{beacon_block_root}`; + the response is the spec-wire `BlindedExecutionPayloadEnvelope` (payload replaced + by `payload_root`, HTR equivalent to the full envelope). Returns only + `Eth-Consensus-Version` (beacon-APIs #580 / PR #10). +- Stateful self-build now works end to end: the validator client fetches the blinded + envelope from the BN, signs its (HTR-equivalent) root, and publishes the + `SignedBlindedExecutionPayloadEnvelope`. +- `POST /eth/v1/beacon/execution_payload_envelopes` body shape is now selected by + the required `Eth-Execution-Payload-Blinded` request header: + - `true` → `SignedBlindedExecutionPayloadEnvelope` (stateful — BN reconstructs + the full envelope from its cache). + - `false` → `SignedExecutionPayloadEnvelopeContents` (stateless — body carries + blobs and KZG proofs). + Replaces the prior SSZ-lead-offset / JSON wrapper-key probe. +- Pluralized gloas execution payload endpoint paths to match the REST naming + convention (beacon-APIs #613): `POST /eth/v1/beacon/execution_payload_bid` → + `/eth/v1/beacon/execution_payload_bids`, and the execution payload envelope + paths use `execution_payload_envelopes`. \ No newline at end of file diff --git a/proto/prysm/v1alpha1/BUILD.bazel b/proto/prysm/v1alpha1/BUILD.bazel index f59f71366c04..fa6784e0b513 100644 --- a/proto/prysm/v1alpha1/BUILD.bazel +++ b/proto/prysm/v1alpha1/BUILD.bazel @@ -211,6 +211,9 @@ ssz_gloas_objs = [ "SignedExecutionPayloadBid", "SignedBlindedExecutionPayloadEnvelope", "SignedExecutionPayloadEnvelope", + "SignedExecutionPayloadEnvelopeContents", + "SignedWireBlindedExecutionPayloadEnvelope", + "WireBlindedExecutionPayloadEnvelope", "BeaconBlockGloas", "BeaconBlockContentsGloas", "SignedBeaconBlockGloas", diff --git a/proto/prysm/v1alpha1/gloas.pb.go b/proto/prysm/v1alpha1/gloas.pb.go index 0d2c43580868..89437c749d9d 100755 --- a/proto/prysm/v1alpha1/gloas.pb.go +++ b/proto/prysm/v1alpha1/gloas.pb.go @@ -1289,7 +1289,7 @@ type BeaconBlockContentsGloas struct { state protoimpl.MessageState `protogen:"open.v1"` Block *BeaconBlockGloas `protobuf:"bytes,1,opt,name=block,proto3" json:"block,omitempty"` ExecutionPayloadEnvelope *ExecutionPayloadEnvelope `protobuf:"bytes,2,opt,name=execution_payload_envelope,json=executionPayloadEnvelope,proto3" json:"execution_payload_envelope,omitempty"` - KzgProofs [][]byte `protobuf:"bytes,3,rep,name=kzg_proofs,json=kzgProofs,proto3" json:"kzg_proofs,omitempty" ssz-max:"4096" ssz-size:"?,48"` + KzgProofs [][]byte `protobuf:"bytes,3,rep,name=kzg_proofs,json=kzgProofs,proto3" json:"kzg_proofs,omitempty" ssz-max:"33554432" ssz-size:"?,48"` Blobs [][]byte `protobuf:"bytes,4,rep,name=blobs,proto3" json:"blobs,omitempty" ssz-max:"4096" ssz-size:"?,131072"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -1353,6 +1353,66 @@ func (x *BeaconBlockContentsGloas) GetBlobs() [][]byte { return nil } +type SignedExecutionPayloadEnvelopeContents struct { + state protoimpl.MessageState `protogen:"open.v1"` + SignedExecutionPayloadEnvelope *SignedExecutionPayloadEnvelope `protobuf:"bytes,1,opt,name=signed_execution_payload_envelope,json=signedExecutionPayloadEnvelope,proto3" json:"signed_execution_payload_envelope,omitempty"` + KzgProofs [][]byte `protobuf:"bytes,2,rep,name=kzg_proofs,json=kzgProofs,proto3" json:"kzg_proofs,omitempty" ssz-max:"33554432" ssz-size:"?,48"` + Blobs [][]byte `protobuf:"bytes,3,rep,name=blobs,proto3" json:"blobs,omitempty" ssz-max:"4096" ssz-size:"?,131072"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SignedExecutionPayloadEnvelopeContents) Reset() { + *x = SignedExecutionPayloadEnvelopeContents{} + mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SignedExecutionPayloadEnvelopeContents) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SignedExecutionPayloadEnvelopeContents) ProtoMessage() {} + +func (x *SignedExecutionPayloadEnvelopeContents) ProtoReflect() protoreflect.Message { + mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[14] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SignedExecutionPayloadEnvelopeContents.ProtoReflect.Descriptor instead. +func (*SignedExecutionPayloadEnvelopeContents) Descriptor() ([]byte, []int) { + return file_proto_prysm_v1alpha1_gloas_proto_rawDescGZIP(), []int{14} +} + +func (x *SignedExecutionPayloadEnvelopeContents) GetSignedExecutionPayloadEnvelope() *SignedExecutionPayloadEnvelope { + if x != nil { + return x.SignedExecutionPayloadEnvelope + } + return nil +} + +func (x *SignedExecutionPayloadEnvelopeContents) GetKzgProofs() [][]byte { + if x != nil { + return x.KzgProofs + } + return nil +} + +func (x *SignedExecutionPayloadEnvelopeContents) GetBlobs() [][]byte { + if x != nil { + return x.Blobs + } + return nil +} + type BuilderPendingPayment struct { state protoimpl.MessageState `protogen:"open.v1"` Weight github_com_OffchainLabs_prysm_v7_consensus_types_primitives.Gwei `protobuf:"varint,1,opt,name=weight,proto3" json:"weight,omitempty" cast-type:"github.com/OffchainLabs/prysm/v7/consensus-types/primitives.Gwei"` @@ -1363,7 +1423,7 @@ type BuilderPendingPayment struct { func (x *BuilderPendingPayment) Reset() { *x = BuilderPendingPayment{} - mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[14] + mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1375,7 +1435,7 @@ func (x *BuilderPendingPayment) String() string { func (*BuilderPendingPayment) ProtoMessage() {} func (x *BuilderPendingPayment) ProtoReflect() protoreflect.Message { - mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[14] + mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1388,7 +1448,7 @@ func (x *BuilderPendingPayment) ProtoReflect() protoreflect.Message { // Deprecated: Use BuilderPendingPayment.ProtoReflect.Descriptor instead. func (*BuilderPendingPayment) Descriptor() ([]byte, []int) { - return file_proto_prysm_v1alpha1_gloas_proto_rawDescGZIP(), []int{14} + return file_proto_prysm_v1alpha1_gloas_proto_rawDescGZIP(), []int{15} } func (x *BuilderPendingPayment) GetWeight() github_com_OffchainLabs_prysm_v7_consensus_types_primitives.Gwei { @@ -1416,7 +1476,7 @@ type BuilderPendingWithdrawal struct { func (x *BuilderPendingWithdrawal) Reset() { *x = BuilderPendingWithdrawal{} - mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[15] + mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1428,7 +1488,7 @@ func (x *BuilderPendingWithdrawal) String() string { func (*BuilderPendingWithdrawal) ProtoMessage() {} func (x *BuilderPendingWithdrawal) ProtoReflect() protoreflect.Message { - mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[15] + mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[16] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1441,7 +1501,7 @@ func (x *BuilderPendingWithdrawal) ProtoReflect() protoreflect.Message { // Deprecated: Use BuilderPendingWithdrawal.ProtoReflect.Descriptor instead. func (*BuilderPendingWithdrawal) Descriptor() ([]byte, []int) { - return file_proto_prysm_v1alpha1_gloas_proto_rawDescGZIP(), []int{15} + return file_proto_prysm_v1alpha1_gloas_proto_rawDescGZIP(), []int{16} } func (x *BuilderPendingWithdrawal) GetFeeRecipient() []byte { @@ -1478,7 +1538,7 @@ type DataColumnSidecarGloas struct { func (x *DataColumnSidecarGloas) Reset() { *x = DataColumnSidecarGloas{} - mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[16] + mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1490,7 +1550,7 @@ func (x *DataColumnSidecarGloas) String() string { func (*DataColumnSidecarGloas) ProtoMessage() {} func (x *DataColumnSidecarGloas) ProtoReflect() protoreflect.Message { - mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[16] + mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[17] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1503,7 +1563,7 @@ func (x *DataColumnSidecarGloas) ProtoReflect() protoreflect.Message { // Deprecated: Use DataColumnSidecarGloas.ProtoReflect.Descriptor instead. func (*DataColumnSidecarGloas) Descriptor() ([]byte, []int) { - return file_proto_prysm_v1alpha1_gloas_proto_rawDescGZIP(), []int{16} + return file_proto_prysm_v1alpha1_gloas_proto_rawDescGZIP(), []int{17} } func (x *DataColumnSidecarGloas) GetIndex() uint64 { @@ -1554,7 +1614,7 @@ type ExecutionPayloadEnvelope struct { func (x *ExecutionPayloadEnvelope) Reset() { *x = ExecutionPayloadEnvelope{} - mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[17] + mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1566,7 +1626,7 @@ func (x *ExecutionPayloadEnvelope) String() string { func (*ExecutionPayloadEnvelope) ProtoMessage() {} func (x *ExecutionPayloadEnvelope) ProtoReflect() protoreflect.Message { - mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[17] + mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1579,7 +1639,7 @@ func (x *ExecutionPayloadEnvelope) ProtoReflect() protoreflect.Message { // Deprecated: Use ExecutionPayloadEnvelope.ProtoReflect.Descriptor instead. func (*ExecutionPayloadEnvelope) Descriptor() ([]byte, []int) { - return file_proto_prysm_v1alpha1_gloas_proto_rawDescGZIP(), []int{17} + return file_proto_prysm_v1alpha1_gloas_proto_rawDescGZIP(), []int{18} } func (x *ExecutionPayloadEnvelope) GetPayload() *v1.ExecutionPayloadGloas { @@ -1627,7 +1687,7 @@ type SignedExecutionPayloadEnvelope struct { func (x *SignedExecutionPayloadEnvelope) Reset() { *x = SignedExecutionPayloadEnvelope{} - mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[18] + mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1639,7 +1699,7 @@ func (x *SignedExecutionPayloadEnvelope) String() string { func (*SignedExecutionPayloadEnvelope) ProtoMessage() {} func (x *SignedExecutionPayloadEnvelope) ProtoReflect() protoreflect.Message { - mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[18] + mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1652,7 +1712,7 @@ func (x *SignedExecutionPayloadEnvelope) ProtoReflect() protoreflect.Message { // Deprecated: Use SignedExecutionPayloadEnvelope.ProtoReflect.Descriptor instead. func (*SignedExecutionPayloadEnvelope) Descriptor() ([]byte, []int) { - return file_proto_prysm_v1alpha1_gloas_proto_rawDescGZIP(), []int{18} + return file_proto_prysm_v1alpha1_gloas_proto_rawDescGZIP(), []int{19} } func (x *SignedExecutionPayloadEnvelope) GetMessage() *ExecutionPayloadEnvelope { @@ -1684,7 +1744,7 @@ type BlindedExecutionPayloadEnvelope struct { func (x *BlindedExecutionPayloadEnvelope) Reset() { *x = BlindedExecutionPayloadEnvelope{} - mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[19] + mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1696,7 +1756,7 @@ func (x *BlindedExecutionPayloadEnvelope) String() string { func (*BlindedExecutionPayloadEnvelope) ProtoMessage() {} func (x *BlindedExecutionPayloadEnvelope) ProtoReflect() protoreflect.Message { - mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[19] + mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[20] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1709,7 +1769,7 @@ func (x *BlindedExecutionPayloadEnvelope) ProtoReflect() protoreflect.Message { // Deprecated: Use BlindedExecutionPayloadEnvelope.ProtoReflect.Descriptor instead. func (*BlindedExecutionPayloadEnvelope) Descriptor() ([]byte, []int) { - return file_proto_prysm_v1alpha1_gloas_proto_rawDescGZIP(), []int{19} + return file_proto_prysm_v1alpha1_gloas_proto_rawDescGZIP(), []int{20} } func (x *BlindedExecutionPayloadEnvelope) GetBlockHash() []byte { @@ -1771,7 +1831,7 @@ type SignedBlindedExecutionPayloadEnvelope struct { func (x *SignedBlindedExecutionPayloadEnvelope) Reset() { *x = SignedBlindedExecutionPayloadEnvelope{} - mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[20] + mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1783,7 +1843,7 @@ func (x *SignedBlindedExecutionPayloadEnvelope) String() string { func (*SignedBlindedExecutionPayloadEnvelope) ProtoMessage() {} func (x *SignedBlindedExecutionPayloadEnvelope) ProtoReflect() protoreflect.Message { - mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[20] + mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[21] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1796,7 +1856,7 @@ func (x *SignedBlindedExecutionPayloadEnvelope) ProtoReflect() protoreflect.Mess // Deprecated: Use SignedBlindedExecutionPayloadEnvelope.ProtoReflect.Descriptor instead. func (*SignedBlindedExecutionPayloadEnvelope) Descriptor() ([]byte, []int) { - return file_proto_prysm_v1alpha1_gloas_proto_rawDescGZIP(), []int{20} + return file_proto_prysm_v1alpha1_gloas_proto_rawDescGZIP(), []int{21} } func (x *SignedBlindedExecutionPayloadEnvelope) GetMessage() *BlindedExecutionPayloadEnvelope { @@ -1813,6 +1873,134 @@ func (x *SignedBlindedExecutionPayloadEnvelope) GetSignature() []byte { return nil } +type WireBlindedExecutionPayloadEnvelope struct { + state protoimpl.MessageState `protogen:"open.v1"` + PayloadRoot []byte `protobuf:"bytes,1,opt,name=payload_root,json=payloadRoot,proto3" json:"payload_root,omitempty" ssz-size:"32"` + ExecutionRequests *v1.ExecutionRequests `protobuf:"bytes,2,opt,name=execution_requests,json=executionRequests,proto3" json:"execution_requests,omitempty"` + BuilderIndex github_com_OffchainLabs_prysm_v7_consensus_types_primitives.BuilderIndex `protobuf:"varint,3,opt,name=builder_index,json=builderIndex,proto3" json:"builder_index,omitempty" cast-type:"github.com/OffchainLabs/prysm/v7/consensus-types/primitives.BuilderIndex"` + BeaconBlockRoot []byte `protobuf:"bytes,4,opt,name=beacon_block_root,json=beaconBlockRoot,proto3" json:"beacon_block_root,omitempty" ssz-size:"32"` + ParentBeaconBlockRoot []byte `protobuf:"bytes,5,opt,name=parent_beacon_block_root,json=parentBeaconBlockRoot,proto3" json:"parent_beacon_block_root,omitempty" ssz-size:"32"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WireBlindedExecutionPayloadEnvelope) Reset() { + *x = WireBlindedExecutionPayloadEnvelope{} + mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WireBlindedExecutionPayloadEnvelope) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WireBlindedExecutionPayloadEnvelope) ProtoMessage() {} + +func (x *WireBlindedExecutionPayloadEnvelope) ProtoReflect() protoreflect.Message { + mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[22] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WireBlindedExecutionPayloadEnvelope.ProtoReflect.Descriptor instead. +func (*WireBlindedExecutionPayloadEnvelope) Descriptor() ([]byte, []int) { + return file_proto_prysm_v1alpha1_gloas_proto_rawDescGZIP(), []int{22} +} + +func (x *WireBlindedExecutionPayloadEnvelope) GetPayloadRoot() []byte { + if x != nil { + return x.PayloadRoot + } + return nil +} + +func (x *WireBlindedExecutionPayloadEnvelope) GetExecutionRequests() *v1.ExecutionRequests { + if x != nil { + return x.ExecutionRequests + } + return nil +} + +func (x *WireBlindedExecutionPayloadEnvelope) GetBuilderIndex() github_com_OffchainLabs_prysm_v7_consensus_types_primitives.BuilderIndex { + if x != nil { + return x.BuilderIndex + } + return github_com_OffchainLabs_prysm_v7_consensus_types_primitives.BuilderIndex(0) +} + +func (x *WireBlindedExecutionPayloadEnvelope) GetBeaconBlockRoot() []byte { + if x != nil { + return x.BeaconBlockRoot + } + return nil +} + +func (x *WireBlindedExecutionPayloadEnvelope) GetParentBeaconBlockRoot() []byte { + if x != nil { + return x.ParentBeaconBlockRoot + } + return nil +} + +type SignedWireBlindedExecutionPayloadEnvelope struct { + state protoimpl.MessageState `protogen:"open.v1"` + Message *WireBlindedExecutionPayloadEnvelope `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + Signature []byte `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty" ssz-size:"96"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SignedWireBlindedExecutionPayloadEnvelope) Reset() { + *x = SignedWireBlindedExecutionPayloadEnvelope{} + mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SignedWireBlindedExecutionPayloadEnvelope) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SignedWireBlindedExecutionPayloadEnvelope) ProtoMessage() {} + +func (x *SignedWireBlindedExecutionPayloadEnvelope) ProtoReflect() protoreflect.Message { + mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[23] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SignedWireBlindedExecutionPayloadEnvelope.ProtoReflect.Descriptor instead. +func (*SignedWireBlindedExecutionPayloadEnvelope) Descriptor() ([]byte, []int) { + return file_proto_prysm_v1alpha1_gloas_proto_rawDescGZIP(), []int{23} +} + +func (x *SignedWireBlindedExecutionPayloadEnvelope) GetMessage() *WireBlindedExecutionPayloadEnvelope { + if x != nil { + return x.Message + } + return nil +} + +func (x *SignedWireBlindedExecutionPayloadEnvelope) GetSignature() []byte { + if x != nil { + return x.Signature + } + return nil +} + type Builder struct { state protoimpl.MessageState `protogen:"open.v1"` Pubkey []byte `protobuf:"bytes,1,opt,name=pubkey,proto3" json:"pubkey,omitempty" ssz-size:"48"` @@ -1827,7 +2015,7 @@ type Builder struct { func (x *Builder) Reset() { *x = Builder{} - mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[21] + mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1839,7 +2027,7 @@ func (x *Builder) String() string { func (*Builder) ProtoMessage() {} func (x *Builder) ProtoReflect() protoreflect.Message { - mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[21] + mi := &file_proto_prysm_v1alpha1_gloas_proto_msgTypes[24] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1852,7 +2040,7 @@ func (x *Builder) ProtoReflect() protoreflect.Message { // Deprecated: Use Builder.ProtoReflect.Descriptor instead. func (*Builder) Descriptor() ([]byte, []int) { - return file_proto_prysm_v1alpha1_gloas_proto_rawDescGZIP(), []int{21} + return file_proto_prysm_v1alpha1_gloas_proto_rawDescGZIP(), []int{24} } func (x *Builder) GetPubkey() []byte { @@ -2439,7 +2627,7 @@ var file_proto_prysm_v1alpha1_gloas_proto_rawDesc = []byte{ 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x8a, 0xb5, 0x18, 0x03, 0x35, 0x31, 0x32, 0x52, 0x10, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x69, 0x63, - 0x65, 0x73, 0x22, 0xa5, 0x02, 0x0a, 0x18, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, + 0x65, 0x73, 0x22, 0xa9, 0x02, 0x0a, 0x18, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x47, 0x6c, 0x6f, 0x61, 0x73, 0x12, 0x3d, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, @@ -2451,67 +2639,118 @@ var file_proto_prysm_v1alpha1_gloas_proto_rawDesc = []byte{ 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x52, 0x18, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, - 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x2f, 0x0a, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x33, 0x0a, 0x0a, 0x6b, 0x7a, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, - 0x0c, 0x42, 0x10, 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x34, 0x38, 0x92, 0xb5, 0x18, 0x04, 0x34, - 0x30, 0x39, 0x36, 0x52, 0x09, 0x6b, 0x7a, 0x67, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x73, 0x12, 0x2a, - 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x14, 0x8a, - 0xb5, 0x18, 0x08, 0x3f, 0x2c, 0x31, 0x33, 0x31, 0x30, 0x37, 0x32, 0x92, 0xb5, 0x18, 0x04, 0x34, - 0x30, 0x39, 0x36, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x22, 0xc6, 0x01, 0x0a, 0x15, 0x42, - 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x79, - 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x5c, 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, - 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, - 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, - 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x47, 0x77, 0x65, 0x69, 0x52, 0x06, 0x77, 0x65, 0x69, 0x67, - 0x68, 0x74, 0x12, 0x4f, 0x0a, 0x0a, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, - 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, - 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x57, 0x69, 0x74, - 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x52, 0x0a, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, - 0x77, 0x61, 0x6c, 0x22, 0x98, 0x02, 0x0a, 0x18, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x50, - 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, - 0x12, 0x2b, 0x0a, 0x0d, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x32, 0x30, 0x52, - 0x0c, 0x66, 0x65, 0x65, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x5c, 0x0a, - 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, + 0x0c, 0x42, 0x14, 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x34, 0x38, 0x92, 0xb5, 0x18, 0x08, 0x33, + 0x33, 0x35, 0x35, 0x34, 0x34, 0x33, 0x32, 0x52, 0x09, 0x6b, 0x7a, 0x67, 0x50, 0x72, 0x6f, 0x6f, + 0x66, 0x73, 0x12, 0x2a, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, + 0x0c, 0x42, 0x14, 0x8a, 0xb5, 0x18, 0x08, 0x3f, 0x2c, 0x31, 0x33, 0x31, 0x30, 0x37, 0x32, 0x92, + 0xb5, 0x18, 0x04, 0x34, 0x30, 0x39, 0x36, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x22, 0x8c, + 0x02, 0x0a, 0x26, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, + 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, + 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x80, 0x01, 0x0a, 0x21, 0x73, 0x69, + 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, + 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, + 0x67, 0x6e, 0x65, 0x64, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, + 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x52, 0x1e, 0x73, 0x69, + 0x67, 0x6e, 0x65, 0x64, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, + 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x33, 0x0a, 0x0a, + 0x6b, 0x7a, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, + 0x42, 0x14, 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x34, 0x38, 0x92, 0xb5, 0x18, 0x08, 0x33, 0x33, + 0x35, 0x35, 0x34, 0x34, 0x33, 0x32, 0x52, 0x09, 0x6b, 0x7a, 0x67, 0x50, 0x72, 0x6f, 0x6f, 0x66, + 0x73, 0x12, 0x2a, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, + 0x42, 0x14, 0x8a, 0xb5, 0x18, 0x08, 0x3f, 0x2c, 0x31, 0x33, 0x31, 0x30, 0x37, 0x32, 0x92, 0xb5, + 0x18, 0x04, 0x34, 0x30, 0x39, 0x36, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x22, 0xc6, 0x01, + 0x0a, 0x15, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, + 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x5c, 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, + 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, + 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x47, 0x77, 0x65, 0x69, 0x52, 0x06, 0x77, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x4f, 0x0a, 0x0a, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, + 0x77, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, + 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x52, 0x0a, 0x77, 0x69, 0x74, 0x68, + 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x22, 0x98, 0x02, 0x0a, 0x18, 0x42, 0x75, 0x69, 0x6c, 0x64, + 0x65, 0x72, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, + 0x77, 0x61, 0x6c, 0x12, 0x2b, 0x0a, 0x0d, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x65, 0x63, 0x69, 0x70, + 0x69, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, + 0x32, 0x30, 0x52, 0x0c, 0x66, 0x65, 0x65, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, + 0x12, 0x5c, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, + 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, + 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, + 0x73, 0x2e, 0x47, 0x77, 0x65, 0x69, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x71, + 0x0a, 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4c, 0x82, 0xb5, 0x18, 0x48, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, + 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, + 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, + 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x49, 0x6e, + 0x64, 0x65, 0x78, 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, + 0x78, 0x22, 0x99, 0x02, 0x0a, 0x16, 0x44, 0x61, 0x74, 0x61, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, + 0x53, 0x69, 0x64, 0x65, 0x63, 0x61, 0x72, 0x47, 0x6c, 0x6f, 0x61, 0x73, 0x12, 0x14, 0x0a, 0x05, + 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x12, 0x2a, 0x0a, 0x06, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0c, 0x42, 0x12, 0x8a, 0xb5, 0x18, 0x06, 0x3f, 0x2c, 0x32, 0x30, 0x34, 0x38, 0x92, 0xb5, + 0x18, 0x04, 0x34, 0x30, 0x39, 0x36, 0x52, 0x06, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x12, 0x2f, + 0x0a, 0x0a, 0x6b, 0x7a, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x73, 0x18, 0x04, 0x20, 0x03, + 0x28, 0x0c, 0x42, 0x10, 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x34, 0x38, 0x92, 0xb5, 0x18, 0x04, + 0x34, 0x30, 0x39, 0x36, 0x52, 0x09, 0x6b, 0x7a, 0x67, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x73, 0x12, + 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, - 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x47, - 0x77, 0x65, 0x69, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x71, 0x0a, 0x0d, 0x62, - 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x04, 0x42, 0x4c, 0x82, 0xb5, 0x18, 0x48, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, - 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, - 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, - 0x76, 0x65, 0x73, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, - 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x99, - 0x02, 0x0a, 0x16, 0x44, 0x61, 0x74, 0x61, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x53, 0x69, 0x64, - 0x65, 0x63, 0x61, 0x72, 0x47, 0x6c, 0x6f, 0x61, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, - 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, - 0x2a, 0x0a, 0x06, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x42, - 0x12, 0x8a, 0xb5, 0x18, 0x06, 0x3f, 0x2c, 0x32, 0x30, 0x34, 0x38, 0x92, 0xb5, 0x18, 0x04, 0x34, - 0x30, 0x39, 0x36, 0x52, 0x06, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x12, 0x2f, 0x0a, 0x0a, 0x6b, - 0x7a, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0c, 0x42, - 0x10, 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x34, 0x38, 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, 0x39, - 0x36, 0x52, 0x09, 0x6b, 0x7a, 0x67, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x73, 0x12, 0x58, 0x0a, 0x04, - 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, - 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, - 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, - 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x12, 0x32, 0x0a, 0x11, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, - 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0f, 0x62, 0x65, 0x61, 0x63, 0x6f, - 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x9d, 0x03, 0x0a, 0x18, 0x45, - 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, - 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x43, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, - 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, - 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, - 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x47, 0x6c, - 0x6f, 0x61, 0x73, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x54, 0x0a, 0x12, + 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, + 0x6c, 0x6f, 0x74, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x12, 0x32, 0x0a, 0x11, 0x62, 0x65, 0x61, + 0x63, 0x6f, 0x6e, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0f, 0x62, 0x65, + 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x9d, 0x03, + 0x0a, 0x18, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, + 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x43, 0x0a, 0x07, 0x70, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x74, + 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x47, 0x6c, 0x6f, 0x61, 0x73, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, + 0x54, 0x0a, 0x12, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x65, 0x74, + 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x73, 0x52, 0x11, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x73, 0x12, 0x71, 0x0a, 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, + 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4c, 0x82, 0xb5, + 0x18, 0x48, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, + 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, + 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x42, 0x75, + 0x69, 0x6c, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, + 0x64, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x32, 0x0a, 0x11, 0x62, 0x65, 0x61, 0x63, + 0x6f, 0x6e, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0f, 0x62, 0x65, 0x61, + 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x3f, 0x0a, 0x18, + 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x5f, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, + 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x15, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x42, 0x65, + 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x91, 0x01, + 0x0a, 0x1e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, + 0x12, 0x49, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, + 0x70, 0x65, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x09, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, + 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x22, 0x94, 0x04, 0x0a, 0x1f, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x45, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, + 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x25, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, + 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, + 0x32, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, 0x54, 0x0a, 0x12, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, @@ -2527,96 +2766,98 @@ var file_proto_prysm_v1alpha1_gloas_proto_rawDesc = []byte{ 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x32, 0x0a, 0x11, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x3f, 0x0a, 0x18, 0x70, 0x61, 0x72, - 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, - 0x02, 0x33, 0x32, 0x52, 0x15, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x42, 0x65, 0x61, 0x63, 0x6f, - 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x91, 0x01, 0x0a, 0x1e, 0x53, - 0x69, 0x67, 0x6e, 0x65, 0x64, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, - 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x49, 0x0a, - 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, - 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, - 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x52, - 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, - 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x94, - 0x04, 0x0a, 0x1f, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, + 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, + 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, + 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x04, 0x73, + 0x6c, 0x6f, 0x74, 0x12, 0x32, 0x0a, 0x11, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, + 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, 0x3f, 0x0a, 0x18, 0x70, 0x61, 0x72, 0x65, 0x6e, + 0x74, 0x5f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, + 0x6f, 0x6f, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, + 0x32, 0x52, 0x15, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x9f, 0x01, 0x0a, 0x25, 0x53, 0x69, 0x67, + 0x6e, 0x65, 0x64, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, - 0x70, 0x65, 0x12, 0x25, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x61, 0x73, 0x68, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x09, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, 0x54, 0x0a, 0x12, 0x65, 0x78, 0x65, - 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, - 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x52, 0x11, 0x65, 0x78, - 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x12, - 0x71, 0x0a, 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4c, 0x82, 0xb5, 0x18, 0x48, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, - 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, - 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, - 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x49, - 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x64, - 0x65, 0x78, 0x12, 0x32, 0x0a, 0x11, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x5f, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, - 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, - 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, - 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, - 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, - 0x12, 0x32, 0x0a, 0x11, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, - 0x02, 0x33, 0x32, 0x52, 0x0f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x48, 0x61, 0x73, 0x68, 0x12, 0x3f, 0x0a, 0x18, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x62, - 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x6f, 0x6f, 0x74, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x15, - 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, - 0x6b, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x9f, 0x01, 0x0a, 0x25, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, - 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, - 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, - 0x50, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x36, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, - 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, - 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, - 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xc1, 0x03, 0x0a, 0x07, 0x42, 0x75, 0x69, 0x6c, - 0x64, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x52, 0x06, 0x70, 0x75, 0x62, - 0x6b, 0x65, 0x79, 0x12, 0x1f, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0c, 0x42, 0x05, 0x8a, 0xb5, 0x18, 0x01, 0x31, 0x52, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x33, 0x0a, 0x11, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, - 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x32, 0x30, 0x52, 0x10, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, - 0x6f, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x5e, 0x0a, 0x07, 0x62, 0x61, 0x6c, - 0x61, 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, + 0x70, 0x65, 0x12, 0x50, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, + 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x6c, 0x69, 0x6e, + 0x64, 0x65, 0x64, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x52, 0x07, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, + 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x8e, 0x03, 0x0a, 0x23, 0x57, + 0x69, 0x72, 0x65, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, + 0x70, 0x65, 0x12, 0x29, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x72, 0x6f, + 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, + 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x54, 0x0a, + 0x12, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, + 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, + 0x52, 0x11, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x73, 0x12, 0x71, 0x0a, 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x69, + 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4c, 0x82, 0xb5, 0x18, 0x48, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, - 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x47, 0x77, 0x65, 0x69, - 0x52, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x6a, 0x0a, 0x0d, 0x64, 0x65, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, - 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, - 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, - 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, - 0x73, 0x2e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x52, 0x0c, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, - 0x45, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x74, 0x0a, 0x12, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, - 0x77, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, - 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, - 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, - 0x65, 0x73, 0x2e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x52, 0x11, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x42, 0x3b, 0x5a, 0x39, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x31, 0x3b, 0x65, 0x74, 0x68, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x42, 0x75, 0x69, 0x6c, + 0x64, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, + 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x32, 0x0a, 0x11, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, + 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0f, 0x62, 0x65, 0x61, 0x63, 0x6f, + 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x3f, 0x0a, 0x18, 0x70, 0x61, + 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x5f, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, + 0x18, 0x02, 0x33, 0x32, 0x52, 0x15, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x42, 0x65, 0x61, 0x63, + 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0xa7, 0x01, 0x0a, 0x29, + 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x57, 0x69, 0x72, 0x65, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, + 0x64, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x54, 0x0a, 0x07, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x65, 0x74, 0x68, + 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x2e, 0x57, 0x69, 0x72, 0x65, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x45, 0x78, + 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, + 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, + 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xc1, 0x03, 0x0a, 0x07, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, + 0x72, 0x12, 0x1e, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, + 0x79, 0x12, 0x1f, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x42, 0x05, 0x8a, 0xb5, 0x18, 0x01, 0x31, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x33, 0x0a, 0x11, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, + 0xb5, 0x18, 0x02, 0x32, 0x30, 0x52, 0x10, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x5e, 0x0a, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, + 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, + 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, + 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x47, 0x77, 0x65, 0x69, 0x52, 0x07, + 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x6a, 0x0a, 0x0d, 0x64, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, + 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, + 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, + 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, + 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, + 0x45, 0x70, 0x6f, 0x63, 0x68, 0x52, 0x0c, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x45, 0x70, + 0x6f, 0x63, 0x68, 0x12, 0x74, 0x0a, 0x12, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, + 0x62, 0x6c, 0x65, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x42, + 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, + 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, + 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, + 0x2e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x52, 0x11, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, + 0x61, 0x62, 0x6c, 0x65, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x3b, 0x65, 0x74, 0x68, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2631,50 +2872,53 @@ func file_proto_prysm_v1alpha1_gloas_proto_rawDescGZIP() []byte { return file_proto_prysm_v1alpha1_gloas_proto_rawDescData } -var file_proto_prysm_v1alpha1_gloas_proto_msgTypes = make([]protoimpl.MessageInfo, 22) +var file_proto_prysm_v1alpha1_gloas_proto_msgTypes = make([]protoimpl.MessageInfo, 25) var file_proto_prysm_v1alpha1_gloas_proto_goTypes = []any{ - (*ExecutionPayloadBid)(nil), // 0: ethereum.eth.v1alpha1.ExecutionPayloadBid - (*SignedExecutionPayloadBid)(nil), // 1: ethereum.eth.v1alpha1.SignedExecutionPayloadBid - (*ProposerPreferences)(nil), // 2: ethereum.eth.v1alpha1.ProposerPreferences - (*SignedProposerPreferences)(nil), // 3: ethereum.eth.v1alpha1.SignedProposerPreferences - (*SubmitSignedProposerPreferencesRequest)(nil), // 4: ethereum.eth.v1alpha1.SubmitSignedProposerPreferencesRequest - (*PayloadAttestationData)(nil), // 5: ethereum.eth.v1alpha1.PayloadAttestationData - (*PayloadAttestation)(nil), // 6: ethereum.eth.v1alpha1.PayloadAttestation - (*PayloadAttestationMessage)(nil), // 7: ethereum.eth.v1alpha1.PayloadAttestationMessage - (*BeaconBlockGloas)(nil), // 8: ethereum.eth.v1alpha1.BeaconBlockGloas - (*BeaconBlockBodyGloas)(nil), // 9: ethereum.eth.v1alpha1.BeaconBlockBodyGloas - (*SignedBeaconBlockGloas)(nil), // 10: ethereum.eth.v1alpha1.SignedBeaconBlockGloas - (*BeaconStateGloas)(nil), // 11: ethereum.eth.v1alpha1.BeaconStateGloas - (*PTCs)(nil), // 12: ethereum.eth.v1alpha1.PTCs - (*BeaconBlockContentsGloas)(nil), // 13: ethereum.eth.v1alpha1.BeaconBlockContentsGloas - (*BuilderPendingPayment)(nil), // 14: ethereum.eth.v1alpha1.BuilderPendingPayment - (*BuilderPendingWithdrawal)(nil), // 15: ethereum.eth.v1alpha1.BuilderPendingWithdrawal - (*DataColumnSidecarGloas)(nil), // 16: ethereum.eth.v1alpha1.DataColumnSidecarGloas - (*ExecutionPayloadEnvelope)(nil), // 17: ethereum.eth.v1alpha1.ExecutionPayloadEnvelope - (*SignedExecutionPayloadEnvelope)(nil), // 18: ethereum.eth.v1alpha1.SignedExecutionPayloadEnvelope - (*BlindedExecutionPayloadEnvelope)(nil), // 19: ethereum.eth.v1alpha1.BlindedExecutionPayloadEnvelope - (*SignedBlindedExecutionPayloadEnvelope)(nil), // 20: ethereum.eth.v1alpha1.SignedBlindedExecutionPayloadEnvelope - (*Builder)(nil), // 21: ethereum.eth.v1alpha1.Builder - (*Eth1Data)(nil), // 22: ethereum.eth.v1alpha1.Eth1Data - (*ProposerSlashing)(nil), // 23: ethereum.eth.v1alpha1.ProposerSlashing - (*AttesterSlashingElectra)(nil), // 24: ethereum.eth.v1alpha1.AttesterSlashingElectra - (*AttestationElectra)(nil), // 25: ethereum.eth.v1alpha1.AttestationElectra - (*Deposit)(nil), // 26: ethereum.eth.v1alpha1.Deposit - (*SignedVoluntaryExit)(nil), // 27: ethereum.eth.v1alpha1.SignedVoluntaryExit - (*SyncAggregate)(nil), // 28: ethereum.eth.v1alpha1.SyncAggregate - (*SignedBLSToExecutionChange)(nil), // 29: ethereum.eth.v1alpha1.SignedBLSToExecutionChange - (*v1.ExecutionRequests)(nil), // 30: ethereum.engine.v1.ExecutionRequests - (*Fork)(nil), // 31: ethereum.eth.v1alpha1.Fork - (*BeaconBlockHeader)(nil), // 32: ethereum.eth.v1alpha1.BeaconBlockHeader - (*Validator)(nil), // 33: ethereum.eth.v1alpha1.Validator - (*Checkpoint)(nil), // 34: ethereum.eth.v1alpha1.Checkpoint - (*SyncCommittee)(nil), // 35: ethereum.eth.v1alpha1.SyncCommittee - (*HistoricalSummary)(nil), // 36: ethereum.eth.v1alpha1.HistoricalSummary - (*PendingDeposit)(nil), // 37: ethereum.eth.v1alpha1.PendingDeposit - (*PendingPartialWithdrawal)(nil), // 38: ethereum.eth.v1alpha1.PendingPartialWithdrawal - (*PendingConsolidation)(nil), // 39: ethereum.eth.v1alpha1.PendingConsolidation - (*v1.Withdrawal)(nil), // 40: ethereum.engine.v1.Withdrawal - (*v1.ExecutionPayloadGloas)(nil), // 41: ethereum.engine.v1.ExecutionPayloadGloas + (*ExecutionPayloadBid)(nil), // 0: ethereum.eth.v1alpha1.ExecutionPayloadBid + (*SignedExecutionPayloadBid)(nil), // 1: ethereum.eth.v1alpha1.SignedExecutionPayloadBid + (*ProposerPreferences)(nil), // 2: ethereum.eth.v1alpha1.ProposerPreferences + (*SignedProposerPreferences)(nil), // 3: ethereum.eth.v1alpha1.SignedProposerPreferences + (*SubmitSignedProposerPreferencesRequest)(nil), // 4: ethereum.eth.v1alpha1.SubmitSignedProposerPreferencesRequest + (*PayloadAttestationData)(nil), // 5: ethereum.eth.v1alpha1.PayloadAttestationData + (*PayloadAttestation)(nil), // 6: ethereum.eth.v1alpha1.PayloadAttestation + (*PayloadAttestationMessage)(nil), // 7: ethereum.eth.v1alpha1.PayloadAttestationMessage + (*BeaconBlockGloas)(nil), // 8: ethereum.eth.v1alpha1.BeaconBlockGloas + (*BeaconBlockBodyGloas)(nil), // 9: ethereum.eth.v1alpha1.BeaconBlockBodyGloas + (*SignedBeaconBlockGloas)(nil), // 10: ethereum.eth.v1alpha1.SignedBeaconBlockGloas + (*BeaconStateGloas)(nil), // 11: ethereum.eth.v1alpha1.BeaconStateGloas + (*PTCs)(nil), // 12: ethereum.eth.v1alpha1.PTCs + (*BeaconBlockContentsGloas)(nil), // 13: ethereum.eth.v1alpha1.BeaconBlockContentsGloas + (*SignedExecutionPayloadEnvelopeContents)(nil), // 14: ethereum.eth.v1alpha1.SignedExecutionPayloadEnvelopeContents + (*BuilderPendingPayment)(nil), // 15: ethereum.eth.v1alpha1.BuilderPendingPayment + (*BuilderPendingWithdrawal)(nil), // 16: ethereum.eth.v1alpha1.BuilderPendingWithdrawal + (*DataColumnSidecarGloas)(nil), // 17: ethereum.eth.v1alpha1.DataColumnSidecarGloas + (*ExecutionPayloadEnvelope)(nil), // 18: ethereum.eth.v1alpha1.ExecutionPayloadEnvelope + (*SignedExecutionPayloadEnvelope)(nil), // 19: ethereum.eth.v1alpha1.SignedExecutionPayloadEnvelope + (*BlindedExecutionPayloadEnvelope)(nil), // 20: ethereum.eth.v1alpha1.BlindedExecutionPayloadEnvelope + (*SignedBlindedExecutionPayloadEnvelope)(nil), // 21: ethereum.eth.v1alpha1.SignedBlindedExecutionPayloadEnvelope + (*WireBlindedExecutionPayloadEnvelope)(nil), // 22: ethereum.eth.v1alpha1.WireBlindedExecutionPayloadEnvelope + (*SignedWireBlindedExecutionPayloadEnvelope)(nil), // 23: ethereum.eth.v1alpha1.SignedWireBlindedExecutionPayloadEnvelope + (*Builder)(nil), // 24: ethereum.eth.v1alpha1.Builder + (*Eth1Data)(nil), // 25: ethereum.eth.v1alpha1.Eth1Data + (*ProposerSlashing)(nil), // 26: ethereum.eth.v1alpha1.ProposerSlashing + (*AttesterSlashingElectra)(nil), // 27: ethereum.eth.v1alpha1.AttesterSlashingElectra + (*AttestationElectra)(nil), // 28: ethereum.eth.v1alpha1.AttestationElectra + (*Deposit)(nil), // 29: ethereum.eth.v1alpha1.Deposit + (*SignedVoluntaryExit)(nil), // 30: ethereum.eth.v1alpha1.SignedVoluntaryExit + (*SyncAggregate)(nil), // 31: ethereum.eth.v1alpha1.SyncAggregate + (*SignedBLSToExecutionChange)(nil), // 32: ethereum.eth.v1alpha1.SignedBLSToExecutionChange + (*v1.ExecutionRequests)(nil), // 33: ethereum.engine.v1.ExecutionRequests + (*Fork)(nil), // 34: ethereum.eth.v1alpha1.Fork + (*BeaconBlockHeader)(nil), // 35: ethereum.eth.v1alpha1.BeaconBlockHeader + (*Validator)(nil), // 36: ethereum.eth.v1alpha1.Validator + (*Checkpoint)(nil), // 37: ethereum.eth.v1alpha1.Checkpoint + (*SyncCommittee)(nil), // 38: ethereum.eth.v1alpha1.SyncCommittee + (*HistoricalSummary)(nil), // 39: ethereum.eth.v1alpha1.HistoricalSummary + (*PendingDeposit)(nil), // 40: ethereum.eth.v1alpha1.PendingDeposit + (*PendingPartialWithdrawal)(nil), // 41: ethereum.eth.v1alpha1.PendingPartialWithdrawal + (*PendingConsolidation)(nil), // 42: ethereum.eth.v1alpha1.PendingConsolidation + (*v1.Withdrawal)(nil), // 43: ethereum.engine.v1.Withdrawal + (*v1.ExecutionPayloadGloas)(nil), // 44: ethereum.engine.v1.ExecutionPayloadGloas } var file_proto_prysm_v1alpha1_gloas_proto_depIdxs = []int32{ 0, // 0: ethereum.eth.v1alpha1.SignedExecutionPayloadBid.message:type_name -> ethereum.eth.v1alpha1.ExecutionPayloadBid @@ -2683,51 +2927,54 @@ var file_proto_prysm_v1alpha1_gloas_proto_depIdxs = []int32{ 5, // 3: ethereum.eth.v1alpha1.PayloadAttestation.data:type_name -> ethereum.eth.v1alpha1.PayloadAttestationData 5, // 4: ethereum.eth.v1alpha1.PayloadAttestationMessage.data:type_name -> ethereum.eth.v1alpha1.PayloadAttestationData 9, // 5: ethereum.eth.v1alpha1.BeaconBlockGloas.body:type_name -> ethereum.eth.v1alpha1.BeaconBlockBodyGloas - 22, // 6: ethereum.eth.v1alpha1.BeaconBlockBodyGloas.eth1_data:type_name -> ethereum.eth.v1alpha1.Eth1Data - 23, // 7: ethereum.eth.v1alpha1.BeaconBlockBodyGloas.proposer_slashings:type_name -> ethereum.eth.v1alpha1.ProposerSlashing - 24, // 8: ethereum.eth.v1alpha1.BeaconBlockBodyGloas.attester_slashings:type_name -> ethereum.eth.v1alpha1.AttesterSlashingElectra - 25, // 9: ethereum.eth.v1alpha1.BeaconBlockBodyGloas.attestations:type_name -> ethereum.eth.v1alpha1.AttestationElectra - 26, // 10: ethereum.eth.v1alpha1.BeaconBlockBodyGloas.deposits:type_name -> ethereum.eth.v1alpha1.Deposit - 27, // 11: ethereum.eth.v1alpha1.BeaconBlockBodyGloas.voluntary_exits:type_name -> ethereum.eth.v1alpha1.SignedVoluntaryExit - 28, // 12: ethereum.eth.v1alpha1.BeaconBlockBodyGloas.sync_aggregate:type_name -> ethereum.eth.v1alpha1.SyncAggregate - 29, // 13: ethereum.eth.v1alpha1.BeaconBlockBodyGloas.bls_to_execution_changes:type_name -> ethereum.eth.v1alpha1.SignedBLSToExecutionChange + 25, // 6: ethereum.eth.v1alpha1.BeaconBlockBodyGloas.eth1_data:type_name -> ethereum.eth.v1alpha1.Eth1Data + 26, // 7: ethereum.eth.v1alpha1.BeaconBlockBodyGloas.proposer_slashings:type_name -> ethereum.eth.v1alpha1.ProposerSlashing + 27, // 8: ethereum.eth.v1alpha1.BeaconBlockBodyGloas.attester_slashings:type_name -> ethereum.eth.v1alpha1.AttesterSlashingElectra + 28, // 9: ethereum.eth.v1alpha1.BeaconBlockBodyGloas.attestations:type_name -> ethereum.eth.v1alpha1.AttestationElectra + 29, // 10: ethereum.eth.v1alpha1.BeaconBlockBodyGloas.deposits:type_name -> ethereum.eth.v1alpha1.Deposit + 30, // 11: ethereum.eth.v1alpha1.BeaconBlockBodyGloas.voluntary_exits:type_name -> ethereum.eth.v1alpha1.SignedVoluntaryExit + 31, // 12: ethereum.eth.v1alpha1.BeaconBlockBodyGloas.sync_aggregate:type_name -> ethereum.eth.v1alpha1.SyncAggregate + 32, // 13: ethereum.eth.v1alpha1.BeaconBlockBodyGloas.bls_to_execution_changes:type_name -> ethereum.eth.v1alpha1.SignedBLSToExecutionChange 1, // 14: ethereum.eth.v1alpha1.BeaconBlockBodyGloas.signed_execution_payload_bid:type_name -> ethereum.eth.v1alpha1.SignedExecutionPayloadBid 6, // 15: ethereum.eth.v1alpha1.BeaconBlockBodyGloas.payload_attestations:type_name -> ethereum.eth.v1alpha1.PayloadAttestation - 30, // 16: ethereum.eth.v1alpha1.BeaconBlockBodyGloas.parent_execution_requests:type_name -> ethereum.engine.v1.ExecutionRequests + 33, // 16: ethereum.eth.v1alpha1.BeaconBlockBodyGloas.parent_execution_requests:type_name -> ethereum.engine.v1.ExecutionRequests 8, // 17: ethereum.eth.v1alpha1.SignedBeaconBlockGloas.block:type_name -> ethereum.eth.v1alpha1.BeaconBlockGloas - 31, // 18: ethereum.eth.v1alpha1.BeaconStateGloas.fork:type_name -> ethereum.eth.v1alpha1.Fork - 32, // 19: ethereum.eth.v1alpha1.BeaconStateGloas.latest_block_header:type_name -> ethereum.eth.v1alpha1.BeaconBlockHeader - 22, // 20: ethereum.eth.v1alpha1.BeaconStateGloas.eth1_data:type_name -> ethereum.eth.v1alpha1.Eth1Data - 22, // 21: ethereum.eth.v1alpha1.BeaconStateGloas.eth1_data_votes:type_name -> ethereum.eth.v1alpha1.Eth1Data - 33, // 22: ethereum.eth.v1alpha1.BeaconStateGloas.validators:type_name -> ethereum.eth.v1alpha1.Validator - 34, // 23: ethereum.eth.v1alpha1.BeaconStateGloas.previous_justified_checkpoint:type_name -> ethereum.eth.v1alpha1.Checkpoint - 34, // 24: ethereum.eth.v1alpha1.BeaconStateGloas.current_justified_checkpoint:type_name -> ethereum.eth.v1alpha1.Checkpoint - 34, // 25: ethereum.eth.v1alpha1.BeaconStateGloas.finalized_checkpoint:type_name -> ethereum.eth.v1alpha1.Checkpoint - 35, // 26: ethereum.eth.v1alpha1.BeaconStateGloas.current_sync_committee:type_name -> ethereum.eth.v1alpha1.SyncCommittee - 35, // 27: ethereum.eth.v1alpha1.BeaconStateGloas.next_sync_committee:type_name -> ethereum.eth.v1alpha1.SyncCommittee - 36, // 28: ethereum.eth.v1alpha1.BeaconStateGloas.historical_summaries:type_name -> ethereum.eth.v1alpha1.HistoricalSummary - 37, // 29: ethereum.eth.v1alpha1.BeaconStateGloas.pending_deposits:type_name -> ethereum.eth.v1alpha1.PendingDeposit - 38, // 30: ethereum.eth.v1alpha1.BeaconStateGloas.pending_partial_withdrawals:type_name -> ethereum.eth.v1alpha1.PendingPartialWithdrawal - 39, // 31: ethereum.eth.v1alpha1.BeaconStateGloas.pending_consolidations:type_name -> ethereum.eth.v1alpha1.PendingConsolidation - 21, // 32: ethereum.eth.v1alpha1.BeaconStateGloas.builders:type_name -> ethereum.eth.v1alpha1.Builder - 14, // 33: ethereum.eth.v1alpha1.BeaconStateGloas.builder_pending_payments:type_name -> ethereum.eth.v1alpha1.BuilderPendingPayment - 15, // 34: ethereum.eth.v1alpha1.BeaconStateGloas.builder_pending_withdrawals:type_name -> ethereum.eth.v1alpha1.BuilderPendingWithdrawal + 34, // 18: ethereum.eth.v1alpha1.BeaconStateGloas.fork:type_name -> ethereum.eth.v1alpha1.Fork + 35, // 19: ethereum.eth.v1alpha1.BeaconStateGloas.latest_block_header:type_name -> ethereum.eth.v1alpha1.BeaconBlockHeader + 25, // 20: ethereum.eth.v1alpha1.BeaconStateGloas.eth1_data:type_name -> ethereum.eth.v1alpha1.Eth1Data + 25, // 21: ethereum.eth.v1alpha1.BeaconStateGloas.eth1_data_votes:type_name -> ethereum.eth.v1alpha1.Eth1Data + 36, // 22: ethereum.eth.v1alpha1.BeaconStateGloas.validators:type_name -> ethereum.eth.v1alpha1.Validator + 37, // 23: ethereum.eth.v1alpha1.BeaconStateGloas.previous_justified_checkpoint:type_name -> ethereum.eth.v1alpha1.Checkpoint + 37, // 24: ethereum.eth.v1alpha1.BeaconStateGloas.current_justified_checkpoint:type_name -> ethereum.eth.v1alpha1.Checkpoint + 37, // 25: ethereum.eth.v1alpha1.BeaconStateGloas.finalized_checkpoint:type_name -> ethereum.eth.v1alpha1.Checkpoint + 38, // 26: ethereum.eth.v1alpha1.BeaconStateGloas.current_sync_committee:type_name -> ethereum.eth.v1alpha1.SyncCommittee + 38, // 27: ethereum.eth.v1alpha1.BeaconStateGloas.next_sync_committee:type_name -> ethereum.eth.v1alpha1.SyncCommittee + 39, // 28: ethereum.eth.v1alpha1.BeaconStateGloas.historical_summaries:type_name -> ethereum.eth.v1alpha1.HistoricalSummary + 40, // 29: ethereum.eth.v1alpha1.BeaconStateGloas.pending_deposits:type_name -> ethereum.eth.v1alpha1.PendingDeposit + 41, // 30: ethereum.eth.v1alpha1.BeaconStateGloas.pending_partial_withdrawals:type_name -> ethereum.eth.v1alpha1.PendingPartialWithdrawal + 42, // 31: ethereum.eth.v1alpha1.BeaconStateGloas.pending_consolidations:type_name -> ethereum.eth.v1alpha1.PendingConsolidation + 24, // 32: ethereum.eth.v1alpha1.BeaconStateGloas.builders:type_name -> ethereum.eth.v1alpha1.Builder + 15, // 33: ethereum.eth.v1alpha1.BeaconStateGloas.builder_pending_payments:type_name -> ethereum.eth.v1alpha1.BuilderPendingPayment + 16, // 34: ethereum.eth.v1alpha1.BeaconStateGloas.builder_pending_withdrawals:type_name -> ethereum.eth.v1alpha1.BuilderPendingWithdrawal 0, // 35: ethereum.eth.v1alpha1.BeaconStateGloas.latest_execution_payload_bid:type_name -> ethereum.eth.v1alpha1.ExecutionPayloadBid - 40, // 36: ethereum.eth.v1alpha1.BeaconStateGloas.payload_expected_withdrawals:type_name -> ethereum.engine.v1.Withdrawal + 43, // 36: ethereum.eth.v1alpha1.BeaconStateGloas.payload_expected_withdrawals:type_name -> ethereum.engine.v1.Withdrawal 12, // 37: ethereum.eth.v1alpha1.BeaconStateGloas.ptc_window:type_name -> ethereum.eth.v1alpha1.PTCs 8, // 38: ethereum.eth.v1alpha1.BeaconBlockContentsGloas.block:type_name -> ethereum.eth.v1alpha1.BeaconBlockGloas - 17, // 39: ethereum.eth.v1alpha1.BeaconBlockContentsGloas.execution_payload_envelope:type_name -> ethereum.eth.v1alpha1.ExecutionPayloadEnvelope - 15, // 40: ethereum.eth.v1alpha1.BuilderPendingPayment.withdrawal:type_name -> ethereum.eth.v1alpha1.BuilderPendingWithdrawal - 41, // 41: ethereum.eth.v1alpha1.ExecutionPayloadEnvelope.payload:type_name -> ethereum.engine.v1.ExecutionPayloadGloas - 30, // 42: ethereum.eth.v1alpha1.ExecutionPayloadEnvelope.execution_requests:type_name -> ethereum.engine.v1.ExecutionRequests - 17, // 43: ethereum.eth.v1alpha1.SignedExecutionPayloadEnvelope.message:type_name -> ethereum.eth.v1alpha1.ExecutionPayloadEnvelope - 30, // 44: ethereum.eth.v1alpha1.BlindedExecutionPayloadEnvelope.execution_requests:type_name -> ethereum.engine.v1.ExecutionRequests - 19, // 45: ethereum.eth.v1alpha1.SignedBlindedExecutionPayloadEnvelope.message:type_name -> ethereum.eth.v1alpha1.BlindedExecutionPayloadEnvelope - 46, // [46:46] is the sub-list for method output_type - 46, // [46:46] is the sub-list for method input_type - 46, // [46:46] is the sub-list for extension type_name - 46, // [46:46] is the sub-list for extension extendee - 0, // [0:46] is the sub-list for field type_name + 18, // 39: ethereum.eth.v1alpha1.BeaconBlockContentsGloas.execution_payload_envelope:type_name -> ethereum.eth.v1alpha1.ExecutionPayloadEnvelope + 19, // 40: ethereum.eth.v1alpha1.SignedExecutionPayloadEnvelopeContents.signed_execution_payload_envelope:type_name -> ethereum.eth.v1alpha1.SignedExecutionPayloadEnvelope + 16, // 41: ethereum.eth.v1alpha1.BuilderPendingPayment.withdrawal:type_name -> ethereum.eth.v1alpha1.BuilderPendingWithdrawal + 44, // 42: ethereum.eth.v1alpha1.ExecutionPayloadEnvelope.payload:type_name -> ethereum.engine.v1.ExecutionPayloadGloas + 33, // 43: ethereum.eth.v1alpha1.ExecutionPayloadEnvelope.execution_requests:type_name -> ethereum.engine.v1.ExecutionRequests + 18, // 44: ethereum.eth.v1alpha1.SignedExecutionPayloadEnvelope.message:type_name -> ethereum.eth.v1alpha1.ExecutionPayloadEnvelope + 33, // 45: ethereum.eth.v1alpha1.BlindedExecutionPayloadEnvelope.execution_requests:type_name -> ethereum.engine.v1.ExecutionRequests + 20, // 46: ethereum.eth.v1alpha1.SignedBlindedExecutionPayloadEnvelope.message:type_name -> ethereum.eth.v1alpha1.BlindedExecutionPayloadEnvelope + 33, // 47: ethereum.eth.v1alpha1.WireBlindedExecutionPayloadEnvelope.execution_requests:type_name -> ethereum.engine.v1.ExecutionRequests + 22, // 48: ethereum.eth.v1alpha1.SignedWireBlindedExecutionPayloadEnvelope.message:type_name -> ethereum.eth.v1alpha1.WireBlindedExecutionPayloadEnvelope + 49, // [49:49] is the sub-list for method output_type + 49, // [49:49] is the sub-list for method input_type + 49, // [49:49] is the sub-list for extension type_name + 49, // [49:49] is the sub-list for extension extendee + 0, // [0:49] is the sub-list for field type_name } func init() { file_proto_prysm_v1alpha1_gloas_proto_init() } @@ -2745,7 +2992,7 @@ func file_proto_prysm_v1alpha1_gloas_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proto_prysm_v1alpha1_gloas_proto_rawDesc, NumEnums: 0, - NumMessages: 22, + NumMessages: 25, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/prysm/v1alpha1/gloas.proto b/proto/prysm/v1alpha1/gloas.proto index ae46644b59c8..00715d3c4a48 100644 --- a/proto/prysm/v1alpha1/gloas.proto +++ b/proto/prysm/v1alpha1/gloas.proto @@ -411,7 +411,7 @@ message BeaconBlockContentsGloas { ExecutionPayloadEnvelope execution_payload_envelope = 2; repeated bytes kzg_proofs = 3 [ (ethereum.eth.ext.ssz_size) = "?,48", - (ethereum.eth.ext.ssz_max) = "max_blob_commitments.size" + (ethereum.eth.ext.ssz_max) = "max_cell_proofs_length.size" ]; repeated bytes blobs = 4 [ (ethereum.eth.ext.ssz_size) = "?,blob.size", @@ -419,6 +419,22 @@ message BeaconBlockContentsGloas { ]; } +// SignedExecutionPayloadEnvelopeContents bundles a signed execution payload +// envelope with the raw blobs and KZG proofs needed by a beacon node that has +// not cached them locally. Used by the stateless publish path of +// POST /eth/v1/beacon/execution_payload_envelopes. +message SignedExecutionPayloadEnvelopeContents { + SignedExecutionPayloadEnvelope signed_execution_payload_envelope = 1; + repeated bytes kzg_proofs = 2 [ + (ethereum.eth.ext.ssz_size) = "?,48", + (ethereum.eth.ext.ssz_max) = "max_cell_proofs_length.size" + ]; + repeated bytes blobs = 3 [ + (ethereum.eth.ext.ssz_size) = "?,blob.size", + (ethereum.eth.ext.ssz_max) = "max_blob_commitments.size" + ]; +} + // BuilderPendingPayment represents a pending payment to a builder. // // Spec: @@ -540,6 +556,23 @@ message SignedBlindedExecutionPayloadEnvelope { bytes signature = 2 [ (ethereum.eth.ext.ssz_size) = "96" ]; } +// WireBlindedExecutionPayloadEnvelope is the beacon-APIs #580 wire form: payload replaced by +// payload_root so HTR matches the full envelope. Distinct from the storage-form BlindedExecutionPayloadEnvelope above. +message WireBlindedExecutionPayloadEnvelope { + bytes payload_root = 1 [ (ethereum.eth.ext.ssz_size) = "32" ]; + ethereum.engine.v1.ExecutionRequests execution_requests = 2; + uint64 builder_index = 3 [ (ethereum.eth.ext.cast_type) = + "github.com/OffchainLabs/prysm/v7/" + "consensus-types/primitives.BuilderIndex" ]; + bytes beacon_block_root = 4 [ (ethereum.eth.ext.ssz_size) = "32" ]; + bytes parent_beacon_block_root = 5 [ (ethereum.eth.ext.ssz_size) = "32" ]; +} + +message SignedWireBlindedExecutionPayloadEnvelope { + WireBlindedExecutionPayloadEnvelope message = 1; + bytes signature = 2 [ (ethereum.eth.ext.ssz_size) = "96" ]; +} + // Builder represents a builder in the Gloas fork. // // Spec: diff --git a/proto/prysm/v1alpha1/gloas.ssz.go b/proto/prysm/v1alpha1/gloas.ssz.go index d9f7101b73d6..b9299af5574c 100644 --- a/proto/prysm/v1alpha1/gloas.ssz.go +++ b/proto/prysm/v1alpha1/gloas.ssz.go @@ -3328,8 +3328,8 @@ func (b *BeaconBlockContentsGloas) MarshalSSZTo(buf []byte) (dst []byte, err err } // Field (2) 'KzgProofs' - if size := len(b.KzgProofs); size > 4096 { - err = ssz.ErrListTooBigFn("--.KzgProofs", size, 4096) + if size := len(b.KzgProofs); size > 33554432 { + err = ssz.ErrListTooBigFn("--.KzgProofs", size, 33554432) return } for ii := 0; ii < len(b.KzgProofs); ii++ { @@ -3416,7 +3416,7 @@ func (b *BeaconBlockContentsGloas) UnmarshalSSZ(buf []byte) error { // Field (2) 'KzgProofs' { buf = tail[o2:o3] - num, err := ssz.DivideInt2(len(buf), 48, 4096) + num, err := ssz.DivideInt2(len(buf), 48, 33554432) if err != nil { return err } @@ -3493,8 +3493,8 @@ func (b *BeaconBlockContentsGloas) HashTreeRootWith(hh *ssz.Hasher) (err error) // Field (2) 'KzgProofs' { - if size := len(b.KzgProofs); size > 4096 { - err = ssz.ErrListTooBigFn("--.KzgProofs", size, 4096) + if size := len(b.KzgProofs); size > 33554432 { + err = ssz.ErrListTooBigFn("--.KzgProofs", size, 33554432) return } subIndx := hh.Index() @@ -3507,7 +3507,7 @@ func (b *BeaconBlockContentsGloas) HashTreeRootWith(hh *ssz.Hasher) (err error) } numItems := uint64(len(b.KzgProofs)) - hh.MerkleizeWithMixin(subIndx, numItems, 4096) + hh.MerkleizeWithMixin(subIndx, numItems, 33554432) } // Field (3) 'Blobs' @@ -3533,6 +3533,215 @@ func (b *BeaconBlockContentsGloas) HashTreeRootWith(hh *ssz.Hasher) (err error) return } +// MarshalSSZ ssz marshals the SignedExecutionPayloadEnvelopeContents object +func (s *SignedExecutionPayloadEnvelopeContents) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(s) +} + +// MarshalSSZTo ssz marshals the SignedExecutionPayloadEnvelopeContents object to a target array +func (s *SignedExecutionPayloadEnvelopeContents) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(12) + + // Offset (0) 'SignedExecutionPayloadEnvelope' + dst = ssz.WriteOffset(dst, offset) + if s.SignedExecutionPayloadEnvelope == nil { + s.SignedExecutionPayloadEnvelope = new(SignedExecutionPayloadEnvelope) + } + offset += s.SignedExecutionPayloadEnvelope.SizeSSZ() + + // Offset (1) 'KzgProofs' + dst = ssz.WriteOffset(dst, offset) + offset += len(s.KzgProofs) * 48 + + // Offset (2) 'Blobs' + dst = ssz.WriteOffset(dst, offset) + offset += len(s.Blobs) * 131072 + + // Field (0) 'SignedExecutionPayloadEnvelope' + if dst, err = s.SignedExecutionPayloadEnvelope.MarshalSSZTo(dst); err != nil { + return + } + + // Field (1) 'KzgProofs' + if size := len(s.KzgProofs); size > 33554432 { + err = ssz.ErrListTooBigFn("--.KzgProofs", size, 33554432) + return + } + for ii := 0; ii < len(s.KzgProofs); ii++ { + if size := len(s.KzgProofs[ii]); size != 48 { + err = ssz.ErrBytesLengthFn("--.KzgProofs[ii]", size, 48) + return + } + dst = append(dst, s.KzgProofs[ii]...) + } + + // Field (2) 'Blobs' + if size := len(s.Blobs); size > 4096 { + err = ssz.ErrListTooBigFn("--.Blobs", size, 4096) + return + } + for ii := 0; ii < len(s.Blobs); ii++ { + if size := len(s.Blobs[ii]); size != 131072 { + err = ssz.ErrBytesLengthFn("--.Blobs[ii]", size, 131072) + return + } + dst = append(dst, s.Blobs[ii]...) + } + + return +} + +// UnmarshalSSZ ssz unmarshals the SignedExecutionPayloadEnvelopeContents object +func (s *SignedExecutionPayloadEnvelopeContents) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 12 { + return ssz.ErrSize + } + + tail := buf + var o0, o1, o2 uint64 + + // Offset (0) 'SignedExecutionPayloadEnvelope' + if o0 = ssz.ReadOffset(buf[0:4]); o0 > size { + return ssz.ErrOffset + } + + if o0 != 12 { + return ssz.ErrInvalidVariableOffset + } + + // Offset (1) 'KzgProofs' + if o1 = ssz.ReadOffset(buf[4:8]); o1 > size || o0 > o1 { + return ssz.ErrOffset + } + + // Offset (2) 'Blobs' + if o2 = ssz.ReadOffset(buf[8:12]); o2 > size || o1 > o2 { + return ssz.ErrOffset + } + + // Field (0) 'SignedExecutionPayloadEnvelope' + { + buf = tail[o0:o1] + if s.SignedExecutionPayloadEnvelope == nil { + s.SignedExecutionPayloadEnvelope = new(SignedExecutionPayloadEnvelope) + } + if err = s.SignedExecutionPayloadEnvelope.UnmarshalSSZ(buf); err != nil { + return err + } + } + + // Field (1) 'KzgProofs' + { + buf = tail[o1:o2] + num, err := ssz.DivideInt2(len(buf), 48, 33554432) + if err != nil { + return err + } + s.KzgProofs = make([][]byte, num) + for ii := 0; ii < num; ii++ { + if cap(s.KzgProofs[ii]) == 0 { + s.KzgProofs[ii] = make([]byte, 0, len(buf[ii*48:(ii+1)*48])) + } + s.KzgProofs[ii] = append(s.KzgProofs[ii], buf[ii*48:(ii+1)*48]...) + } + } + + // Field (2) 'Blobs' + { + buf = tail[o2:] + num, err := ssz.DivideInt2(len(buf), 131072, 4096) + if err != nil { + return err + } + s.Blobs = make([][]byte, num) + for ii := 0; ii < num; ii++ { + if cap(s.Blobs[ii]) == 0 { + s.Blobs[ii] = make([]byte, 0, len(buf[ii*131072:(ii+1)*131072])) + } + s.Blobs[ii] = append(s.Blobs[ii], buf[ii*131072:(ii+1)*131072]...) + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the SignedExecutionPayloadEnvelopeContents object +func (s *SignedExecutionPayloadEnvelopeContents) SizeSSZ() (size int) { + size = 12 + + // Field (0) 'SignedExecutionPayloadEnvelope' + if s.SignedExecutionPayloadEnvelope == nil { + s.SignedExecutionPayloadEnvelope = new(SignedExecutionPayloadEnvelope) + } + size += s.SignedExecutionPayloadEnvelope.SizeSSZ() + + // Field (1) 'KzgProofs' + size += len(s.KzgProofs) * 48 + + // Field (2) 'Blobs' + size += len(s.Blobs) * 131072 + + return +} + +// HashTreeRoot ssz hashes the SignedExecutionPayloadEnvelopeContents object +func (s *SignedExecutionPayloadEnvelopeContents) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(s) +} + +// HashTreeRootWith ssz hashes the SignedExecutionPayloadEnvelopeContents object with a hasher +func (s *SignedExecutionPayloadEnvelopeContents) HashTreeRootWith(hh *ssz.Hasher) (err error) { + indx := hh.Index() + + // Field (0) 'SignedExecutionPayloadEnvelope' + if err = s.SignedExecutionPayloadEnvelope.HashTreeRootWith(hh); err != nil { + return + } + + // Field (1) 'KzgProofs' + { + if size := len(s.KzgProofs); size > 33554432 { + err = ssz.ErrListTooBigFn("--.KzgProofs", size, 33554432) + return + } + subIndx := hh.Index() + for _, i := range s.KzgProofs { + if len(i) != 48 { + err = ssz.ErrBytesLength + return + } + hh.PutBytes(i) + } + + numItems := uint64(len(s.KzgProofs)) + hh.MerkleizeWithMixin(subIndx, numItems, 33554432) + } + + // Field (2) 'Blobs' + { + if size := len(s.Blobs); size > 4096 { + err = ssz.ErrListTooBigFn("--.Blobs", size, 4096) + return + } + subIndx := hh.Index() + for _, i := range s.Blobs { + if len(i) != 131072 { + err = ssz.ErrBytesLength + return + } + hh.PutBytes(i) + } + + numItems := uint64(len(s.Blobs)) + hh.MerkleizeWithMixin(subIndx, numItems, 4096) + } + + hh.Merkleize(indx) + return +} + // MarshalSSZ ssz marshals the BuilderPendingPayment object func (b *BuilderPendingPayment) MarshalSSZ() ([]byte, error) { return ssz.MarshalSSZ(b) @@ -4475,6 +4684,273 @@ func (s *SignedBlindedExecutionPayloadEnvelope) HashTreeRootWith(hh *ssz.Hasher) return } +// MarshalSSZ ssz marshals the WireBlindedExecutionPayloadEnvelope object +func (w *WireBlindedExecutionPayloadEnvelope) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(w) +} + +// MarshalSSZTo ssz marshals the WireBlindedExecutionPayloadEnvelope object to a target array +func (w *WireBlindedExecutionPayloadEnvelope) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(108) + + // Field (0) 'PayloadRoot' + if size := len(w.PayloadRoot); size != 32 { + err = ssz.ErrBytesLengthFn("--.PayloadRoot", size, 32) + return + } + dst = append(dst, w.PayloadRoot...) + + // Offset (1) 'ExecutionRequests' + dst = ssz.WriteOffset(dst, offset) + if w.ExecutionRequests == nil { + w.ExecutionRequests = new(v1.ExecutionRequests) + } + offset += w.ExecutionRequests.SizeSSZ() + + // Field (2) 'BuilderIndex' + dst = ssz.MarshalUint(dst, w.BuilderIndex) + + // Field (3) 'BeaconBlockRoot' + if size := len(w.BeaconBlockRoot); size != 32 { + err = ssz.ErrBytesLengthFn("--.BeaconBlockRoot", size, 32) + return + } + dst = append(dst, w.BeaconBlockRoot...) + + // Field (4) 'ParentBeaconBlockRoot' + if size := len(w.ParentBeaconBlockRoot); size != 32 { + err = ssz.ErrBytesLengthFn("--.ParentBeaconBlockRoot", size, 32) + return + } + dst = append(dst, w.ParentBeaconBlockRoot...) + + // Field (1) 'ExecutionRequests' + if dst, err = w.ExecutionRequests.MarshalSSZTo(dst); err != nil { + return + } + + return +} + +// UnmarshalSSZ ssz unmarshals the WireBlindedExecutionPayloadEnvelope object +func (w *WireBlindedExecutionPayloadEnvelope) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 108 { + return ssz.ErrSize + } + + tail := buf + var o1 uint64 + + // Field (0) 'PayloadRoot' + if cap(w.PayloadRoot) == 0 { + w.PayloadRoot = make([]byte, 0, len(buf[0:32])) + } + w.PayloadRoot = append(w.PayloadRoot, buf[0:32]...) + + // Offset (1) 'ExecutionRequests' + if o1 = ssz.ReadOffset(buf[32:36]); o1 > size { + return ssz.ErrOffset + } + + if o1 != 108 { + return ssz.ErrInvalidVariableOffset + } + + // Field (2) 'BuilderIndex' + w.BuilderIndex = ssz.UnmarshallUint[github_com_OffchainLabs_prysm_v7_consensus_types_primitives.BuilderIndex](buf[36:44]) + + // Field (3) 'BeaconBlockRoot' + if cap(w.BeaconBlockRoot) == 0 { + w.BeaconBlockRoot = make([]byte, 0, len(buf[44:76])) + } + w.BeaconBlockRoot = append(w.BeaconBlockRoot, buf[44:76]...) + + // Field (4) 'ParentBeaconBlockRoot' + if cap(w.ParentBeaconBlockRoot) == 0 { + w.ParentBeaconBlockRoot = make([]byte, 0, len(buf[76:108])) + } + w.ParentBeaconBlockRoot = append(w.ParentBeaconBlockRoot, buf[76:108]...) + + // Field (1) 'ExecutionRequests' + { + buf = tail[o1:] + if w.ExecutionRequests == nil { + w.ExecutionRequests = new(v1.ExecutionRequests) + } + if err = w.ExecutionRequests.UnmarshalSSZ(buf); err != nil { + return err + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the WireBlindedExecutionPayloadEnvelope object +func (w *WireBlindedExecutionPayloadEnvelope) SizeSSZ() (size int) { + size = 108 + + // Field (1) 'ExecutionRequests' + if w.ExecutionRequests == nil { + w.ExecutionRequests = new(v1.ExecutionRequests) + } + size += w.ExecutionRequests.SizeSSZ() + + return +} + +// HashTreeRoot ssz hashes the WireBlindedExecutionPayloadEnvelope object +func (w *WireBlindedExecutionPayloadEnvelope) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(w) +} + +// HashTreeRootWith ssz hashes the WireBlindedExecutionPayloadEnvelope object with a hasher +func (w *WireBlindedExecutionPayloadEnvelope) HashTreeRootWith(hh *ssz.Hasher) (err error) { + indx := hh.Index() + + // Field (0) 'PayloadRoot' + if size := len(w.PayloadRoot); size != 32 { + err = ssz.ErrBytesLengthFn("--.PayloadRoot", size, 32) + return + } + hh.PutBytes(w.PayloadRoot) + + // Field (1) 'ExecutionRequests' + if err = w.ExecutionRequests.HashTreeRootWith(hh); err != nil { + return + } + + // Field (2) 'BuilderIndex' + ssz.PutUint(hh, w.BuilderIndex) + + // Field (3) 'BeaconBlockRoot' + if size := len(w.BeaconBlockRoot); size != 32 { + err = ssz.ErrBytesLengthFn("--.BeaconBlockRoot", size, 32) + return + } + hh.PutBytes(w.BeaconBlockRoot) + + // Field (4) 'ParentBeaconBlockRoot' + if size := len(w.ParentBeaconBlockRoot); size != 32 { + err = ssz.ErrBytesLengthFn("--.ParentBeaconBlockRoot", size, 32) + return + } + hh.PutBytes(w.ParentBeaconBlockRoot) + + hh.Merkleize(indx) + return +} + +// MarshalSSZ ssz marshals the SignedWireBlindedExecutionPayloadEnvelope object +func (s *SignedWireBlindedExecutionPayloadEnvelope) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(s) +} + +// MarshalSSZTo ssz marshals the SignedWireBlindedExecutionPayloadEnvelope object to a target array +func (s *SignedWireBlindedExecutionPayloadEnvelope) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(100) + + // Offset (0) 'Message' + dst = ssz.WriteOffset(dst, offset) + if s.Message == nil { + s.Message = new(WireBlindedExecutionPayloadEnvelope) + } + offset += s.Message.SizeSSZ() + + // Field (1) 'Signature' + if size := len(s.Signature); size != 96 { + err = ssz.ErrBytesLengthFn("--.Signature", size, 96) + return + } + dst = append(dst, s.Signature...) + + // Field (0) 'Message' + if dst, err = s.Message.MarshalSSZTo(dst); err != nil { + return + } + + return +} + +// UnmarshalSSZ ssz unmarshals the SignedWireBlindedExecutionPayloadEnvelope object +func (s *SignedWireBlindedExecutionPayloadEnvelope) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 100 { + return ssz.ErrSize + } + + tail := buf + var o0 uint64 + + // Offset (0) 'Message' + if o0 = ssz.ReadOffset(buf[0:4]); o0 > size { + return ssz.ErrOffset + } + + if o0 != 100 { + return ssz.ErrInvalidVariableOffset + } + + // Field (1) 'Signature' + if cap(s.Signature) == 0 { + s.Signature = make([]byte, 0, len(buf[4:100])) + } + s.Signature = append(s.Signature, buf[4:100]...) + + // Field (0) 'Message' + { + buf = tail[o0:] + if s.Message == nil { + s.Message = new(WireBlindedExecutionPayloadEnvelope) + } + if err = s.Message.UnmarshalSSZ(buf); err != nil { + return err + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the SignedWireBlindedExecutionPayloadEnvelope object +func (s *SignedWireBlindedExecutionPayloadEnvelope) SizeSSZ() (size int) { + size = 100 + + // Field (0) 'Message' + if s.Message == nil { + s.Message = new(WireBlindedExecutionPayloadEnvelope) + } + size += s.Message.SizeSSZ() + + return +} + +// HashTreeRoot ssz hashes the SignedWireBlindedExecutionPayloadEnvelope object +func (s *SignedWireBlindedExecutionPayloadEnvelope) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(s) +} + +// HashTreeRootWith ssz hashes the SignedWireBlindedExecutionPayloadEnvelope object with a hasher +func (s *SignedWireBlindedExecutionPayloadEnvelope) HashTreeRootWith(hh *ssz.Hasher) (err error) { + indx := hh.Index() + + // Field (0) 'Message' + if err = s.Message.HashTreeRootWith(hh); err != nil { + return + } + + // Field (1) 'Signature' + if size := len(s.Signature); size != 96 { + err = ssz.ErrBytesLengthFn("--.Signature", size, 96) + return + } + hh.PutBytes(s.Signature) + + hh.Merkleize(indx) + return +} + // MarshalSSZ ssz marshals the Builder object func (b *Builder) MarshalSSZ() ([]byte, error) { return ssz.MarshalSSZ(b) diff --git a/testing/validator-mock/validator_client_mock.go b/testing/validator-mock/validator_client_mock.go index e9900d9fcc4f..be4b93f5c3ff 100644 --- a/testing/validator-mock/validator_client_mock.go +++ b/testing/validator-mock/validator_client_mock.go @@ -209,18 +209,19 @@ func (mr *MockValidatorClientMockRecorder) FeeRecipientByPubKey(ctx, in any) *go } // GetExecutionPayloadEnvelope mocks base method. -func (m *MockValidatorClient) GetExecutionPayloadEnvelope(ctx context.Context, slot primitives.Slot) (*eth.ExecutionPayloadEnvelope, error) { +func (m *MockValidatorClient) GetExecutionPayloadEnvelope(ctx context.Context, slot primitives.Slot, beaconBlockRoot [32]byte) (*eth.ExecutionPayloadEnvelope, *eth.WireBlindedExecutionPayloadEnvelope, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetExecutionPayloadEnvelope", ctx, slot) + ret := m.ctrl.Call(m, "GetExecutionPayloadEnvelope", ctx, slot, beaconBlockRoot) ret0, _ := ret[0].(*eth.ExecutionPayloadEnvelope) - ret1, _ := ret[1].(error) - return ret0, ret1 + ret1, _ := ret[1].(*eth.WireBlindedExecutionPayloadEnvelope) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 } // GetExecutionPayloadEnvelope indicates an expected call of GetExecutionPayloadEnvelope. -func (mr *MockValidatorClientMockRecorder) GetExecutionPayloadEnvelope(ctx, slot any) *gomock.Call { +func (mr *MockValidatorClientMockRecorder) GetExecutionPayloadEnvelope(ctx, slot, beaconBlockRoot any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetExecutionPayloadEnvelope", reflect.TypeOf((*MockValidatorClient)(nil).GetExecutionPayloadEnvelope), ctx, slot) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetExecutionPayloadEnvelope", reflect.TypeOf((*MockValidatorClient)(nil).GetExecutionPayloadEnvelope), ctx, slot, beaconBlockRoot) } // Host mocks base method. @@ -387,6 +388,21 @@ func (mr *MockValidatorClientMockRecorder) PublishExecutionPayloadEnvelope(ctx, return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PublishExecutionPayloadEnvelope", reflect.TypeOf((*MockValidatorClient)(nil).PublishExecutionPayloadEnvelope), ctx, in) } +// PublishBlindedExecutionPayloadEnvelope mocks base method. +func (m *MockValidatorClient) PublishBlindedExecutionPayloadEnvelope(ctx context.Context, in *eth.SignedWireBlindedExecutionPayloadEnvelope) (*empty.Empty, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PublishBlindedExecutionPayloadEnvelope", ctx, in) + ret0, _ := ret[0].(*empty.Empty) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// PublishBlindedExecutionPayloadEnvelope indicates an expected call of PublishBlindedExecutionPayloadEnvelope. +func (mr *MockValidatorClientMockRecorder) PublishBlindedExecutionPayloadEnvelope(ctx, in any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PublishBlindedExecutionPayloadEnvelope", reflect.TypeOf((*MockValidatorClient)(nil).PublishBlindedExecutionPayloadEnvelope), ctx, in) +} + // SubmitSignedProposerPreferences mocks base method. func (m *MockValidatorClient) SubmitSignedProposerPreferences(ctx context.Context, in *eth.SubmitSignedProposerPreferencesRequest) (*empty.Empty, error) { m.ctrl.T.Helper() diff --git a/validator/client/beacon-api/beacon_api_validator_client.go b/validator/client/beacon-api/beacon_api_validator_client.go index 4fe8b62570f6..b86e0ed6e72d 100644 --- a/validator/client/beacon-api/beacon_api_validator_client.go +++ b/validator/client/beacon-api/beacon_api_validator_client.go @@ -387,13 +387,24 @@ func (c *beaconApiValidatorClient) AggregatedSyncSelections(ctx context.Context, func wrapInMetrics[Resp any](action string, f func() (Resp, error)) (Resp, error) { now := time.Now() resp, err := f() + recordMetrics(action, now, err) + return resp, err +} + +func wrapInMetrics2[R1, R2 any](action string, f func() (R1, R2, error)) (R1, R2, error) { + now := time.Now() + r1, r2, err := f() + recordMetrics(action, now, err) + return r1, r2, err +} + +func recordMetrics(action string, start time.Time, err error) { httpActionCount.WithLabelValues(action).Inc() if err == nil { - httpActionLatency.WithLabelValues(action).Observe(time.Since(now).Seconds()) + httpActionLatency.WithLabelValues(action).Observe(time.Since(start).Seconds()) } else { failedHTTPActionCount.WithLabelValues(action).Inc() } - return resp, err } func (c *beaconApiValidatorClient) Host() string { @@ -406,12 +417,12 @@ func (c *beaconApiValidatorClient) EnsureReady(ctx context.Context) bool { // Gloas Fork Methods -func (c *beaconApiValidatorClient) GetExecutionPayloadEnvelope(ctx context.Context, slot primitives.Slot) (*ethpb.ExecutionPayloadEnvelope, error) { +func (c *beaconApiValidatorClient) GetExecutionPayloadEnvelope(ctx context.Context, slot primitives.Slot, beaconBlockRoot [32]byte) (*ethpb.ExecutionPayloadEnvelope, *ethpb.WireBlindedExecutionPayloadEnvelope, error) { ctx, span := trace.StartSpan(ctx, "beacon-api.GetExecutionPayloadEnvelope") defer span.End() - return wrapInMetrics[*ethpb.ExecutionPayloadEnvelope]("GetExecutionPayloadEnvelope", func() (*ethpb.ExecutionPayloadEnvelope, error) { - return c.getExecutionPayloadEnvelope(ctx, slot) + return wrapInMetrics2("GetExecutionPayloadEnvelope", func() (*ethpb.ExecutionPayloadEnvelope, *ethpb.WireBlindedExecutionPayloadEnvelope, error) { + return c.getExecutionPayloadEnvelope(ctx, slot, beaconBlockRoot) }) } @@ -424,6 +435,15 @@ func (c *beaconApiValidatorClient) PublishExecutionPayloadEnvelope(ctx context.C }) } +func (c *beaconApiValidatorClient) PublishBlindedExecutionPayloadEnvelope(ctx context.Context, in *ethpb.SignedWireBlindedExecutionPayloadEnvelope) (*empty.Empty, error) { + ctx, span := trace.StartSpan(ctx, "beacon-api.PublishBlindedExecutionPayloadEnvelope") + defer span.End() + + return wrapInMetrics[*empty.Empty]("PublishBlindedExecutionPayloadEnvelope", func() (*empty.Empty, error) { + return c.publishBlindedExecutionPayloadEnvelope(ctx, in) + }) +} + func (c *beaconApiValidatorClient) PayloadAttestationData(ctx context.Context, slot primitives.Slot) (*ethpb.PayloadAttestationData, error) { ctx, span := trace.StartSpan(ctx, "beacon-api.PayloadAttestationData") defer span.End() diff --git a/validator/client/beacon-api/envelope_cache.go b/validator/client/beacon-api/envelope_cache.go index e5011920d305..4114058c7b24 100644 --- a/validator/client/beacon-api/envelope_cache.go +++ b/validator/client/beacon-api/envelope_cache.go @@ -20,7 +20,7 @@ type envelopeContents struct { // stateless block production path to carry the execution payload envelope and // its associated blob data from the /eth/v4/validator/blocks response to the // self-build envelope publisher, avoiding a redundant -// /eth/v1/validator/execution_payload_envelope fetch. +// /eth/v1/validator/execution_payload_envelopes fetch. type executionPayloadEnvelopeCache struct { mu sync.Mutex entries map[primitives.Slot]*envelopeContents diff --git a/validator/client/beacon-api/execution_payload_envelope.go b/validator/client/beacon-api/execution_payload_envelope.go index 6f3b39582099..d81d645a747e 100644 --- a/validator/client/beacon-api/execution_payload_envelope.go +++ b/validator/client/beacon-api/execution_payload_envelope.go @@ -5,73 +5,154 @@ import ( "context" "encoding/json" "fmt" + "net/http" + "strconv" + "strings" + "github.com/OffchainLabs/prysm/v7/api" "github.com/OffchainLabs/prysm/v7/api/server/structs" "github.com/OffchainLabs/prysm/v7/consensus-types/primitives" + "github.com/OffchainLabs/prysm/v7/network/httputil" ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1" + "github.com/OffchainLabs/prysm/v7/runtime/version" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/golang/protobuf/ptypes/empty" "github.com/pkg/errors" ) +// getExecutionPayloadEnvelope returns the envelope to sign for self-build. Stateless mode has the +// full envelope cached locally (from the v4 block fetch); stateful mode fetches the spec-wire +// blinded form from the BN, which exposes only the blinded envelope (beacon-APIs #580). Exactly one +// of the returned values is non-nil. func (c *beaconApiValidatorClient) getExecutionPayloadEnvelope( ctx context.Context, slot primitives.Slot, -) (*ethpb.ExecutionPayloadEnvelope, error) { - envelope, _, _ := c.envelopeCache.peek(slot) - if envelope != nil { - return envelope, nil + beaconBlockRoot [32]byte, +) (*ethpb.ExecutionPayloadEnvelope, *ethpb.WireBlindedExecutionPayloadEnvelope, error) { + if envelope, _, _ := c.envelopeCache.peek(slot); envelope != nil { + return envelope, nil, nil } - endpoint := fmt.Sprintf("/eth/v1/validator/execution_payload_envelope/%d", slot) - var resp structs.GetValidatorExecutionPayloadEnvelopeResponse - if err := c.handler.Get(ctx, endpoint, &resp); err != nil { - return nil, errors.Wrap(err, "could not get execution payload envelope") + + endpoint := fmt.Sprintf("/eth/v1/validator/execution_payload_envelopes/%d/%s", slot, hexutil.Encode(beaconBlockRoot[:])) + body, header, err := c.handler.GetSSZ(ctx, endpoint) + if err != nil { + return nil, nil, errors.Wrap(err, "could not get blinded execution payload envelope") + } + if strings.Contains(header.Get("Content-Type"), api.OctetStreamMediaType) { + blinded := ðpb.WireBlindedExecutionPayloadEnvelope{} + if err := blinded.UnmarshalSSZ(body); err != nil { + return nil, nil, errors.Wrap(err, "could not unmarshal blinded envelope SSZ") + } + return nil, blinded, nil + } + var resp structs.GetValidatorBlindedExecutionPayloadEnvelopeResponse + if err := json.Unmarshal(body, &resp); err != nil { + return nil, nil, errors.Wrap(err, "could not decode blinded envelope JSON") } if resp.Data == nil { - return nil, errors.New("execution payload envelope data is nil") + return nil, nil, errors.New("blinded execution payload envelope data is nil") } - envelope, err := resp.Data.ToConsensus() + blinded, err := resp.Data.ToConsensus() if err != nil { - return nil, errors.Wrap(err, "could not convert execution payload envelope to consensus") + return nil, nil, errors.Wrap(err, "could not convert blinded envelope") } - return envelope, nil + return nil, blinded, nil } +// publishExecutionPayloadEnvelope publishes the full envelope plus cached blobs/proofs as +// SignedExecutionPayloadEnvelopeContents (stateless flow). Stateful self-build uses +// publishBlindedExecutionPayloadEnvelope instead. func (c *beaconApiValidatorClient) publishExecutionPayloadEnvelope( ctx context.Context, envelope *ethpb.SignedExecutionPayloadEnvelope, ) (*empty.Empty, error) { - // In stateless mode, drain the envelope cache and publish Contents (envelope - // + blobs + proofs). On cache miss, log and fall through to bare publish. - if c.stateless && envelope != nil && envelope.Message != nil && envelope.Message.Payload != nil { - slot := primitives.Slot(envelope.Message.Payload.SlotNumber) - cachedEnv, blobs, kzgProofs := c.envelopeCache.Take(slot) - if cachedEnv != nil { - contents, err := structs.SignedExecutionPayloadEnvelopeContentsFromConsensus(envelope, kzgProofs, blobs) - if err != nil { - return nil, errors.Wrap(err, "could not convert envelope contents to JSON") - } - body, err := json.Marshal(contents) - if err != nil { - return nil, errors.Wrap(err, "could not marshal envelope contents") - } - if err := c.handler.Post(ctx, "/eth/v1/beacon/execution_payload_envelope", nil, bytes.NewBuffer(body), nil); err != nil { - return nil, errors.Wrap(err, "could not publish execution payload envelope contents") - } - return &empty.Empty{}, nil - } - log.WithField("slot", slot).Warn("Stateless publish: envelope cache miss; falling back to bare envelope publish") + const endpoint = "/eth/v1/beacon/execution_payload_envelopes" + if envelope == nil || envelope.Message == nil || envelope.Message.Payload == nil { + return nil, errors.New("nil signed envelope or payload") } - jsonEnvelope, err := structs.SignedExecutionPayloadEnvelopeFromConsensus(envelope) + slot := primitives.Slot(envelope.Message.Payload.SlotNumber) + cachedEnv, blobs, kzgProofs := c.envelopeCache.Take(slot) + if cachedEnv == nil { + return nil, errors.Errorf("stateless publish: envelope cache miss for slot %d", slot) + } + contents := ðpb.SignedExecutionPayloadEnvelopeContents{ + SignedExecutionPayloadEnvelope: envelope, + KzgProofs: kzgProofs, + Blobs: blobs, + } + ssz, err := contents.MarshalSSZ() if err != nil { - return nil, errors.Wrap(err, "could not convert envelope to JSON") + return nil, errors.Wrap(err, "could not marshal envelope contents SSZ") } - body, err := json.Marshal(jsonEnvelope) + jsonFn := func() ([]byte, error) { + j, jerr := structs.SignedExecutionPayloadEnvelopeContentsFromConsensus(envelope, kzgProofs, blobs) + if jerr != nil { + return nil, jerr + } + return json.Marshal(j) + } + if err := c.postEnvelope(ctx, endpoint, envelopeHeaders(false), ssz, jsonFn); err != nil { + return nil, errors.Wrap(err, "could not publish execution payload envelope contents") + } + return &empty.Empty{}, nil +} + +// publishBlindedExecutionPayloadEnvelope publishes the signed blinded envelope (stateful flow); the +// BN reconstructs the full envelope from its cache. Signature is valid by HTR equivalence. +func (c *beaconApiValidatorClient) publishBlindedExecutionPayloadEnvelope( + ctx context.Context, + signed *ethpb.SignedWireBlindedExecutionPayloadEnvelope, +) (*empty.Empty, error) { + const endpoint = "/eth/v1/beacon/execution_payload_envelopes" + if signed == nil || signed.Message == nil { + return nil, errors.New("nil signed blinded envelope") + } + ssz, err := signed.MarshalSSZ() if err != nil { - return nil, errors.Wrap(err, "could not marshal envelope") + return nil, errors.Wrap(err, "could not marshal blinded envelope SSZ") } - if err := c.handler.Post(ctx, "/eth/v1/beacon/execution_payload_envelope", nil, bytes.NewBuffer(body), nil); err != nil { - return nil, errors.Wrap(err, "could not publish execution payload envelope") + jsonFn := func() ([]byte, error) { + msg, jerr := structs.BlindedExecutionPayloadEnvelopeFromConsensus(signed.Message) + if jerr != nil { + return nil, jerr + } + j := &structs.SignedBlindedExecutionPayloadEnvelope{ + Message: msg, + Signature: hexutil.Encode(signed.Signature), + } + return json.Marshal(j) + } + if err := c.postEnvelope(ctx, endpoint, envelopeHeaders(true), ssz, jsonFn); err != nil { + return nil, errors.Wrap(err, "could not publish blinded execution payload envelope") } return &empty.Empty{}, nil } + +func envelopeHeaders(blinded bool) map[string]string { + return map[string]string{ + api.VersionHeader: version.String(version.Gloas), + api.ExecutionPayloadBlindedHeader: strconv.FormatBool(blinded), + } +} + +// postEnvelope publishes SSZ first; on 406 Not Acceptable falls back to JSON. +func (c *beaconApiValidatorClient) postEnvelope(ctx context.Context, endpoint string, headers map[string]string, ssz []byte, jsonFn func() ([]byte, error)) error { + _, _, err := c.handler.PostSSZ(ctx, endpoint, headers, bytes.NewBuffer(ssz)) + if err == nil { + return nil + } + errJson := &httputil.DefaultJsonError{} + if !errors.As(err, &errJson) { + return err + } + if errJson.Code != http.StatusNotAcceptable { + return errJson + } + log.WithError(err).Warn("Envelope SSZ publish rejected, falling back to JSON") + body, jerr := jsonFn() + if jerr != nil { + return errors.Wrap(jerr, "could not marshal envelope JSON for fallback") + } + return c.handler.Post(ctx, endpoint, headers, bytes.NewBuffer(body), nil) +} diff --git a/validator/client/beacon-api/execution_payload_envelope_test.go b/validator/client/beacon-api/execution_payload_envelope_test.go index 715a02c536ea..92e5766edc12 100644 --- a/validator/client/beacon-api/execution_payload_envelope_test.go +++ b/validator/client/beacon-api/execution_payload_envelope_test.go @@ -3,13 +3,18 @@ package beacon_api import ( "bytes" "encoding/json" + "fmt" + "net/http" "testing" + "github.com/OffchainLabs/prysm/v7/api" "github.com/OffchainLabs/prysm/v7/api/server/structs" "github.com/OffchainLabs/prysm/v7/consensus-types/primitives" "github.com/OffchainLabs/prysm/v7/encoding/bytesutil" + "github.com/OffchainLabs/prysm/v7/network/httputil" enginev1 "github.com/OffchainLabs/prysm/v7/proto/engine/v1" ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1" + "github.com/OffchainLabs/prysm/v7/runtime/version" "github.com/OffchainLabs/prysm/v7/testing/assert" "github.com/OffchainLabs/prysm/v7/testing/require" "github.com/OffchainLabs/prysm/v7/validator/client/beacon-api/mock" @@ -54,106 +59,108 @@ func TestGetExecutionPayloadEnvelope_CachedHit(t *testing.T) { } client.envelopeCache.Add(100, envelope, nil, nil) - resp, err := client.getExecutionPayloadEnvelope(t.Context(), 100) + full, blinded, err := client.getExecutionPayloadEnvelope(t.Context(), 100, [32]byte{}) require.NoError(t, err) - require.NotNil(t, resp) - assert.Equal(t, primitives.BuilderIndex(42), resp.BuilderIndex) + require.NotNil(t, full) + require.IsNil(t, blinded) + assert.Equal(t, primitives.BuilderIndex(42), full.BuilderIndex) // Peek must leave the entry in the cache so the publish path can read blob data. cached, _, _ := client.envelopeCache.peek(100) require.NotNil(t, cached) } -func TestGetExecutionPayloadEnvelope_Valid(t *testing.T) { +// Stateful: on a local cache miss the VC fetches the blinded envelope from the BN. +func TestGetExecutionPayloadEnvelope_StatefulFetchesBlinded(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() envelope := testProtoEnvelope() - jsonEnvelope, err := structs.ExecutionPayloadEnvelopeFromConsensus(envelope) + blinded, err := structs.WireBlindedFromFull(envelope) + require.NoError(t, err) + body, err := blinded.MarshalSSZ() require.NoError(t, err) + root := bytesutil.ToBytes32(envelope.BeaconBlockRoot) + respHeader := http.Header{} + respHeader.Set("Content-Type", api.OctetStreamMediaType) + handler := mock.NewMockJsonRestHandler(ctrl) - handler.EXPECT().Get( - gomock.Any(), - "/eth/v1/validator/execution_payload_envelope/100", + handler.EXPECT().GetSSZ( gomock.Any(), - ).SetArg( - 2, - structs.GetValidatorExecutionPayloadEnvelopeResponse{ - Version: "gloas", - Data: jsonEnvelope, - }, - ).Return(nil) + fmt.Sprintf("/eth/v1/validator/execution_payload_envelopes/100/%s", hexutil.Encode(root[:])), + ).Return(body, respHeader, nil) - client := &beaconApiValidatorClient{handler: handler} - resp, err := client.getExecutionPayloadEnvelope(t.Context(), 100) + client := &beaconApiValidatorClient{handler: handler, envelopeCache: newExecutionPayloadEnvelopeCache()} + full, gotBlinded, err := client.getExecutionPayloadEnvelope(t.Context(), 100, root) require.NoError(t, err) - require.NotNil(t, resp) - assert.Equal(t, primitives.BuilderIndex(42), resp.BuilderIndex) - assert.Equal(t, primitives.Slot(100), resp.Payload.SlotNumber) - assert.DeepEqual(t, envelope.BeaconBlockRoot, resp.BeaconBlockRoot) + require.IsNil(t, full) + require.NotNil(t, gotBlinded) + assert.Equal(t, primitives.BuilderIndex(42), gotBlinded.BuilderIndex) } -func TestGetExecutionPayloadEnvelope_Error(t *testing.T) { +// Stateful publish sends the blinded envelope with Eth-Execution-Payload-Blinded: true. +func TestPublishBlindedExecutionPayloadEnvelope(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - handler := mock.NewMockJsonRestHandler(ctrl) - handler.EXPECT().Get( - gomock.Any(), gomock.Any(), gomock.Any(), - ).Return(errors.New("not found")) - - client := &beaconApiValidatorClient{handler: handler} - _, err := client.getExecutionPayloadEnvelope(t.Context(), 999) - assert.ErrorContains(t, "not found", err) -} - -func TestGetExecutionPayloadEnvelope_NilData(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() + signed := ðpb.SignedExecutionPayloadEnvelope{Message: testProtoEnvelope(), Signature: bytesutil.PadTo([]byte("sig"), 96)} + signedBlinded, err := structs.SignedWireBlindedFromFull(signed) + require.NoError(t, err) + expectedBody, err := signedBlinded.MarshalSSZ() + require.NoError(t, err) + expectedHeaders := map[string]string{ + api.VersionHeader: version.String(version.Gloas), + api.ExecutionPayloadBlindedHeader: "true", + } handler := mock.NewMockJsonRestHandler(ctrl) - handler.EXPECT().Get( - gomock.Any(), gomock.Any(), gomock.Any(), - ).SetArg( - 2, - structs.GetValidatorExecutionPayloadEnvelopeResponse{ - Version: "gloas", - Data: nil, - }, - ).Return(nil) + handler.EXPECT().PostSSZ( + gomock.Any(), + "/eth/v1/beacon/execution_payload_envelopes", + expectedHeaders, + bytes.NewBuffer(expectedBody), + ).Return(nil, nil, nil) client := &beaconApiValidatorClient{handler: handler} - _, err := client.getExecutionPayloadEnvelope(t.Context(), 100) - assert.ErrorContains(t, "execution payload envelope data is nil", err) + resp, err := client.publishBlindedExecutionPayloadEnvelope(t.Context(), signedBlinded) + require.NoError(t, err) + require.NotNil(t, resp) } -func TestPublishExecutionPayloadEnvelope_Valid(t *testing.T) { +func TestPublishBlindedExecutionPayloadEnvelope_JSONFallbackOn406(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - envelope := testProtoEnvelope() - signed := ðpb.SignedExecutionPayloadEnvelope{ - Message: envelope, - Signature: bytesutil.PadTo([]byte("sig"), 96), - } - - jsonEnvelope, err := structs.SignedExecutionPayloadEnvelopeFromConsensus(signed) + signed := ðpb.SignedExecutionPayloadEnvelope{Message: testProtoEnvelope(), Signature: bytesutil.PadTo([]byte("sig"), 96)} + signedBlinded, err := structs.SignedWireBlindedFromFull(signed) + require.NoError(t, err) + msg, err := structs.BlindedExecutionPayloadEnvelopeFromConsensus(signedBlinded.Message) require.NoError(t, err) - expectedBody, err := json.Marshal(jsonEnvelope) + expectedJSON, err := json.Marshal(&structs.SignedBlindedExecutionPayloadEnvelope{ + Message: msg, + Signature: hexutil.Encode(signedBlinded.Signature), + }) require.NoError(t, err) + expectedHeaders := map[string]string{ + api.VersionHeader: version.String(version.Gloas), + api.ExecutionPayloadBlindedHeader: "true", + } handler := mock.NewMockJsonRestHandler(ctrl) + handler.EXPECT().PostSSZ( + gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), + ).Return(nil, nil, &httputil.DefaultJsonError{Code: http.StatusNotAcceptable, Message: "not acceptable"}) handler.EXPECT().Post( gomock.Any(), - "/eth/v1/beacon/execution_payload_envelope", - nil, - bytes.NewBuffer(expectedBody), + "/eth/v1/beacon/execution_payload_envelopes", + expectedHeaders, + bytes.NewBuffer(expectedJSON), nil, ).Return(nil) client := &beaconApiValidatorClient{handler: handler} - resp, err := client.publishExecutionPayloadEnvelope(t.Context(), signed) + resp, err := client.publishBlindedExecutionPayloadEnvelope(t.Context(), signedBlinded) require.NoError(t, err) require.NotNil(t, resp) } @@ -170,19 +177,24 @@ func TestPublishExecutionPayloadEnvelope_StatelessSendsContents(t *testing.T) { blob := bytesutil.PadTo([]byte("blob"), 131072) proof := bytesutil.PadTo([]byte("proof"), 48) - contents, err := structs.SignedExecutionPayloadEnvelopeContentsFromConsensus(signed, [][]byte{proof}, [][]byte{blob}) - require.NoError(t, err) - expectedBody, err := json.Marshal(contents) + expectedBody, err := (ðpb.SignedExecutionPayloadEnvelopeContents{ + SignedExecutionPayloadEnvelope: signed, + KzgProofs: [][]byte{proof}, + Blobs: [][]byte{blob}, + }).MarshalSSZ() require.NoError(t, err) + expectedHeaders := map[string]string{ + api.VersionHeader: version.String(version.Gloas), + api.ExecutionPayloadBlindedHeader: "false", + } handler := mock.NewMockJsonRestHandler(ctrl) - handler.EXPECT().Post( + handler.EXPECT().PostSSZ( gomock.Any(), - "/eth/v1/beacon/execution_payload_envelope", - nil, + "/eth/v1/beacon/execution_payload_envelopes", + expectedHeaders, bytes.NewBuffer(expectedBody), - nil, - ).Return(nil) + ).Return(nil, nil, nil) client := &beaconApiValidatorClient{ handler: handler, @@ -200,76 +212,24 @@ func TestPublishExecutionPayloadEnvelope_StatelessSendsContents(t *testing.T) { assert.Equal(t, (*ethpb.ExecutionPayloadEnvelope)(nil), cached) } -func TestPublishExecutionPayloadEnvelope_StatelessSendsContentsWithEmptyBlobs(t *testing.T) { +func TestPublishExecutionPayloadEnvelope_StatelessCacheMissErrors(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - envelope := testProtoEnvelope() signed := ðpb.SignedExecutionPayloadEnvelope{ - Message: envelope, + Message: testProtoEnvelope(), Signature: bytesutil.PadTo([]byte("sig"), 96), } - contents, err := structs.SignedExecutionPayloadEnvelopeContentsFromConsensus(signed, nil, nil) - require.NoError(t, err) - expectedBody, err := json.Marshal(contents) - require.NoError(t, err) - + // No PostSSZ/Post expectation — must error before any HTTP call. handler := mock.NewMockJsonRestHandler(ctrl) - handler.EXPECT().Post( - gomock.Any(), - "/eth/v1/beacon/execution_payload_envelope", - nil, - bytes.NewBuffer(expectedBody), - nil, - ).Return(nil) - client := &beaconApiValidatorClient{ handler: handler, - stateless: true, envelopeCache: newExecutionPayloadEnvelopeCache(), } - // Cache has an entry but no blobs (0-blob slot) — still send Contents. - client.envelopeCache.Add(primitives.Slot(envelope.Payload.SlotNumber), envelope, nil, nil) - - resp, err := client.publishExecutionPayloadEnvelope(t.Context(), signed) - require.NoError(t, err) - require.NotNil(t, resp) -} - -func TestPublishExecutionPayloadEnvelope_StatelessFallsBackWithoutBlobs(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - envelope := testProtoEnvelope() - signed := ðpb.SignedExecutionPayloadEnvelope{ - Message: envelope, - Signature: bytesutil.PadTo([]byte("sig"), 96), - } - jsonEnvelope, err := structs.SignedExecutionPayloadEnvelopeFromConsensus(signed) - require.NoError(t, err) - expectedBody, err := json.Marshal(jsonEnvelope) - require.NoError(t, err) - - handler := mock.NewMockJsonRestHandler(ctrl) - handler.EXPECT().Post( - gomock.Any(), - "/eth/v1/beacon/execution_payload_envelope", - nil, - bytes.NewBuffer(expectedBody), - nil, - ).Return(nil) - - client := &beaconApiValidatorClient{ - handler: handler, - stateless: true, - envelopeCache: newExecutionPayloadEnvelopeCache(), - } - - resp, err := client.publishExecutionPayloadEnvelope(t.Context(), signed) - require.NoError(t, err) - require.NotNil(t, resp) + _, err := client.publishExecutionPayloadEnvelope(t.Context(), signed) + assert.ErrorContains(t, "stateless publish: envelope cache miss", err) } func TestPublishExecutionPayloadEnvelope_Error(t *testing.T) { @@ -283,11 +243,13 @@ func TestPublishExecutionPayloadEnvelope_Error(t *testing.T) { } handler := mock.NewMockJsonRestHandler(ctrl) - handler.EXPECT().Post( - gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), - ).Return(errors.New("server error")) + handler.EXPECT().PostSSZ( + gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), + ).Return(nil, nil, errors.New("server error")) + + client := &beaconApiValidatorClient{handler: handler, envelopeCache: newExecutionPayloadEnvelopeCache()} + client.envelopeCache.Add(primitives.Slot(envelope.Payload.SlotNumber), envelope, nil, nil) - client := &beaconApiValidatorClient{handler: handler} _, err := client.publishExecutionPayloadEnvelope(t.Context(), signed) assert.ErrorContains(t, "server error", err) } diff --git a/validator/client/grpc-api/grpc_validator_client.go b/validator/client/grpc-api/grpc_validator_client.go index a8a30d6a66de..b2dabf8b8474 100644 --- a/validator/client/grpc-api/grpc_validator_client.go +++ b/validator/client/grpc-api/grpc_validator_client.go @@ -446,24 +446,35 @@ func (c *grpcValidatorClient) EnsureReady(ctx context.Context) bool { } // Gloas Fork Methods -func (c *grpcValidatorClient) GetExecutionPayloadEnvelope(ctx context.Context, slot primitives.Slot) (*ethpb.ExecutionPayloadEnvelope, error) { +// +// TODO(#580): the gRPC envelope path is full-typed end-to-end (get full, sign full, publish full). +// The beacon-APIs blinded flow (GET BlindedExecutionPayloadEnvelope / POST +// SignedBlindedExecutionPayloadEnvelope) is implemented only over REST. A blinded gRPC variant would +// need a v1alpha1 service change plus web3signer blinded signing; deferred as gRPC is BN-internal. +func (c *grpcValidatorClient) GetExecutionPayloadEnvelope(ctx context.Context, slot primitives.Slot, _ [32]byte) (*ethpb.ExecutionPayloadEnvelope, *ethpb.WireBlindedExecutionPayloadEnvelope, error) { req := ðpb.ExecutionPayloadEnvelopeRequest{ Slot: slot, } resp, err := c.getClient().GetExecutionPayloadEnvelope(ctx, req) if err != nil { - return nil, errors.Wrap( + return nil, nil, errors.Wrap( client.ErrConnectionIssue, errors.Wrap(err, "GetExecutionPayloadEnvelope").Error(), ) } - return resp.Envelope, nil + // TODO(#580): gRPC only returns the full envelope (blinded form is nil). The spec-wire blinded + // flow is REST-only; implementing it over gRPC needs a v1alpha1 service change + web3signer support. + return resp.Envelope, nil, nil } func (c *grpcValidatorClient) PublishExecutionPayloadEnvelope(ctx context.Context, in *ethpb.SignedExecutionPayloadEnvelope) (*empty.Empty, error) { return c.getClient().PublishExecutionPayloadEnvelope(ctx, in) } +func (c *grpcValidatorClient) PublishBlindedExecutionPayloadEnvelope(_ context.Context, _ *ethpb.SignedWireBlindedExecutionPayloadEnvelope) (*empty.Empty, error) { + return nil, errors.New("blinded execution payload envelope publishing is not supported over gRPC; use the REST API") +} + func (c *grpcValidatorClient) PayloadAttestationData(ctx context.Context, slot primitives.Slot) (*ethpb.PayloadAttestationData, error) { req := ðpb.PayloadAttestationDataRequest{ Slot: slot, diff --git a/validator/client/iface/validator_client.go b/validator/client/iface/validator_client.go index 8cc4c1c3edfd..745419720734 100644 --- a/validator/client/iface/validator_client.go +++ b/validator/client/iface/validator_client.go @@ -165,8 +165,9 @@ type ValidatorClient interface { AggregatedSyncSelections(ctx context.Context, selections []SyncCommitteeSelection) ([]SyncCommitteeSelection, error) Host() string EnsureReady(ctx context.Context) bool - GetExecutionPayloadEnvelope(ctx context.Context, slot primitives.Slot) (*ethpb.ExecutionPayloadEnvelope, error) + GetExecutionPayloadEnvelope(ctx context.Context, slot primitives.Slot, beaconBlockRoot [32]byte) (*ethpb.ExecutionPayloadEnvelope, *ethpb.WireBlindedExecutionPayloadEnvelope, error) PublishExecutionPayloadEnvelope(ctx context.Context, in *ethpb.SignedExecutionPayloadEnvelope) (*empty.Empty, error) + PublishBlindedExecutionPayloadEnvelope(ctx context.Context, in *ethpb.SignedWireBlindedExecutionPayloadEnvelope) (*empty.Empty, error) PayloadAttestationData(ctx context.Context, slot primitives.Slot) (*ethpb.PayloadAttestationData, error) SubmitPayloadAttestation(ctx context.Context, in *ethpb.PayloadAttestationMessage) (*empty.Empty, error) } diff --git a/validator/client/propose_gloas.go b/validator/client/propose_gloas.go index e6e2a4ac302e..33976fbb1e77 100644 --- a/validator/client/propose_gloas.go +++ b/validator/client/propose_gloas.go @@ -85,13 +85,29 @@ func (v *validator) proposeSelfBuildEnvelope( return nil } - envelope, err := v.validatorClient.GetExecutionPayloadEnvelope(ctx, slot) + blockRoot, err := blk.Block().HashTreeRoot() + if err != nil { + return errors.Wrap(err, "could not compute beacon block root") + } + + full, blinded, err := v.validatorClient.GetExecutionPayloadEnvelope(ctx, slot, blockRoot) if err != nil { validatorSelfBuildEnvelopeSubmissionTotal.WithLabelValues("failed").Inc() return errors.Wrap(err, "failed to get execution payload envelope for self-build") } - signedEnvelope, err := v.signExecutionPayloadEnvelope(ctx, pubKey, slot, envelope) + // Stateful REST returns only the blinded envelope (BN reconstructs the full from its cache); + // gRPC and stateless REST return the full envelope. + if full == nil { + if err := v.publishSelfBuildBlinded(ctx, pubKey, slot, blinded); err != nil { + validatorSelfBuildEnvelopeSubmissionTotal.WithLabelValues("failed").Inc() + return err + } + validatorSelfBuildEnvelopeSubmissionTotal.WithLabelValues("success").Inc() + return nil + } + + signedEnvelope, err := v.signExecutionPayloadEnvelope(ctx, pubKey, slot, full) if err != nil { validatorSelfBuildEnvelopeSubmissionTotal.WithLabelValues("failed").Inc() return errors.Wrap(err, "could not sign execution payload envelope") @@ -105,3 +121,43 @@ func (v *validator) proposeSelfBuildEnvelope( return nil } + +// publishSelfBuildBlinded signs the blinded envelope (HTR matches the full envelope, so the +// signature is valid against either) and publishes it. Signing is local-keymanager only — +// web3signer blinded-envelope signing is not yet supported. +func (v *validator) publishSelfBuildBlinded( + ctx context.Context, + pubKey [fieldparams.BLSPubkeyLength]byte, + slot primitives.Slot, + blinded *ethpb.WireBlindedExecutionPayloadEnvelope, +) error { + if blinded == nil { + return errors.New("nil blinded execution payload envelope") + } + epoch := slots.ToEpoch(slot) + domain, err := v.domainData(ctx, epoch, params.BeaconConfig().DomainBeaconBuilder[:]) + if err != nil { + return errors.Wrap(err, "could not get domain data") + } + if domain == nil { + return errors.New("nil domain data") + } + signingRoot, err := signing.ComputeSigningRoot(blinded, domain.SignatureDomain) + if err != nil { + return errors.Wrap(err, "could not compute signing root") + } + sig, err := v.km.Sign(ctx, &validatorpb.SignRequest{ + PublicKey: pubKey[:], + SigningRoot: signingRoot[:], + SignatureDomain: domain.SignatureDomain, + SigningSlot: slot, + }) + if err != nil { + return errors.Wrap(err, "could not sign blinded execution payload envelope") + } + signed := ðpb.SignedWireBlindedExecutionPayloadEnvelope{Message: blinded, Signature: sig.Marshal()} + if _, err := v.validatorClient.PublishBlindedExecutionPayloadEnvelope(ctx, signed); err != nil { + return errors.Wrap(err, "failed to publish blinded execution payload envelope") + } + return nil +} diff --git a/validator/client/propose_gloas_test.go b/validator/client/propose_gloas_test.go index f508e7b31319..5ab7a8d07321 100644 --- a/validator/client/propose_gloas_test.go +++ b/validator/client/propose_gloas_test.go @@ -28,12 +28,12 @@ func signedGloasBlock(t *testing.T, slot primitives.Slot, builderIndex primitive if blk.Block.Body == nil { blk.Block.Body = ðpb.BeaconBlockBodyGloas{} } - blk.Block.Body.SignedExecutionPayloadBid = ðpb.SignedExecutionPayloadBid{ + blk.Block.Body.SignedExecutionPayloadBid = util.HydrateSignedExecutionPayloadBid(ðpb.SignedExecutionPayloadBid{ Message: ðpb.ExecutionPayloadBid{ BuilderIndex: builderIndex, }, Signature: make([]byte, 96), - } + }) signed, err := consensusblocks.NewSignedBeaconBlock(blk) require.NoError(t, err) @@ -71,8 +71,8 @@ func TestProposeSelfBuildEnvelope(t *testing.T) { expectedEnvelope := testExecutionPayloadEnvelope(slot, builderIndex) m.validatorClient.EXPECT(). - GetExecutionPayloadEnvelope(gomock.Any(), slot). - Return(expectedEnvelope, nil) + GetExecutionPayloadEnvelope(gomock.Any(), slot, gomock.Any()). + Return(expectedEnvelope, nil, nil) builderDomain := make([]byte, 32) copy(builderDomain, params.BeaconConfig().DomainBeaconBuilder[:]) @@ -117,8 +117,8 @@ func TestProposeSelfBuildEnvelope_ClientError(t *testing.T) { defer finish() m.validatorClient.EXPECT(). - GetExecutionPayloadEnvelope(gomock.Any(), gomock.Any()). - Return(nil, errors.New("connection refused")) + GetExecutionPayloadEnvelope(gomock.Any(), gomock.Any(), gomock.Any()). + Return(nil, nil, errors.New("connection refused")) signedBlock := signedGloasBlock(t, 1, params.BeaconConfig().BuilderIndexSelfBuild) @@ -292,8 +292,8 @@ func TestProposeBlock_Gloas_EnvelopeAfterBlock(t *testing.T) { Return(ðpb.ProposeResponse{BlockRoot: make([]byte, 32)}, nil) getEnvelopeCall := m.validatorClient.EXPECT(). - GetExecutionPayloadEnvelope(gomock.Any(), primitives.Slot(1)). - Return(envelope, nil). + GetExecutionPayloadEnvelope(gomock.Any(), primitives.Slot(1), gomock.Any()). + Return(envelope, nil, nil). After(proposeCall) // DomainData for envelope signing.