Edit to validator self build for gloas (#16366)

This commit is contained in:
terence
2026-02-17 14:44:50 -08:00
committed by GitHub
parent 9ee77efe1f
commit 6b7721f6c3
4 changed files with 115 additions and 107 deletions

View File

@@ -393,7 +393,6 @@ func (c *grpcValidatorClient) EnsureReady(ctx context.Context) bool {
}
// Gloas Fork Methods
func (c *grpcValidatorClient) GetExecutionPayloadEnvelope(ctx context.Context, slot primitives.Slot, builderIndex primitives.BuilderIndex) (*ethpb.ExecutionPayloadEnvelope, error) {
req := &ethpb.ExecutionPayloadEnvelopeRequest{
Slot: slot,

View File

@@ -177,29 +177,9 @@ func (v *validator) ProposeBlock(ctx context.Context, slot primitives.Slot, pubK
return
}
// For Gloas, retrieve, sign, and publish the execution payload envelope after
// broadcasting the block. The envelope's state root is lazily computed by the
// beacon node using the post-block state, so the block must be submitted first.
if blk.Version() >= version.Gloas {
envelope, err := v.getExecutionPayloadEnvelope(ctx, slot, b)
if err != nil {
log.WithError(err).Error("Failed to get execution payload envelope")
if v.emitAccountMetrics {
ValidatorProposeFailVec.WithLabelValues(fmtKey).Inc()
}
return
}
signedEnvelope, err := v.signExecutionPayloadEnvelope(ctx, pubKey, slot, envelope)
if err != nil {
log.WithError(err).Error("Failed to sign execution payload envelope")
if v.emitAccountMetrics {
ValidatorProposeFailVec.WithLabelValues(fmtKey).Inc()
}
return
}
if _, err := v.validatorClient.PublishExecutionPayloadEnvelope(ctx, signedEnvelope); err != nil {
log.WithError(err).Error("Failed to publish execution payload envelope")
}
if err := v.proposeSelfBuildEnvelope(ctx, slot, pubKey, blk); err != nil {
log.WithError(err).Error("Failed to propose self-build envelope")
return
}
span.SetAttributes(

View File

@@ -6,38 +6,18 @@ import (
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/signing"
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/consensus-types/interfaces"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v7/monitoring/tracing/trace"
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
validatorpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1/validator-client"
"github.com/OffchainLabs/prysm/v7/runtime/version"
"github.com/OffchainLabs/prysm/v7/time/slots"
"github.com/pkg/errors"
)
// getExecutionPayloadEnvelope retrieves the execution payload envelope from the
// beacon node for the given block's builder index and slot.
func (v *validator) getExecutionPayloadEnvelope(
ctx context.Context,
slot primitives.Slot,
b *ethpb.GenericBeaconBlock,
) (*ethpb.ExecutionPayloadEnvelope, error) {
ctx, span := trace.StartSpan(ctx, "validator.getExecutionPayloadEnvelope")
defer span.End()
gloasBlock := b.GetGloas()
if gloasBlock == nil {
return nil, errors.New("expected Gloas block but got nil")
}
if gloasBlock.Body == nil || gloasBlock.Body.SignedExecutionPayloadBid == nil || gloasBlock.Body.SignedExecutionPayloadBid.Message == nil {
return nil, errors.New("block missing signed execution payload bid")
}
builderIndex := gloasBlock.Body.SignedExecutionPayloadBid.Message.BuilderIndex
return v.validatorClient.GetExecutionPayloadEnvelope(ctx, slot, builderIndex)
}
// signExecutionPayloadEnvelope signs the execution payload envelope using the
// builder's key. The envelope is signed with DomainBeaconBuilder since it is
// proposer's key. The envelope is signed with DomainBeaconBuilder since it is
// a builder artifact — even in the self-build case where the proposer acts as
// their own builder.
func (v *validator) signExecutionPayloadEnvelope(
@@ -82,3 +62,41 @@ func (v *validator) signExecutionPayloadEnvelope(
Signature: sig.Marshal(),
}, nil
}
func (v *validator) proposeSelfBuildEnvelope(
ctx context.Context,
slot primitives.Slot,
pubKey [fieldparams.BLSPubkeyLength]byte,
blk interfaces.SignedBeaconBlock,
) error {
if blk.Version() < version.Gloas {
return nil
}
bid, err := blk.Block().Body().SignedExecutionPayloadBid()
if err != nil {
return err
}
if bid == nil || bid.Message == nil {
return errors.New("no execution payload bid found in block body")
}
if bid.Message.BuilderIndex != params.BeaconConfig().BuilderIndexSelfBuild {
return nil
}
envelope, err := v.validatorClient.GetExecutionPayloadEnvelope(ctx, slot, params.BeaconConfig().BuilderIndexSelfBuild)
if err != nil {
return errors.Wrap(err, "failed to get execution payload envelope for self-build")
}
signedEnvelope, err := v.signExecutionPayloadEnvelope(ctx, pubKey, slot, envelope)
if err != nil {
return errors.Wrap(err, "could not sign execution payload envelope")
}
if _, err := v.validatorClient.PublishExecutionPayloadEnvelope(ctx, signedEnvelope); err != nil {
return errors.Wrap(err, "failed to publish execution payload envelope")
}
return nil
}

View File

@@ -6,6 +6,8 @@ import (
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/signing"
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
"github.com/OffchainLabs/prysm/v7/config/params"
consensusblocks "github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
"github.com/OffchainLabs/prysm/v7/consensus-types/interfaces"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v7/crypto/bls"
enginev1 "github.com/OffchainLabs/prysm/v7/proto/engine/v1"
@@ -18,6 +20,26 @@ import (
"google.golang.org/protobuf/types/known/emptypb"
)
func signedGloasBlock(t *testing.T, slot primitives.Slot, builderIndex primitives.BuilderIndex) interfaces.SignedBeaconBlock {
t.Helper()
blk := util.NewBeaconBlockGloas()
blk.Block.Slot = slot
if blk.Block.Body == nil {
blk.Block.Body = &ethpb.BeaconBlockBodyGloas{}
}
blk.Block.Body.SignedExecutionPayloadBid = &ethpb.SignedExecutionPayloadBid{
Message: &ethpb.ExecutionPayloadBid{
BuilderIndex: builderIndex,
},
Signature: make([]byte, 96),
}
signed, err := consensusblocks.NewSignedBeaconBlock(blk)
require.NoError(t, err)
return signed
}
func testExecutionPayloadEnvelope(slot primitives.Slot, builderIndex primitives.BuilderIndex) *ethpb.ExecutionPayloadEnvelope {
return &ethpb.ExecutionPayloadEnvelope{
Payload: &enginev1.ExecutionPayloadDeneb{
@@ -39,12 +61,12 @@ func testExecutionPayloadEnvelope(slot primitives.Slot, builderIndex primitives.
}
}
func TestExecutionPayloadEnvelope(t *testing.T) {
validator, m, _, finish := setup(t, false)
func TestProposeSelfBuildEnvelope(t *testing.T) {
validator, m, validatorKey, finish := setup(t, false)
defer finish()
slot := primitives.Slot(100)
builderIndex := primitives.BuilderIndex(42)
builderIndex := params.BeaconConfig().BuilderIndexSelfBuild
expectedEnvelope := testExecutionPayloadEnvelope(slot, builderIndex)
@@ -52,80 +74,59 @@ func TestExecutionPayloadEnvelope(t *testing.T) {
GetExecutionPayloadEnvelope(gomock.Any(), slot, builderIndex).
Return(expectedEnvelope, nil)
b := &ethpb.GenericBeaconBlock{
Block: &ethpb.GenericBeaconBlock_Gloas{
Gloas: &ethpb.BeaconBlockGloas{
Slot: slot,
Body: &ethpb.BeaconBlockBodyGloas{
SignedExecutionPayloadBid: &ethpb.SignedExecutionPayloadBid{
Message: &ethpb.ExecutionPayloadBid{
BuilderIndex: builderIndex,
},
Signature: make([]byte, 96),
},
},
},
},
}
builderDomain := make([]byte, 32)
copy(builderDomain, params.BeaconConfig().DomainBeaconBuilder[:])
m.validatorClient.EXPECT().
DomainData(gomock.Any(), gomock.Any()).
Return(&ethpb.DomainResponse{SignatureDomain: builderDomain}, nil)
envelope, err := validator.getExecutionPayloadEnvelope(t.Context(), slot, b)
m.validatorClient.EXPECT().
PublishExecutionPayloadEnvelope(gomock.Any(), gomock.AssignableToTypeOf(&ethpb.SignedExecutionPayloadEnvelope{})).
Return(&emptypb.Empty{}, nil)
signedBlock := signedGloasBlock(t, slot, builderIndex)
var pubKey [fieldparams.BLSPubkeyLength]byte
copy(pubKey[:], validatorKey.PublicKey().Marshal())
err := validator.proposeSelfBuildEnvelope(t.Context(), slot, pubKey, signedBlock)
require.NoError(t, err)
require.DeepEqual(t, expectedEnvelope, envelope)
}
func TestExecutionPayloadEnvelope_NilBlock(t *testing.T) {
func TestProposeSelfBuildEnvelope_MissingBid(t *testing.T) {
validator, _, _, finish := setup(t, false)
defer finish()
b := &ethpb.GenericBeaconBlock{
Block: &ethpb.GenericBeaconBlock_Gloas{},
blk := util.NewBeaconBlockGloas()
blk.Block.Slot = 1
if blk.Block.Body == nil {
blk.Block.Body = &ethpb.BeaconBlockBodyGloas{}
}
blk.Block.Body.SignedExecutionPayloadBid = nil
_, err := validator.getExecutionPayloadEnvelope(t.Context(), 1, b)
require.ErrorContains(t, "expected Gloas block but got nil", err)
signedBlock, err := consensusblocks.NewSignedBeaconBlock(blk)
require.NoError(t, err)
var pubKey [fieldparams.BLSPubkeyLength]byte
err = validator.proposeSelfBuildEnvelope(t.Context(), 1, pubKey, signedBlock)
require.ErrorContains(t, "no execution payload bid found in block body", err)
}
func TestExecutionPayloadEnvelope_MissingBid(t *testing.T) {
validator, _, _, finish := setup(t, false)
defer finish()
b := &ethpb.GenericBeaconBlock{
Block: &ethpb.GenericBeaconBlock_Gloas{
Gloas: &ethpb.BeaconBlockGloas{
Slot: 1,
Body: &ethpb.BeaconBlockBodyGloas{},
},
},
}
_, err := validator.getExecutionPayloadEnvelope(t.Context(), 1, b)
require.ErrorContains(t, "block missing signed execution payload bid", err)
}
func TestExecutionPayloadEnvelope_ClientError(t *testing.T) {
validator, m, _, finish := setup(t, false)
func TestProposeSelfBuildEnvelope_ClientError(t *testing.T) {
validator, m, validatorKey, finish := setup(t, false)
defer finish()
m.validatorClient.EXPECT().
GetExecutionPayloadEnvelope(gomock.Any(), gomock.Any(), gomock.Any()).
Return(nil, errors.New("connection refused"))
b := &ethpb.GenericBeaconBlock{
Block: &ethpb.GenericBeaconBlock_Gloas{
Gloas: &ethpb.BeaconBlockGloas{
Slot: 1,
Body: &ethpb.BeaconBlockBodyGloas{
SignedExecutionPayloadBid: &ethpb.SignedExecutionPayloadBid{
Message: &ethpb.ExecutionPayloadBid{BuilderIndex: 1},
Signature: make([]byte, 96),
},
},
},
},
}
signedBlock := signedGloasBlock(t, 1, params.BeaconConfig().BuilderIndexSelfBuild)
_, err := validator.getExecutionPayloadEnvelope(t.Context(), 1, b)
require.ErrorContains(t, "connection refused", err)
var pubKey [fieldparams.BLSPubkeyLength]byte
copy(pubKey[:], validatorKey.PublicKey().Marshal())
err := validator.proposeSelfBuildEnvelope(t.Context(), 1, pubKey, signedBlock)
require.ErrorContains(t, "failed to get execution payload envelope for self-build", err)
}
func TestSignExecutionPayloadEnvelope(t *testing.T) {
@@ -250,7 +251,17 @@ func TestProposeBlock_Gloas_EnvelopeAfterBlock(t *testing.T) {
copy(pubKey[:], validatorKey.PublicKey().Marshal())
blk := util.NewBeaconBlockGloas()
builderIndex := blk.Block.Body.SignedExecutionPayloadBid.Message.BuilderIndex
builderIndex := params.BeaconConfig().BuilderIndexSelfBuild
if blk.Block.Body == nil {
blk.Block.Body = &ethpb.BeaconBlockBodyGloas{}
}
if blk.Block.Body.SignedExecutionPayloadBid == nil {
blk.Block.Body.SignedExecutionPayloadBid = &ethpb.SignedExecutionPayloadBid{}
}
if blk.Block.Body.SignedExecutionPayloadBid.Message == nil {
blk.Block.Body.SignedExecutionPayloadBid.Message = &ethpb.ExecutionPayloadBid{}
}
blk.Block.Body.SignedExecutionPayloadBid.Message.BuilderIndex = builderIndex
gloasBlock := &ethpb.GenericBeaconBlock{
Block: &ethpb.GenericBeaconBlock_Gloas{