diff --git a/beacon-chain/blockchain/process_block_test.go b/beacon-chain/blockchain/process_block_test.go index 4306d16c8b..3cce2ca77e 100644 --- a/beacon-chain/blockchain/process_block_test.go +++ b/beacon-chain/blockchain/process_block_test.go @@ -2805,6 +2805,10 @@ func TestProcessLightClientUpdate(t *testing.T) { require.NoError(t, s.cfg.BeaconDB.SaveHeadBlockRoot(ctx, [32]byte{1, 2})) for _, testVersion := range version.All()[1:] { + if testVersion == version.Gloas { + // TODO(16027): Unskip light client tests for Gloas + continue + } t.Run(version.String(testVersion), func(t *testing.T) { l := util.NewTestLightClient(t, testVersion) diff --git a/beacon-chain/core/blocks/payload.go b/beacon-chain/core/blocks/payload.go index 011befdeec..3074a20bf8 100644 --- a/beacon-chain/core/blocks/payload.go +++ b/beacon-chain/core/blocks/payload.go @@ -90,6 +90,9 @@ func IsExecutionEnabled(st state.ReadOnlyBeaconState, body interfaces.ReadOnlyBe if st == nil || body == nil { return false, errors.New("nil state or block body") } + if st.Version() >= version.Capella { + return true, nil + } if IsPreBellatrixVersion(st.Version()) { return false, nil } diff --git a/beacon-chain/core/blocks/payload_test.go b/beacon-chain/core/blocks/payload_test.go index d5119e1299..d9e76cde1a 100644 --- a/beacon-chain/core/blocks/payload_test.go +++ b/beacon-chain/core/blocks/payload_test.go @@ -260,11 +260,12 @@ func Test_IsExecutionBlockCapella(t *testing.T) { func Test_IsExecutionEnabled(t *testing.T) { tests := []struct { - name string - payload *enginev1.ExecutionPayload - header interfaces.ExecutionData - useAltairSt bool - want bool + name string + payload *enginev1.ExecutionPayload + header interfaces.ExecutionData + useAltairSt bool + useCapellaSt bool + want bool }{ { name: "use older than bellatrix state", @@ -331,6 +332,17 @@ func Test_IsExecutionEnabled(t *testing.T) { }(), want: true, }, + { + name: "capella state always enabled", + payload: emptyPayload(), + header: func() interfaces.ExecutionData { + h, err := emptyPayloadHeader() + require.NoError(t, err) + return h + }(), + useCapellaSt: true, + want: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -342,6 +354,8 @@ func Test_IsExecutionEnabled(t *testing.T) { require.NoError(t, err) if tt.useAltairSt { st, _ = util.DeterministicGenesisStateAltair(t, 1) + } else if tt.useCapellaSt { + st, _ = util.DeterministicGenesisStateCapella(t, 1) } got, err := blocks.IsExecutionEnabled(st, body) require.NoError(t, err) diff --git a/beacon-chain/db/kv/lightclient_test.go b/beacon-chain/db/kv/lightclient_test.go index bb49e67b57..7b11e95da7 100644 --- a/beacon-chain/db/kv/lightclient_test.go +++ b/beacon-chain/db/kv/lightclient_test.go @@ -216,6 +216,10 @@ func TestStore_LightClientUpdate_CanSaveRetrieve(t *testing.T) { db := setupDB(t) ctx := t.Context() for _, testVersion := range version.All()[1:] { + if testVersion == version.Gloas { + // TODO(16027): Unskip light client tests for Gloas + continue + } t.Run(version.String(testVersion), func(t *testing.T) { update, err := createUpdate(t, testVersion) require.NoError(t, err) @@ -572,6 +576,10 @@ func TestStore_LightClientBootstrap_CanSaveRetrieve(t *testing.T) { require.IsNil(t, retrievedBootstrap) }) for _, testVersion := range version.All()[1:] { + if testVersion == version.Gloas { + // TODO(16027): Unskip light client tests for Gloas + continue + } t.Run(version.String(testVersion), func(t *testing.T) { bootstrap, err := createDefaultLightClientBootstrap(primitives.Slot(uint64(params.BeaconConfig().VersionToForkEpochMap()[testVersion]) * uint64(params.BeaconConfig().SlotsPerEpoch))) require.NoError(t, err) diff --git a/beacon-chain/light-client/lightclient_test.go b/beacon-chain/light-client/lightclient_test.go index 8c1e5a544c..86ed754eeb 100644 --- a/beacon-chain/light-client/lightclient_test.go +++ b/beacon-chain/light-client/lightclient_test.go @@ -33,6 +33,10 @@ func TestLightClient_NewLightClientOptimisticUpdateFromBeaconState(t *testing.T) params.OverrideBeaconConfig(cfg) for _, testVersion := range version.All()[1:] { + if testVersion == version.Gloas { + // TODO(16027): Unskip light client tests for Gloas + continue + } t.Run(version.String(testVersion), func(t *testing.T) { l := util.NewTestLightClient(t, testVersion) diff --git a/beacon-chain/rpc/eth/light-client/handlers_test.go b/beacon-chain/rpc/eth/light-client/handlers_test.go index bc36785542..2d036ed986 100644 --- a/beacon-chain/rpc/eth/light-client/handlers_test.go +++ b/beacon-chain/rpc/eth/light-client/handlers_test.go @@ -47,6 +47,10 @@ func TestLightClientHandler_GetLightClientBootstrap(t *testing.T) { params.OverrideBeaconConfig(cfg) for _, testVersion := range version.All()[1:] { + if testVersion == version.Gloas { + // TODO(16027): Unskip light client tests for Gloas + continue + } t.Run(version.String(testVersion), func(t *testing.T) { l := util.NewTestLightClient(t, testVersion) @@ -178,6 +182,10 @@ func TestLightClientHandler_GetLightClientByRange(t *testing.T) { t.Run("can save retrieve", func(t *testing.T) { for _, testVersion := range version.All()[1:] { + if testVersion == version.Gloas { + // TODO(16027): Unskip light client tests for Gloas + continue + } t.Run(version.String(testVersion), func(t *testing.T) { slot := primitives.Slot(params.BeaconConfig().VersionToForkEpochMap()[testVersion] * primitives.Epoch(config.SlotsPerEpoch)).Add(1) @@ -732,6 +740,10 @@ func TestLightClientHandler_GetLightClientFinalityUpdate(t *testing.T) { }) for _, testVersion := range version.All()[1:] { + if testVersion == version.Gloas { + // TODO(16027): Unskip light client tests for Gloas + continue + } t.Run(version.String(testVersion), func(t *testing.T) { ctx := t.Context() @@ -827,6 +839,10 @@ func TestLightClientHandler_GetLightClientOptimisticUpdate(t *testing.T) { }) for _, testVersion := range version.All()[1:] { + if testVersion == version.Gloas { + // TODO(16027): Unskip light client tests for Gloas + continue + } t.Run(version.String(testVersion), func(t *testing.T) { ctx := t.Context() l := util.NewTestLightClient(t, testVersion) diff --git a/changelog/ttsao_implement-gloas-fork-blocks.md b/changelog/ttsao_implement-gloas-fork-blocks.md new file mode 100644 index 0000000000..1cee57d2e3 --- /dev/null +++ b/changelog/ttsao_implement-gloas-fork-blocks.md @@ -0,0 +1,3 @@ +### Added + +- Implement Gloas fork support in consensus-types/blocks with factory methods, getters, setters, and proto handling diff --git a/consensus-types/blocks/BUILD.bazel b/consensus-types/blocks/BUILD.bazel index 8fea0333e7..402c3fb9e4 100644 --- a/consensus-types/blocks/BUILD.bazel +++ b/consensus-types/blocks/BUILD.bazel @@ -53,6 +53,7 @@ go_test( "roblob_test.go", "roblock_test.go", "rodatacolumn_test.go", + "setters_test.go", ], embed = [":go_default_library"], deps = [ diff --git a/consensus-types/blocks/factory.go b/consensus-types/blocks/factory.go index 74a21d970a..f11797ad74 100644 --- a/consensus-types/blocks/factory.go +++ b/consensus-types/blocks/factory.go @@ -82,6 +82,8 @@ func NewSignedBeaconBlock(i any) (interfaces.SignedBeaconBlock, error) { return initBlindedSignedBlockFromProtoFulu(b) case *eth.GenericSignedBeaconBlock_BlindedFulu: return initBlindedSignedBlockFromProtoFulu(b.BlindedFulu) + case *eth.SignedBeaconBlockGloas: + return initSignedBlockFromProtoGloas(b) default: return nil, errors.Wrapf(ErrUnsupportedSignedBeaconBlock, "unable to create block from type %T", i) } @@ -138,6 +140,8 @@ func NewBeaconBlock(i any) (interfaces.ReadOnlyBeaconBlock, error) { return initBlindedBlockFromProtoFulu(b) case *eth.GenericBeaconBlock_BlindedFulu: return initBlindedBlockFromProtoFulu(b.BlindedFulu) + case *eth.BeaconBlockGloas: + return initBlockFromProtoGloas(b) default: return nil, errors.Wrapf(errUnsupportedBeaconBlock, "unable to create block from type %T", i) } @@ -168,6 +172,8 @@ func NewBeaconBlockBody(i any) (interfaces.ReadOnlyBeaconBlockBody, error) { return initBlockBodyFromProtoElectra(b) case *eth.BlindedBeaconBlockBodyElectra: return initBlindedBlockBodyFromProtoElectra(b) + case *eth.BeaconBlockBodyGloas: + return initBlockBodyFromProtoGloas(b) default: return nil, errors.Wrapf(errUnsupportedBeaconBlockBody, "unable to create block body from type %T", i) } @@ -260,6 +266,12 @@ func BuildSignedBeaconBlock(blk interfaces.ReadOnlyBeaconBlock, signature []byte return nil, errIncorrectBlockVersion } return NewSignedBeaconBlock(ð.SignedBeaconBlockFulu{Block: pb, Signature: signature}) + case version.Gloas: + pb, ok := pb.(*eth.BeaconBlockGloas) + if !ok { + return nil, errIncorrectBlockVersion + } + return NewSignedBeaconBlock(ð.SignedBeaconBlockGloas{Block: pb, Signature: signature}) default: return nil, errUnsupportedBeaconBlock } diff --git a/consensus-types/blocks/factory_test.go b/consensus-types/blocks/factory_test.go index 76fb0ff531..0249558b41 100644 --- a/consensus-types/blocks/factory_test.go +++ b/consensus-types/blocks/factory_test.go @@ -161,6 +161,18 @@ func Test_NewSignedBeaconBlock(t *testing.T) { assert.Equal(t, version.Deneb, b.Version()) assert.Equal(t, true, b.IsBlinded()) }) + t.Run("SignedBeaconBlockGloas", func(t *testing.T) { + pb := ð.SignedBeaconBlockGloas{ + Block: ð.BeaconBlockGloas{ + Body: ð.BeaconBlockBodyGloas{}, + }, + Signature: []byte("sig"), + } + b, err := NewSignedBeaconBlock(pb) + require.NoError(t, err) + assert.Equal(t, version.Gloas, b.Version()) + assert.Equal(t, false, b.IsBlinded()) + }) t.Run("nil", func(t *testing.T) { _, err := NewSignedBeaconBlock(nil) assert.ErrorContains(t, "received nil object", err) @@ -276,6 +288,13 @@ func Test_NewBeaconBlock(t *testing.T) { assert.Equal(t, version.Deneb, b.Version()) assert.Equal(t, true, b.IsBlinded()) }) + t.Run("BeaconBlockGloas", func(t *testing.T) { + pb := ð.BeaconBlockGloas{Body: ð.BeaconBlockBodyGloas{}} + b, err := NewBeaconBlock(pb) + require.NoError(t, err) + assert.Equal(t, version.Gloas, b.Version()) + assert.Equal(t, false, b.IsBlinded()) + }) t.Run("nil", func(t *testing.T) { _, err := NewBeaconBlock(nil) assert.ErrorContains(t, "received nil object", err) @@ -354,6 +373,15 @@ func Test_NewBeaconBlockBody(t *testing.T) { assert.Equal(t, version.Deneb, b.version) assert.Equal(t, true, b.IsBlinded()) }) + t.Run("BeaconBlockBodyGloas", func(t *testing.T) { + pb := ð.BeaconBlockBodyGloas{} + i, err := NewBeaconBlockBody(pb) + require.NoError(t, err) + b, ok := i.(*BeaconBlockBody) + require.Equal(t, true, ok) + assert.Equal(t, version.Gloas, b.version) + assert.Equal(t, false, b.IsBlinded()) + }) t.Run("nil", func(t *testing.T) { _, err := NewBeaconBlockBody(nil) assert.ErrorContains(t, "received nil object", err) @@ -425,6 +453,14 @@ func Test_BuildSignedBeaconBlock(t *testing.T) { assert.Equal(t, version.Deneb, sb.Version()) assert.Equal(t, true, sb.IsBlinded()) }) + t.Run("Gloas", func(t *testing.T) { + b := &BeaconBlock{version: version.Gloas, body: &BeaconBlockBody{version: version.Gloas}} + sb, err := BuildSignedBeaconBlock(b, sig[:]) + require.NoError(t, err) + assert.DeepEqual(t, sig, sb.Signature()) + assert.Equal(t, version.Gloas, sb.Version()) + assert.Equal(t, false, sb.IsBlinded()) + }) } func TestBuildSignedBeaconBlockFromExecutionPayload(t *testing.T) { @@ -535,4 +571,21 @@ func TestBuildSignedBeaconBlockFromExecutionPayload(t *testing.T) { require.DeepEqual(t, uint64(123), payload.ExcessBlobGas) require.DeepEqual(t, uint64(321), payload.BlobGasUsed) }) + t.Run("gloas execution unsupported", func(t *testing.T) { + base := &SignedBeaconBlock{ + version: version.Gloas, + block: &BeaconBlock{version: version.Gloas, body: &BeaconBlockBody{version: version.Gloas}}, + } + blinded := &testBlindedSignedBeaconBlock{SignedBeaconBlock: base} + _, err := BuildSignedBeaconBlockFromExecutionPayload(blinded, nil) + require.ErrorContains(t, "Execution is not supported for gloas", err) + }) +} + +type testBlindedSignedBeaconBlock struct { + *SignedBeaconBlock +} + +func (b *testBlindedSignedBeaconBlock) IsBlinded() bool { + return true } diff --git a/consensus-types/blocks/getters.go b/consensus-types/blocks/getters.go index b1e41cb06c..6486606bce 100644 --- a/consensus-types/blocks/getters.go +++ b/consensus-types/blocks/getters.go @@ -80,6 +80,8 @@ func (b *SignedBeaconBlock) Copy() (interfaces.SignedBeaconBlock, error) { return initBlindedSignedBlockFromProtoFulu(pb.(*eth.SignedBlindedBeaconBlockFulu).Copy()) } return initSignedBlockFromProtoFulu(pb.(*eth.SignedBeaconBlockFulu).Copy()) + case version.Gloas: + return initSignedBlockFromProtoGloas(eth.CopySignedBeaconBlockGloas(pb.(*eth.SignedBeaconBlockGloas))) default: return nil, errIncorrectBlockVersion } @@ -157,6 +159,9 @@ func (b *SignedBeaconBlock) PbGenericBlock() (*eth.GenericSignedBeaconBlock, err return ð.GenericSignedBeaconBlock{ Block: ð.GenericSignedBeaconBlock_Fulu{Fulu: bc}, }, nil + case version.Gloas: + // Gloas doesn't support GenericSignedBeaconBlock yet + return nil, errors.New("Gloas blocks don't support GenericSignedBeaconBlock conversion") default: return nil, errIncorrectBlockVersion } @@ -164,7 +169,7 @@ func (b *SignedBeaconBlock) PbGenericBlock() (*eth.GenericSignedBeaconBlock, err // ToBlinded converts a non-blinded block to its blinded equivalent. func (b *SignedBeaconBlock) ToBlinded() (interfaces.ReadOnlySignedBeaconBlock, error) { - if b.version < version.Bellatrix { + if b.version < version.Bellatrix || b.version >= version.Gloas { return nil, ErrUnsupportedVersion } if b.IsBlinded() { @@ -376,7 +381,7 @@ func (b *SignedBeaconBlock) Version() int { // IsBlinded metadata on whether a block is blinded func (b *SignedBeaconBlock) IsBlinded() bool { - return b.version >= version.Bellatrix && b.block.body.executionPayload == nil + return b.version < version.Gloas && b.version >= version.Bellatrix && b.block.body.executionPayload == nil } // Header converts the underlying protobuf object from blinded block to header format. @@ -437,6 +442,8 @@ func (b *SignedBeaconBlock) MarshalSSZ() ([]byte, error) { return pb.(*eth.SignedBlindedBeaconBlockFulu).MarshalSSZ() } return pb.(*eth.SignedBeaconBlockFulu).MarshalSSZ() + case version.Gloas: + return pb.(*eth.SignedBeaconBlockGloas).MarshalSSZ() default: return []byte{}, errIncorrectBlockVersion } @@ -479,6 +486,8 @@ func (b *SignedBeaconBlock) MarshalSSZTo(dst []byte) ([]byte, error) { return pb.(*eth.SignedBlindedBeaconBlockFulu).MarshalSSZTo(dst) } return pb.(*eth.SignedBeaconBlockFulu).MarshalSSZTo(dst) + case version.Gloas: + return pb.(*eth.SignedBeaconBlockGloas).MarshalSSZTo(dst) default: return []byte{}, errIncorrectBlockVersion } @@ -526,6 +535,8 @@ func (b *SignedBeaconBlock) SizeSSZ() int { return pb.(*eth.SignedBlindedBeaconBlockFulu).SizeSSZ() } return pb.(*eth.SignedBeaconBlockFulu).SizeSSZ() + case version.Gloas: + return pb.(*eth.SignedBeaconBlockGloas).SizeSSZ() default: panic(incorrectBlockVersion) } @@ -666,6 +677,16 @@ func (b *SignedBeaconBlock) UnmarshalSSZ(buf []byte) error { return err } } + case version.Gloas: + pb := ð.SignedBeaconBlockGloas{} + err := pb.UnmarshalSSZ(buf) + if err != nil { + return err + } + newBlock, err = initSignedBlockFromProtoGloas(pb) + if err != nil { + return err + } default: return errIncorrectBlockVersion } @@ -705,7 +726,7 @@ func (b *BeaconBlock) IsNil() bool { // IsBlinded checks if the beacon block is a blinded block. func (b *BeaconBlock) IsBlinded() bool { - return b.version >= version.Bellatrix && b.body.executionPayload == nil + return b.version < version.Gloas && b.version >= version.Bellatrix && b.body.executionPayload == nil } // Version of the underlying protobuf object. @@ -749,6 +770,9 @@ func (b *BeaconBlock) HashTreeRoot() ([field_params.RootLength]byte, error) { return pb.(*eth.BlindedBeaconBlockFulu).HashTreeRoot() } return pb.(*eth.BeaconBlockElectra).HashTreeRoot() + case version.Gloas: + return pb.(*eth.BeaconBlockGloas).HashTreeRoot() + default: return [field_params.RootLength]byte{}, errIncorrectBlockVersion } @@ -790,6 +814,8 @@ func (b *BeaconBlock) HashTreeRootWith(h *ssz.Hasher) error { return pb.(*eth.BlindedBeaconBlockFulu).HashTreeRootWith(h) } return pb.(*eth.BeaconBlockElectra).HashTreeRootWith(h) + case version.Gloas: + return pb.(*eth.BeaconBlockGloas).HashTreeRootWith(h) default: return errIncorrectBlockVersion } @@ -832,6 +858,8 @@ func (b *BeaconBlock) MarshalSSZ() ([]byte, error) { return pb.(*eth.BlindedBeaconBlockFulu).MarshalSSZ() } return pb.(*eth.BeaconBlockElectra).MarshalSSZ() + case version.Gloas: + return pb.(*eth.BeaconBlockGloas).MarshalSSZ() default: return []byte{}, errIncorrectBlockVersion } @@ -874,6 +902,8 @@ func (b *BeaconBlock) MarshalSSZTo(dst []byte) ([]byte, error) { return pb.(*eth.BlindedBeaconBlockFulu).MarshalSSZTo(dst) } return pb.(*eth.BeaconBlockElectra).MarshalSSZTo(dst) + case version.Gloas: + return pb.(*eth.BeaconBlockGloas).MarshalSSZTo(dst) default: return []byte{}, errIncorrectBlockVersion } @@ -921,6 +951,8 @@ func (b *BeaconBlock) SizeSSZ() int { return pb.(*eth.BlindedBeaconBlockFulu).SizeSSZ() } return pb.(*eth.BeaconBlockElectra).SizeSSZ() + case version.Gloas: + return pb.(*eth.BeaconBlockGloas).SizeSSZ() default: panic(incorrectBodyVersion) } @@ -1061,6 +1093,16 @@ func (b *BeaconBlock) UnmarshalSSZ(buf []byte) error { return err } } + case version.Gloas: + pb := ð.BeaconBlockGloas{} + if err := pb.UnmarshalSSZ(buf); err != nil { + return err + } + var err error + newBlock, err = initBlockFromProtoGloas(pb) + if err != nil { + return err + } default: return errIncorrectBlockVersion } @@ -1200,15 +1242,13 @@ func (b *BeaconBlockBody) SyncAggregate() (*eth.SyncAggregate, error) { // Execution returns the execution payload of the block body. func (b *BeaconBlockBody) Execution() (interfaces.ExecutionData, error) { - switch b.version { - case version.Phase0, version.Altair: + if b.version <= version.Altair || b.version >= version.Gloas { return nil, consensus_types.ErrNotSupported("Execution", b.version) - default: - if b.IsBlinded() { - return b.executionPayloadHeader, nil - } - return b.executionPayload, nil } + if b.IsBlinded() { + return b.executionPayloadHeader, nil + } + return b.executionPayload, nil } func (b *BeaconBlockBody) BLSToExecutionChanges() ([]*eth.SignedBLSToExecutionChange, error) { @@ -1233,12 +1273,28 @@ func (b *BeaconBlockBody) BlobKzgCommitments() ([][]byte, error) { // ExecutionRequests returns the execution requests func (b *BeaconBlockBody) ExecutionRequests() (*enginev1.ExecutionRequests, error) { - if b.version < version.Electra { + if b.version < version.Electra || b.version >= version.Gloas { return nil, consensus_types.ErrNotSupported("ExecutionRequests", b.version) } return b.executionRequests, nil } +// PayloadAttestations returns the payload attestations in the block. +func (b *BeaconBlockBody) PayloadAttestations() ([]*eth.PayloadAttestation, error) { + if b.version >= version.Gloas { + return b.payloadAttestations, nil + } + return nil, consensus_types.ErrNotSupported("PayloadAttestations", b.version) +} + +// SignedExecutionPayloadBid returns the signed execution payload header in the block. +func (b *BeaconBlockBody) SignedExecutionPayloadBid() (*eth.SignedExecutionPayloadBid, error) { + if b.version >= version.Gloas { + return b.signedExecutionPayloadBid, nil + } + return nil, consensus_types.ErrNotSupported("SignedExecutionPayloadBid", b.version) +} + // Version returns the version of the beacon block body func (b *BeaconBlockBody) Version() int { return b.version @@ -1280,6 +1336,8 @@ func (b *BeaconBlockBody) HashTreeRoot() ([field_params.RootLength]byte, error) return pb.(*eth.BlindedBeaconBlockBodyElectra).HashTreeRoot() } return pb.(*eth.BeaconBlockBodyElectra).HashTreeRoot() + case version.Gloas: + return pb.(*eth.BeaconBlockBodyGloas).HashTreeRoot() default: return [field_params.RootLength]byte{}, errIncorrectBodyVersion } @@ -1287,5 +1345,5 @@ func (b *BeaconBlockBody) HashTreeRoot() ([field_params.RootLength]byte, error) // IsBlinded checks if the beacon block body is a blinded block body. func (b *BeaconBlockBody) IsBlinded() bool { - return b.version >= version.Bellatrix && b.executionPayload == nil + return b.version < version.Gloas && b.version >= version.Bellatrix && b.executionPayload == nil } diff --git a/consensus-types/blocks/getters_test.go b/consensus-types/blocks/getters_test.go index 6a4991f3a8..be049bc0ff 100644 --- a/consensus-types/blocks/getters_test.go +++ b/consensus-types/blocks/getters_test.go @@ -3,7 +3,9 @@ package blocks import ( "testing" + bitfield "github.com/OffchainLabs/go-bitfield" fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams" + consensus_types "github.com/OffchainLabs/prysm/v7/consensus-types" "github.com/OffchainLabs/prysm/v7/consensus-types/interfaces" "github.com/OffchainLabs/prysm/v7/consensus-types/primitives" "github.com/OffchainLabs/prysm/v7/encoding/bytesutil" @@ -73,14 +75,54 @@ func Test_SignedBeaconBlock_IsNil(t *testing.T) { } func Test_SignedBeaconBlock_Copy(t *testing.T) { - bb := &BeaconBlockBody{} - b := &BeaconBlock{body: bb} - sb := &SignedBeaconBlock{block: b} - cp, err := sb.Copy() - require.NoError(t, err) - assert.NotEqual(t, cp, sb) - assert.NotEqual(t, cp.Block(), sb.block) - assert.NotEqual(t, cp.Block().Body(), sb.block.body) + t.Run("basic", func(t *testing.T) { + bb := &BeaconBlockBody{} + b := &BeaconBlock{body: bb} + sb := &SignedBeaconBlock{block: b} + cp, err := sb.Copy() + require.NoError(t, err) + assert.NotEqual(t, cp, sb) + assert.NotEqual(t, cp.Block(), sb.block) + assert.NotEqual(t, cp.Block().Body(), sb.block.body) + }) + + t.Run("gloas deep copy", func(t *testing.T) { + payload := []*eth.PayloadAttestation{{Signature: []byte{0x01}}} + payloadBid := ð.SignedExecutionPayloadBid{Signature: []byte{0x02}} + sb := &SignedBeaconBlock{ + version: version.Gloas, + block: &BeaconBlock{ + version: version.Gloas, + body: &BeaconBlockBody{ + version: version.Gloas, + payloadAttestations: payload, + signedExecutionPayloadBid: payloadBid, + }, + }, + } + + cpIntf, err := sb.Copy() + require.NoError(t, err) + + cp, ok := cpIntf.(*SignedBeaconBlock) + require.Equal(t, true, ok) + assert.NotEqual(t, sb, cp) + require.Equal(t, version.Gloas, cp.version) + + att, err := cp.Block().Body().PayloadAttestations() + require.NoError(t, err) + require.DeepEqual(t, payload, att) + origAttSig := att[0].Signature[0] + payload[0].Signature[0] ^= 0xFF + require.Equal(t, origAttSig, att[0].Signature[0]) + + bid, err := cp.Block().Body().SignedExecutionPayloadBid() + require.NoError(t, err) + require.DeepEqual(t, payloadBid, bid) + origBidSig := bid.Signature[0] + payloadBid.Signature[0] ^= 0xFF + require.Equal(t, origBidSig, bid.Signature[0]) + }) } func Test_SignedBeaconBlock_Version(t *testing.T) { @@ -122,6 +164,16 @@ func Test_SignedBeaconBlock_Header(t *testing.T) { assert.DeepEqual(t, expectedHTR[:], h.Header.BodyRoot) } +func Test_SignedBeaconBlock_PbGenericBlockGloasUnsupported(t *testing.T) { + sb := &SignedBeaconBlock{ + version: version.Gloas, + block: &BeaconBlock{version: version.Gloas, body: &BeaconBlockBody{version: version.Gloas}}, + } + + _, err := sb.PbGenericBlock() + require.ErrorContains(t, "Gloas blocks don't support GenericSignedBeaconBlock conversion", err) +} + func Test_SignedBeaconBlock_UnmarshalSSZ(t *testing.T) { pb := hydrateSignedBeaconBlock() buf, err := pb.MarshalSSZ() @@ -190,6 +242,17 @@ func Test_BeaconBlock_IsBlinded(t *testing.T) { b1 := &SignedBeaconBlock{version: version.Bellatrix, block: &BeaconBlock{body: &BeaconBlockBody{executionPayloadHeader: executionPayloadHeader{}}}} assert.Equal(t, true, b1.IsBlinded()) + + t.Run("gloas never blinded", func(t *testing.T) { + sb := &SignedBeaconBlock{version: version.Gloas, block: &BeaconBlock{body: &BeaconBlockBody{version: version.Gloas}}} + assert.Equal(t, false, sb.IsBlinded()) + }) +} + +func Test_SignedBeaconBlock_ToBlinded_GloasUnsupported(t *testing.T) { + sb := &SignedBeaconBlock{version: version.Gloas, block: &BeaconBlock{version: version.Gloas, body: &BeaconBlockBody{version: version.Gloas}}} + _, err := sb.ToBlinded() + require.ErrorIs(t, err, ErrUnsupportedVersion) } func Test_BeaconBlock_Version(t *testing.T) { @@ -324,6 +387,46 @@ func Test_BeaconBlockBody_Deposits(t *testing.T) { assert.DeepSSZEqual(t, d, bb.Block().Body().Deposits()) } +func Test_BeaconBlockBody_PayloadAttestations(t *testing.T) { + t.Run("unsupported before gloas", func(t *testing.T) { + bb := &BeaconBlockBody{version: version.Fulu} + _, err := bb.PayloadAttestations() + require.ErrorIs(t, err, consensus_types.ErrUnsupportedField) + }) + + t.Run("gloas returns payload", func(t *testing.T) { + payload := []*eth.PayloadAttestation{{Signature: []byte{0x01}}} + sb := &SignedBeaconBlock{ + version: version.Gloas, + block: &BeaconBlock{version: version.Gloas, body: &BeaconBlockBody{version: version.Gloas}}, + } + require.NoError(t, sb.SetPayloadAttestations(payload)) + got, err := sb.Block().Body().PayloadAttestations() + require.NoError(t, err) + require.DeepEqual(t, payload, got) + }) +} + +func Test_BeaconBlockBody_SignedExecutionPayloadBid(t *testing.T) { + t.Run("unsupported before gloas", func(t *testing.T) { + bb := &BeaconBlockBody{version: version.Fulu} + _, err := bb.SignedExecutionPayloadBid() + require.ErrorIs(t, err, consensus_types.ErrUnsupportedField) + }) + + t.Run("gloas returns bid", func(t *testing.T) { + bid := ð.SignedExecutionPayloadBid{Signature: []byte{0xFF}} + sb := &SignedBeaconBlock{ + version: version.Gloas, + block: &BeaconBlock{version: version.Gloas, body: &BeaconBlockBody{version: version.Gloas}}, + } + require.NoError(t, sb.SetSignedExecutionPayloadBid(bid)) + got, err := sb.Block().Body().SignedExecutionPayloadBid() + require.NoError(t, err) + require.DeepEqual(t, bid, got) + }) +} + func Test_BeaconBlockBody_VoluntaryExits(t *testing.T) { ve := make([]*eth.SignedVoluntaryExit, 0) bb := &SignedBeaconBlock{block: &BeaconBlock{body: &BeaconBlockBody{}}} @@ -400,6 +503,32 @@ func Test_BeaconBlockBody_Execution(t *testing.T) { gas, err = eDenebHeader.ExcessBlobGas() require.NoError(t, err) require.DeepEqual(t, gas, uint64(223)) + + bb = &SignedBeaconBlock{version: version.Gloas, block: &BeaconBlock{version: version.Gloas, body: &BeaconBlockBody{version: version.Gloas}}} + _, err = bb.Block().Body().Execution() + require.ErrorIs(t, err, consensus_types.ErrUnsupportedField) +} + +func Test_BeaconBlockBody_ExecutionRequests(t *testing.T) { + t.Run("unsupported before Electra", func(t *testing.T) { + bb := &BeaconBlockBody{version: version.Deneb} + _, err := bb.ExecutionRequests() + require.ErrorIs(t, err, consensus_types.ErrUnsupportedField) + }) + + t.Run("electra returns requests", func(t *testing.T) { + reqs := &pb.ExecutionRequests{} + bb := &BeaconBlockBody{version: version.Electra, executionRequests: reqs} + result, err := bb.ExecutionRequests() + require.NoError(t, err) + require.Equal(t, reqs, result) + }) + + t.Run("unsupported for Gloas", func(t *testing.T) { + bb := &BeaconBlockBody{version: version.Gloas} + _, err := bb.ExecutionRequests() + require.ErrorIs(t, err, consensus_types.ErrUnsupportedField) + }) } func Test_BeaconBlockBody_HashTreeRoot(t *testing.T) { @@ -413,6 +542,17 @@ func Test_BeaconBlockBody_HashTreeRoot(t *testing.T) { assert.DeepEqual(t, expectedHTR, actualHTR) } +func Test_BeaconBlockBody_HashTreeRootGloas(t *testing.T) { + pb := hydrateBeaconBlockBodyGloas() + expectedHTR, err := pb.HashTreeRoot() + require.NoError(t, err) + b, err := initBlockBodyFromProtoGloas(pb) + require.NoError(t, err) + actualHTR, err := b.HashTreeRoot() + require.NoError(t, err) + assert.DeepEqual(t, expectedHTR, actualHTR) +} + func hydrateSignedBeaconBlock() *eth.SignedBeaconBlock { return ð.SignedBeaconBlock{ Signature: make([]byte, fieldparams.BLSSignatureLength), @@ -509,6 +649,43 @@ func hydrateBeaconBlockBodyCapella() *eth.BeaconBlockBodyCapella { } } +func hydrateBeaconBlockBodyGloas() *eth.BeaconBlockBodyGloas { + bits := bitfield.NewBitvector512() + bits.SetBitAt(0, true) + + return ð.BeaconBlockBodyGloas{ + RandaoReveal: make([]byte, fieldparams.BLSSignatureLength), + Graffiti: make([]byte, fieldparams.RootLength), + Eth1Data: ð.Eth1Data{ + DepositRoot: make([]byte, fieldparams.RootLength), + BlockHash: make([]byte, fieldparams.RootLength), + }, + SyncAggregate: ð.SyncAggregate{ + SyncCommitteeBits: make([]byte, fieldparams.SyncAggregateSyncCommitteeBytesLength), + SyncCommitteeSignature: make([]byte, fieldparams.BLSSignatureLength), + }, + SignedExecutionPayloadBid: ð.SignedExecutionPayloadBid{ + Message: ð.ExecutionPayloadBid{ + ParentBlockHash: make([]byte, fieldparams.RootLength), + ParentBlockRoot: make([]byte, fieldparams.RootLength), + BlockHash: make([]byte, fieldparams.RootLength), + FeeRecipient: make([]byte, 20), + BlobKzgCommitmentsRoot: make([]byte, fieldparams.RootLength), + }, + Signature: make([]byte, fieldparams.BLSSignatureLength), + }, + PayloadAttestations: []*eth.PayloadAttestation{ + { + AggregationBits: bits, + Data: ð.PayloadAttestationData{ + BeaconBlockRoot: make([]byte, fieldparams.RootLength), + }, + Signature: make([]byte, fieldparams.BLSSignatureLength), + }, + }, + } +} + func hydrateBeaconBlockBodyDeneb() *eth.BeaconBlockBodyDeneb { return ð.BeaconBlockBodyDeneb{ RandaoReveal: make([]byte, fieldparams.BLSSignatureLength), diff --git a/consensus-types/blocks/proto.go b/consensus-types/blocks/proto.go index 4c3bc95645..4c8f7986b1 100644 --- a/consensus-types/blocks/proto.go +++ b/consensus-types/blocks/proto.go @@ -185,6 +185,19 @@ func (b *SignedBeaconBlock) Proto() (proto.Message, error) { // nolint:gocognit Block: block, Signature: b.signature[:], }, nil + case version.Gloas: + var block *eth.BeaconBlockGloas + if blockMessage != nil { + var ok bool + block, ok = blockMessage.(*eth.BeaconBlockGloas) + if !ok { + return nil, errIncorrectBlockVersion + } + } + return ð.SignedBeaconBlockGloas{ + Block: block, + Signature: b.signature[:], + }, nil default: return nil, errors.New("unsupported signed beacon block version") } @@ -399,6 +412,22 @@ func (b *BeaconBlock) Proto() (proto.Message, error) { // nolint:gocognit StateRoot: b.stateRoot[:], Body: body, }, nil + case version.Gloas: + var body *eth.BeaconBlockBodyGloas + if bodyMessage != nil { + var ok bool + body, ok = bodyMessage.(*eth.BeaconBlockBodyGloas) + if !ok { + return nil, errIncorrectBodyVersion + } + } + return ð.BeaconBlockGloas{ + Slot: b.slot, + ProposerIndex: b.proposerIndex, + ParentRoot: b.parentRoot[:], + StateRoot: b.stateRoot[:], + Body: body, + }, nil default: return nil, fmt.Errorf("unsupported beacon block version: %s", version.String(b.version)) } @@ -668,6 +697,21 @@ func (b *BeaconBlockBody) Proto() (proto.Message, error) { BlobKzgCommitments: b.blobKzgCommitments, ExecutionRequests: b.executionRequests, }, nil + case version.Gloas: + return ð.BeaconBlockBodyGloas{ + RandaoReveal: b.randaoReveal[:], + Eth1Data: b.eth1Data, + Graffiti: b.graffiti[:], + ProposerSlashings: b.proposerSlashings, + AttesterSlashings: b.attesterSlashingsElectra, + Attestations: b.attestationsElectra, + Deposits: b.deposits, + VoluntaryExits: b.voluntaryExits, + SyncAggregate: b.syncAggregate, + BlsToExecutionChanges: b.blsToExecutionChanges, + SignedExecutionPayloadBid: b.signedExecutionPayloadBid, + PayloadAttestations: b.payloadAttestations, + }, nil default: return nil, errors.New("unsupported beacon block body version") } @@ -1477,3 +1521,67 @@ func initBlindedBlockBodyFromProtoFulu(pb *eth.BlindedBeaconBlockBodyElectra) (* } return b, nil } + +// ---------------------------------------------------------------------------- +// Gloas +// ---------------------------------------------------------------------------- + +func initSignedBlockFromProtoGloas(pb *eth.SignedBeaconBlockGloas) (*SignedBeaconBlock, error) { + if pb == nil { + return nil, errNilBlock + } + + block, err := initBlockFromProtoGloas(pb.Block) + if err != nil { + return nil, err + } + b := &SignedBeaconBlock{ + version: version.Gloas, + block: block, + signature: bytesutil.ToBytes96(pb.Signature), + } + return b, nil +} + +func initBlockFromProtoGloas(pb *eth.BeaconBlockGloas) (*BeaconBlock, error) { + if pb == nil { + return nil, errNilBlock + } + + body, err := initBlockBodyFromProtoGloas(pb.Body) + if err != nil { + return nil, err + } + b := &BeaconBlock{ + version: version.Gloas, + slot: pb.Slot, + proposerIndex: pb.ProposerIndex, + parentRoot: bytesutil.ToBytes32(pb.ParentRoot), + stateRoot: bytesutil.ToBytes32(pb.StateRoot), + body: body, + } + return b, nil +} + +func initBlockBodyFromProtoGloas(pb *eth.BeaconBlockBodyGloas) (*BeaconBlockBody, error) { + if pb == nil { + return nil, errNilBlockBody + } + + b := &BeaconBlockBody{ + version: version.Gloas, + randaoReveal: bytesutil.ToBytes96(pb.RandaoReveal), + eth1Data: pb.Eth1Data, + graffiti: bytesutil.ToBytes32(pb.Graffiti), + proposerSlashings: pb.ProposerSlashings, + attesterSlashingsElectra: pb.AttesterSlashings, + attestationsElectra: pb.Attestations, + deposits: pb.Deposits, + voluntaryExits: pb.VoluntaryExits, + syncAggregate: pb.SyncAggregate, + blsToExecutionChanges: pb.BlsToExecutionChanges, + signedExecutionPayloadBid: pb.SignedExecutionPayloadBid, + payloadAttestations: pb.PayloadAttestations, + } + return b, nil +} diff --git a/consensus-types/blocks/proto_test.go b/consensus-types/blocks/proto_test.go index d775fc5f1b..69fbaef89c 100644 --- a/consensus-types/blocks/proto_test.go +++ b/consensus-types/blocks/proto_test.go @@ -1792,6 +1792,61 @@ func bodyBlindedElectra(t *testing.T) *BeaconBlockBody { } } +func TestSignedBeaconBlockProtoGloas(t *testing.T) { + payload := []*eth.PayloadAttestation{{Signature: []byte{0x01}}} + bid := ð.SignedExecutionPayloadBid{Signature: []byte{0x02}} + sb := &SignedBeaconBlock{ + version: version.Gloas, + block: &BeaconBlock{ + version: version.Gloas, + body: &BeaconBlockBody{ + version: version.Gloas, + payloadAttestations: payload, + signedExecutionPayloadBid: bid, + }, + }, + } + + msg, err := sb.Proto() + require.NoError(t, err) + gloas, ok := msg.(*eth.SignedBeaconBlockGloas) + require.Equal(t, true, ok) + require.DeepEqual(t, payload, gloas.Block.Body.PayloadAttestations) + require.DeepEqual(t, bid, gloas.Block.Body.SignedExecutionPayloadBid) +} + +func TestInitSignedBlockFromProtoGloas(t *testing.T) { + bits := bitfield.NewBitvector512() + bits.SetBitAt(0, true) + pb := ð.SignedBeaconBlockGloas{ + Block: ð.BeaconBlockGloas{ + Body: ð.BeaconBlockBodyGloas{ + PayloadAttestations: []*eth.PayloadAttestation{ + { + AggregationBits: bits, + Signature: []byte{0x01}, + }, + }, + SignedExecutionPayloadBid: ð.SignedExecutionPayloadBid{Signature: []byte{0x02}}, + }, + }, + Signature: []byte{0x03}, + } + + sb, err := initSignedBlockFromProtoGloas(pb) + require.NoError(t, err) + require.Equal(t, version.Gloas, sb.Version()) + + gotPayload, err := sb.Block().Body().PayloadAttestations() + require.NoError(t, err) + require.Equal(t, 1, len(gotPayload)) + require.DeepEqual(t, pb.Block.Body.PayloadAttestations, gotPayload) + + gotBid, err := sb.Block().Body().SignedExecutionPayloadBid() + require.NoError(t, err) + require.DeepEqual(t, pb.Block.Body.SignedExecutionPayloadBid, gotBid) +} + func getFields() fields { b20 := make([]byte, 20) b48 := make([]byte, 48) diff --git a/consensus-types/blocks/setters.go b/consensus-types/blocks/setters.go index 5df0b2517e..dcc03b78f6 100644 --- a/consensus-types/blocks/setters.go +++ b/consensus-types/blocks/setters.go @@ -144,7 +144,7 @@ func (b *SignedBeaconBlock) SetSyncAggregate(s *eth.SyncAggregate) error { // SetExecution sets the execution payload of the block body. // This function is not thread safe, it is only used during block creation. func (b *SignedBeaconBlock) SetExecution(e interfaces.ExecutionData) error { - if b.version == version.Phase0 || b.version == version.Altair { + if b.version == version.Phase0 || b.version == version.Altair || b.version >= version.Gloas { return consensus_types.ErrNotSupported("Execution", b.version) } if e.IsBlinded() { @@ -176,9 +176,27 @@ func (b *SignedBeaconBlock) SetBlobKzgCommitments(c [][]byte) error { // SetExecutionRequests sets the execution requests in the block. func (b *SignedBeaconBlock) SetExecutionRequests(req *enginev1.ExecutionRequests) error { - if b.version < version.Electra { + if b.version < version.Electra || b.version >= version.Gloas { return consensus_types.ErrNotSupported("SetExecutionRequests", b.version) } b.block.body.executionRequests = req return nil } + +// SetPayloadAttestations sets the payload attestations in the block. +func (b *SignedBeaconBlock) SetPayloadAttestations(pa []*eth.PayloadAttestation) error { + if b.version < version.Gloas { + return consensus_types.ErrNotSupported("SetPayloadAttestations", b.version) + } + b.block.body.payloadAttestations = pa + return nil +} + +// SetSignedExecutionPayloadBid sets the signed execution payload header in the block. +func (b *SignedBeaconBlock) SetSignedExecutionPayloadBid(header *eth.SignedExecutionPayloadBid) error { + if b.version < version.Gloas { + return consensus_types.ErrNotSupported("SetSignedExecutionPayloadBid", b.version) + } + b.block.body.signedExecutionPayloadBid = header + return nil +} diff --git a/consensus-types/blocks/setters_test.go b/consensus-types/blocks/setters_test.go new file mode 100644 index 0000000000..746cedf175 --- /dev/null +++ b/consensus-types/blocks/setters_test.go @@ -0,0 +1,107 @@ +package blocks + +import ( + "testing" + + bitfield "github.com/OffchainLabs/go-bitfield" + consensus_types "github.com/OffchainLabs/prysm/v7/consensus-types" + 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/require" +) + +func TestSignedBeaconBlock_SetPayloadAttestations(t *testing.T) { + t.Run("rejects pre-Gloas versions", func(t *testing.T) { + sb := newTestSignedBeaconBlock(version.Fulu) + payload := []*eth.PayloadAttestation{{}} + + err := sb.SetPayloadAttestations(payload) + + require.ErrorIs(t, err, consensus_types.ErrUnsupportedField) + require.IsNil(t, sb.block.body.payloadAttestations) + }) + + t.Run("sets payload attestations for Gloas", func(t *testing.T) { + sb := newTestSignedBeaconBlock(version.Gloas) + payload := []*eth.PayloadAttestation{ + { + AggregationBits: bitfield.NewBitvector512(), + Data: ð.PayloadAttestationData{ + BeaconBlockRoot: []byte{0x01, 0x02}, + PayloadPresent: true, + BlobDataAvailable: true, + }, + Signature: []byte{0x03}, + }, + } + + err := sb.SetPayloadAttestations(payload) + + require.NoError(t, err) + require.DeepEqual(t, payload, sb.block.body.payloadAttestations) + }) +} + +func TestSignedBeaconBlock_SetSignedExecutionPayloadBid(t *testing.T) { + t.Run("rejects pre-Gloas versions", func(t *testing.T) { + sb := newTestSignedBeaconBlock(version.Fulu) + payloadBid := ð.SignedExecutionPayloadBid{} + + err := sb.SetSignedExecutionPayloadBid(payloadBid) + + require.ErrorIs(t, err, consensus_types.ErrUnsupportedField) + require.IsNil(t, sb.block.body.signedExecutionPayloadBid) + }) + + t.Run("sets signed execution payload bid for Gloas", func(t *testing.T) { + sb := newTestSignedBeaconBlock(version.Gloas) + payloadBid := ð.SignedExecutionPayloadBid{ + Message: ð.ExecutionPayloadBid{ + ParentBlockHash: []byte{0xaa}, + BlockHash: []byte{0xbb}, + FeeRecipient: []byte{0xcc}, + }, + Signature: []byte{0xdd}, + } + + err := sb.SetSignedExecutionPayloadBid(payloadBid) + + require.NoError(t, err) + require.Equal(t, payloadBid, sb.block.body.signedExecutionPayloadBid) + }) +} + +func TestSignedBeaconBlock_SetExecution(t *testing.T) { + t.Run("rejects Gloas version", func(t *testing.T) { + sb := newTestSignedBeaconBlock(version.Gloas) + payload := &enginev1.ExecutionPayload{} + wrapped, err := WrappedExecutionPayload(payload) + require.NoError(t, err) + + err = sb.SetExecution(wrapped) + require.ErrorIs(t, err, consensus_types.ErrUnsupportedField) + }) +} + +func TestSignedBeaconBlock_SetExecutionRequests(t *testing.T) { + t.Run("rejects Gloas version", func(t *testing.T) { + sb := newTestSignedBeaconBlock(version.Gloas) + requests := &enginev1.ExecutionRequests{} + + err := sb.SetExecutionRequests(requests) + require.ErrorIs(t, err, consensus_types.ErrUnsupportedField) + }) +} + +func newTestSignedBeaconBlock(ver int) *SignedBeaconBlock { + return &SignedBeaconBlock{ + version: ver, + block: &BeaconBlock{ + version: ver, + body: &BeaconBlockBody{ + version: ver, + }, + }, + } +} diff --git a/consensus-types/blocks/types.go b/consensus-types/blocks/types.go index 2cd90e6a19..f67ef35d73 100644 --- a/consensus-types/blocks/types.go +++ b/consensus-types/blocks/types.go @@ -40,23 +40,25 @@ var ( // BeaconBlockBody is the main beacon block body structure. It can represent any block type. type BeaconBlockBody struct { - version int - randaoReveal [field_params.BLSSignatureLength]byte - eth1Data *eth.Eth1Data - graffiti [field_params.RootLength]byte - proposerSlashings []*eth.ProposerSlashing - attesterSlashings []*eth.AttesterSlashing - attesterSlashingsElectra []*eth.AttesterSlashingElectra - attestations []*eth.Attestation - attestationsElectra []*eth.AttestationElectra - deposits []*eth.Deposit - voluntaryExits []*eth.SignedVoluntaryExit - syncAggregate *eth.SyncAggregate - executionPayload interfaces.ExecutionData - executionPayloadHeader interfaces.ExecutionData - blsToExecutionChanges []*eth.SignedBLSToExecutionChange - blobKzgCommitments [][]byte - executionRequests *enginev1.ExecutionRequests + version int + randaoReveal [field_params.BLSSignatureLength]byte + eth1Data *eth.Eth1Data + graffiti [field_params.RootLength]byte + proposerSlashings []*eth.ProposerSlashing + attesterSlashings []*eth.AttesterSlashing + attesterSlashingsElectra []*eth.AttesterSlashingElectra + attestations []*eth.Attestation + attestationsElectra []*eth.AttestationElectra + deposits []*eth.Deposit + voluntaryExits []*eth.SignedVoluntaryExit + syncAggregate *eth.SyncAggregate + executionPayload interfaces.ExecutionData + executionPayloadHeader interfaces.ExecutionData + blsToExecutionChanges []*eth.SignedBLSToExecutionChange + blobKzgCommitments [][]byte + executionRequests *enginev1.ExecutionRequests + signedExecutionPayloadBid *eth.SignedExecutionPayloadBid + payloadAttestations []*eth.PayloadAttestation } var _ interfaces.ReadOnlyBeaconBlockBody = &BeaconBlockBody{} diff --git a/consensus-types/interfaces/beacon_block.go b/consensus-types/interfaces/beacon_block.go index a48a8db955..5b62c54c4e 100644 --- a/consensus-types/interfaces/beacon_block.go +++ b/consensus-types/interfaces/beacon_block.go @@ -69,6 +69,8 @@ type ReadOnlyBeaconBlockBody interface { BLSToExecutionChanges() ([]*ethpb.SignedBLSToExecutionChange, error) BlobKzgCommitments() ([][]byte, error) ExecutionRequests() (*enginev1.ExecutionRequests, error) + PayloadAttestations() ([]*ethpb.PayloadAttestation, error) + SignedExecutionPayloadBid() (*ethpb.SignedExecutionPayloadBid, error) } type SignedBeaconBlock interface { @@ -91,6 +93,8 @@ type SignedBeaconBlock interface { SetSlot(slot primitives.Slot) SetSignature(sig []byte) SetExecutionRequests(er *enginev1.ExecutionRequests) error + SetPayloadAttestations(pa []*ethpb.PayloadAttestation) error + SetSignedExecutionPayloadBid(header *ethpb.SignedExecutionPayloadBid) error Unblind(e ExecutionData) error } diff --git a/consensus-types/mock/block.go b/consensus-types/mock/block.go index da5507067f..15f72a6f2e 100644 --- a/consensus-types/mock/block.go +++ b/consensus-types/mock/block.go @@ -276,6 +276,14 @@ func (b *BeaconBlockBody) ExecutionRequests() (*enginev1.ExecutionRequests, erro panic("implement me") } +func (b *BeaconBlockBody) PayloadAttestations() ([]*eth.PayloadAttestation, error) { + panic("implement me") +} + +func (b *BeaconBlockBody) SignedExecutionPayloadBid() (*eth.SignedExecutionPayloadBid, error) { + panic("implement me") +} + func (b *BeaconBlockBody) Attestations() []eth.Att { panic("implement me") } diff --git a/proto/prysm/v1alpha1/cloners.go b/proto/prysm/v1alpha1/cloners.go index 436196cc2f..a8754f0400 100644 --- a/proto/prysm/v1alpha1/cloners.go +++ b/proto/prysm/v1alpha1/cloners.go @@ -61,3 +61,116 @@ func CopySyncCommitteeContribution(c *SyncCommitteeContribution) *SyncCommitteeC Signature: bytesutil.SafeCopyBytes(c.Signature), } } + +// CopySignedBeaconBlockGloas copies the provided signed beacon block Gloas object. +func CopySignedBeaconBlockGloas(sb *SignedBeaconBlockGloas) *SignedBeaconBlockGloas { + if sb == nil { + return nil + } + return &SignedBeaconBlockGloas{ + Block: copyBeaconBlockGloas(sb.Block), + Signature: bytesutil.SafeCopyBytes(sb.Signature), + } +} + +// copyBeaconBlockGloas copies the provided beacon block Gloas object. +func copyBeaconBlockGloas(b *BeaconBlockGloas) *BeaconBlockGloas { + if b == nil { + return nil + } + return &BeaconBlockGloas{ + Slot: b.Slot, + ProposerIndex: b.ProposerIndex, + ParentRoot: bytesutil.SafeCopyBytes(b.ParentRoot), + StateRoot: bytesutil.SafeCopyBytes(b.StateRoot), + Body: copyBeaconBlockBodyGloas(b.Body), + } +} + +// copyPayloadAttestation copies the provided payload attestation object. +func copyPayloadAttestation(pa *PayloadAttestation) *PayloadAttestation { + if pa == nil { + return nil + } + copied := &PayloadAttestation{ + AggregationBits: pa.AggregationBits, + Signature: bytesutil.SafeCopyBytes(pa.Signature), + } + if pa.Data != nil { + copied.Data = &PayloadAttestationData{ + BeaconBlockRoot: bytesutil.SafeCopyBytes(pa.Data.BeaconBlockRoot), + Slot: pa.Data.Slot, + PayloadPresent: pa.Data.PayloadPresent, + BlobDataAvailable: pa.Data.BlobDataAvailable, + } + } + return copied +} + +// copyPayloadAttestations copies a slice of payload attestations. +func copyPayloadAttestations(pas []*PayloadAttestation) []*PayloadAttestation { + if len(pas) == 0 { + return nil + } + copied := make([]*PayloadAttestation, len(pas)) + for i, pa := range pas { + copied[i] = copyPayloadAttestation(pa) + } + return copied +} + +// copySignedExecutionPayloadBid copies the provided signed execution payload header. +func copySignedExecutionPayloadBid(header *SignedExecutionPayloadBid) *SignedExecutionPayloadBid { + if header == nil { + return nil + } + copied := &SignedExecutionPayloadBid{ + Signature: bytesutil.SafeCopyBytes(header.Signature), + } + if header.Message != nil { + copied.Message = &ExecutionPayloadBid{ + ParentBlockHash: bytesutil.SafeCopyBytes(header.Message.ParentBlockHash), + ParentBlockRoot: bytesutil.SafeCopyBytes(header.Message.ParentBlockRoot), + BlockHash: bytesutil.SafeCopyBytes(header.Message.BlockHash), + FeeRecipient: bytesutil.SafeCopyBytes(header.Message.FeeRecipient), + GasLimit: header.Message.GasLimit, + BuilderIndex: header.Message.BuilderIndex, + Slot: header.Message.Slot, + Value: header.Message.Value, + BlobKzgCommitmentsRoot: bytesutil.SafeCopyBytes(header.Message.BlobKzgCommitmentsRoot), + } + } + return copied +} + +// copyBeaconBlockBodyGloas copies the provided beacon block body Gloas object. +func copyBeaconBlockBodyGloas(body *BeaconBlockBodyGloas) *BeaconBlockBodyGloas { + if body == nil { + return nil + } + + copied := &BeaconBlockBodyGloas{ + RandaoReveal: bytesutil.SafeCopyBytes(body.RandaoReveal), + Graffiti: bytesutil.SafeCopyBytes(body.Graffiti), + } + + if body.Eth1Data != nil { + copied.Eth1Data = body.Eth1Data.Copy() + } + + if body.SyncAggregate != nil { + copied.SyncAggregate = body.SyncAggregate.Copy() + } + + copied.ProposerSlashings = CopySlice(body.ProposerSlashings) + copied.AttesterSlashings = CopySlice(body.AttesterSlashings) + copied.Attestations = CopySlice(body.Attestations) + copied.Deposits = CopySlice(body.Deposits) + copied.VoluntaryExits = CopySlice(body.VoluntaryExits) + copied.BlsToExecutionChanges = CopySlice(body.BlsToExecutionChanges) + + copied.SignedExecutionPayloadBid = copySignedExecutionPayloadBid(body.SignedExecutionPayloadBid) + copied.PayloadAttestations = copyPayloadAttestations(body.PayloadAttestations) + + return copied +} diff --git a/proto/prysm/v1alpha1/cloners_test.go b/proto/prysm/v1alpha1/cloners_test.go index a6e9975bc9..78175a7fe6 100644 --- a/proto/prysm/v1alpha1/cloners_test.go +++ b/proto/prysm/v1alpha1/cloners_test.go @@ -5,9 +5,12 @@ import ( "reflect" "testing" + bitfield "github.com/OffchainLabs/go-bitfield" + "github.com/OffchainLabs/prysm/v7/consensus-types/primitives" enginev1 "github.com/OffchainLabs/prysm/v7/proto/engine/v1" v1alpha1 "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1" "github.com/OffchainLabs/prysm/v7/testing/assert" + "google.golang.org/protobuf/proto" ) func TestCopySignedBeaconBlock(t *testing.T) { @@ -100,6 +103,49 @@ func TestCopySyncCommitteeContribution(t *testing.T) { assert.NotEmpty(t, got, "Copied sync committee contribution has empty fields") } +func TestCopySignedBeaconBlockGloasNil(t *testing.T) { + if got := v1alpha1.CopySignedBeaconBlockGloas(nil); got != nil { + t.Fatalf("CopySignedBeaconBlockGloas(nil) = %v, want nil", got) + } +} + +func TestCopySignedBeaconBlockGloasDeepCopy(t *testing.T) { + original := genSignedBeaconBlockGloas() + + copied := v1alpha1.CopySignedBeaconBlockGloas(original) + if copied == original { + t.Fatalf("CopySignedBeaconBlockGloas returned the original pointer") + } + if !reflect.DeepEqual(copied, original) { + t.Fatalf("CopySignedBeaconBlockGloas() = %v, want %v", copied, original) + } + + want := proto.Clone(copied).(*v1alpha1.SignedBeaconBlockGloas) + + original.Signature[0] ^= 0xFF + original.Block.ParentRoot[0] ^= 0xFF + original.Block.StateRoot[0] ^= 0xFF + original.Block.Body.RandaoReveal[0] ^= 0xFF + original.Block.Body.Graffiti[0] ^= 0xFF + original.Block.Body.SyncAggregate.SyncCommitteeSignature[0] ^= 0xFF + original.Block.Body.SignedExecutionPayloadBid.Signature[0] ^= 0xFF + original.Block.Body.SignedExecutionPayloadBid.Message.BlockHash[0] ^= 0xFF + original.Block.Body.PayloadAttestations[0].Signature[0] ^= 0xFF + original.Block.Body.PayloadAttestations[0].Data.BeaconBlockRoot[0] ^= 0xFF + original.Block.Body.PayloadAttestations = append(original.Block.Body.PayloadAttestations, &v1alpha1.PayloadAttestation{}) + original.Block.Body.BlsToExecutionChanges[0].Message.ValidatorIndex++ + + if !reflect.DeepEqual(want, copied) { + t.Fatalf("copy mutated after modifying source: got %v, want %v", copied, want) + } + if copied.Block == original.Block { + t.Fatal("expected copied block pointer to differ from original") + } + if copied.Block.Body == original.Block.Body { + t.Fatal("expected copied block body pointer to differ from original") + } +} + func TestCopyPendingAttestationSlice(t *testing.T) { tests := []struct { name string @@ -1125,3 +1171,78 @@ func genConsolidationRequest() *enginev1.ConsolidationRequest { TargetPubkey: bytes(48), } } + +func genSignedBeaconBlockGloas() *v1alpha1.SignedBeaconBlockGloas { + return &v1alpha1.SignedBeaconBlockGloas{ + Block: genBeaconBlockGloas(), + Signature: bytes(96), + } +} + +func genBeaconBlockGloas() *v1alpha1.BeaconBlockGloas { + return &v1alpha1.BeaconBlockGloas{ + Slot: primitives.Slot(rand.Uint64()), + ProposerIndex: primitives.ValidatorIndex(rand.Uint64()), + ParentRoot: bytes(32), + StateRoot: bytes(32), + Body: genBeaconBlockBodyGloas(), + } +} + +func genBeaconBlockBodyGloas() *v1alpha1.BeaconBlockBodyGloas { + return &v1alpha1.BeaconBlockBodyGloas{ + RandaoReveal: bytes(96), + Eth1Data: genEth1Data(), + Graffiti: bytes(32), + ProposerSlashings: genProposerSlashings(3), + AttesterSlashings: genAttesterSlashingsElectra(3), + Attestations: genAttestationsElectra(3), + Deposits: genDeposits(3), + VoluntaryExits: genSignedVoluntaryExits(3), + SyncAggregate: genSyncAggregate(), + BlsToExecutionChanges: genBLSToExecutionChanges(2), + SignedExecutionPayloadBid: genSignedExecutionPayloadBidGloas(), + PayloadAttestations: genPayloadAttestations(2), + } +} + +func genSignedExecutionPayloadBidGloas() *v1alpha1.SignedExecutionPayloadBid { + return &v1alpha1.SignedExecutionPayloadBid{ + Message: genExecutionPayloadBidGloas(), + Signature: bytes(96), + } +} + +func genExecutionPayloadBidGloas() *v1alpha1.ExecutionPayloadBid { + return &v1alpha1.ExecutionPayloadBid{ + ParentBlockHash: bytes(32), + ParentBlockRoot: bytes(32), + BlockHash: bytes(32), + FeeRecipient: bytes(20), + GasLimit: rand.Uint64(), + BuilderIndex: primitives.ValidatorIndex(rand.Uint64()), + Slot: primitives.Slot(rand.Uint64()), + Value: primitives.Gwei(rand.Uint64()), + BlobKzgCommitmentsRoot: bytes(32), + } +} + +func genPayloadAttestations(num int) []*v1alpha1.PayloadAttestation { + pas := make([]*v1alpha1.PayloadAttestation, num) + for i := range pas { + bits := bitfield.NewBitvector512() + bits.SetBitAt(uint64(i%512), true) + + pas[i] = &v1alpha1.PayloadAttestation{ + AggregationBits: bits, + Data: &v1alpha1.PayloadAttestationData{ + BeaconBlockRoot: bytes(32), + Slot: primitives.Slot(rand.Uint64()), + PayloadPresent: i%2 == 0, + BlobDataAvailable: i%2 == 1, + }, + Signature: bytes(96), + } + } + return pas +}