diff --git a/beacon-chain/core/blocks/payload.go b/beacon-chain/core/blocks/payload.go index d7d68f223c..0ac10bccc2 100644 --- a/beacon-chain/core/blocks/payload.go +++ b/beacon-chain/core/blocks/payload.go @@ -47,6 +47,23 @@ func IsMergeBlock(st state.BeaconState, blk block.BeaconBlockBody) (bool, error) return !isEmptyPayload(payload), nil } +// ExecutionEnabled returns true if the beacon chain can begin executing. +// Meaning the payload header is beacon state is non-empty or the payload in block body is non-empty. +// +// Spec code: +// def is_execution_enabled(state: BeaconState, body: BeaconBlockBody) -> bool: +// return is_merge_block(state, body) or is_merge_complete(state) +func ExecutionEnabled(st state.BeaconState, blk block.BeaconBlockBody) (bool, error) { + mergeBlock, err := IsMergeBlock(st, blk) + if err != nil { + return false, err + } + if mergeBlock { + return true, nil + } + return MergeComplete(st) +} + func isEmptyPayload(p *ethpb.ExecutionPayload) bool { if !bytes.Equal(p.ParentHash, make([]byte, fieldparams.RootLength)) { return false diff --git a/beacon-chain/core/blocks/payload_test.go b/beacon-chain/core/blocks/payload_test.go index 830ed08c49..11924552a9 100644 --- a/beacon-chain/core/blocks/payload_test.go +++ b/beacon-chain/core/blocks/payload_test.go @@ -344,6 +344,71 @@ func Test_MergeBlock(t *testing.T) { } } +func Test_ExecutionEnabled(t *testing.T) { + tests := []struct { + name string + payload *ethpb.ExecutionPayload + header *ethpb.ExecutionPayloadHeader + want bool + }{ + { + name: "empty header, empty payload", + payload: emptyPayload(), + header: emptyPayloadHeader(), + want: false, + }, + { + name: "non-empty header, empty payload", + payload: emptyPayload(), + header: func() *ethpb.ExecutionPayloadHeader { + h := emptyPayloadHeader() + h.ParentHash = bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength) + return h + }(), + want: true, + }, + { + name: "empty header, non-empty payload", + header: emptyPayloadHeader(), + payload: func() *ethpb.ExecutionPayload { + p := emptyPayload() + p.Timestamp = 1 + return p + }(), + want: true, + }, + { + name: "non-empty header, non-empty payload", + header: func() *ethpb.ExecutionPayloadHeader { + h := emptyPayloadHeader() + h.ParentHash = bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength) + return h + }(), + payload: func() *ethpb.ExecutionPayload { + p := emptyPayload() + p.Timestamp = 1 + return p + }(), + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + st, _ := util.DeterministicGenesisStateMerge(t, 1) + require.NoError(t, st.SetLatestExecutionPayloadHeader(tt.header)) + blk := util.NewBeaconBlockMerge() + blk.Block.Body.ExecutionPayload = tt.payload + body, err := wrapper.WrappedMergeBeaconBlockBody(blk.Block.Body) + require.NoError(t, err) + got, err := blocks.ExecutionEnabled(st, body) + require.NoError(t, err) + if got != tt.want { + t.Errorf("ExecutionEnabled() got = %v, want %v", got, tt.want) + } + }) + } +} + func BenchmarkMergeComplete(b *testing.B) { st, _ := util.DeterministicGenesisStateMerge(b, 1) require.NoError(b, st.SetLatestExecutionPayloadHeader(emptyPayloadHeader()))