From 3ce26ae7e2ac917ae76043b06cd0a0f3b05249a0 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Sun, 17 Apr 2022 00:02:59 -0700 Subject: [PATCH] Add process payload header (#10529) * Add process payload header * Update block.go * Remove unused receivers * Use errors vars * Add some comments * Add some comments --- beacon-chain/core/blocks/payload.go | 69 +++++++- beacon-chain/core/blocks/payload_test.go | 152 +++++++++++++++++- .../transition/transition_no_verify_sig.go | 25 ++- .../prysm/v1alpha1/block/block_interfaces.go | 1 + proto/prysm/v1alpha1/block/mock/block.go | 4 + .../v1alpha1/wrapper/beacon_block_altair.go | 5 + .../wrapper/beacon_block_altair_test.go | 6 + .../wrapper/beacon_block_bellatrix.go | 5 + .../wrapper/beacon_block_bellatrix_test.go | 6 + .../v1alpha1/wrapper/beacon_block_phase0.go | 5 + .../wrapper/blinded_beacon_block_bellatrix.go | 5 + .../blinded_beacon_block_bellatrix_test.go | 7 + 12 files changed, 277 insertions(+), 13 deletions(-) diff --git a/beacon-chain/core/blocks/payload.go b/beacon-chain/core/blocks/payload.go index c5e6e6255c..34c6fc755e 100644 --- a/beacon-chain/core/blocks/payload.go +++ b/beacon-chain/core/blocks/payload.go @@ -18,6 +18,12 @@ import ( "github.com/prysmaticlabs/prysm/time/slots" ) +var ( + ErrInvalidPayloadBlockHash = errors.New("invalid payload block hash") + ErrInvalidPayloadTimeStamp = errors.New("invalid payload timestamp") + ErrInvalidPayloadPrevRandao = errors.New("invalid payload previous randao") +) + // IsMergeTransitionComplete returns true if the transition to Bellatrix has completed. // Meaning the payload header in beacon state is not `ExecutionPayloadHeader()` (i.e. not empty). // @@ -146,14 +152,14 @@ func ValidatePayload(st state.BeaconState, payload *enginev1.ExecutionPayload) e } if !bytes.Equal(payload.PrevRandao, random) { - return errors.New("incorrect prev randao") + return ErrInvalidPayloadPrevRandao } t, err := slots.ToTime(st.GenesisTime(), st.Slot()) if err != nil { return err } if payload.Timestamp != uint64(t.Unix()) { - return errors.New("incorrect timestamp") + return ErrInvalidPayloadTimeStamp } return nil } @@ -328,3 +334,62 @@ func isEmptyHeader(h *ethpb.ExecutionPayloadHeader) bool { } return true } + +// ValidatePayloadHeaderWhenMergeCompletes validates the payload header when the merge completes. +func ValidatePayloadHeaderWhenMergeCompletes(st state.BeaconState, header *ethpb.ExecutionPayloadHeader) error { + // Skip validation if the state is not merge compatible. + complete, err := IsMergeTransitionComplete(st) + if err != nil { + return err + } + if !complete { + return nil + } + // Validate current header's parent hash matches state header's block hash. + h, err := st.LatestExecutionPayloadHeader() + if err != nil { + return err + } + if !bytes.Equal(header.ParentHash, h.BlockHash) { + return ErrInvalidPayloadBlockHash + } + return nil +} + +// ValidatePayloadHeader validates the payload header. +func ValidatePayloadHeader(st state.BeaconState, header *ethpb.ExecutionPayloadHeader) error { + // Validate header's random mix matches with state in current epoch + random, err := helpers.RandaoMix(st, time.CurrentEpoch(st)) + if err != nil { + return err + } + if !bytes.Equal(header.PrevRandao, random) { + return ErrInvalidPayloadPrevRandao + } + + // Validate header's timestamp matches with state in current slot. + t, err := slots.ToTime(st.GenesisTime(), st.Slot()) + if err != nil { + return err + } + if header.Timestamp != uint64(t.Unix()) { + return ErrInvalidPayloadTimeStamp + } + return nil +} + +// ProcessPayloadHeader processes the payload header. +func ProcessPayloadHeader(st state.BeaconState, header *ethpb.ExecutionPayloadHeader) (state.BeaconState, error) { + if err := ValidatePayloadHeaderWhenMergeCompletes(st, header); err != nil { + return nil, err + } + + if err := ValidatePayloadHeader(st, header); err != nil { + return nil, err + } + + if err := st.SetLatestExecutionPayloadHeader(header); err != nil { + return nil, err + } + return st, nil +} diff --git a/beacon-chain/core/blocks/payload_test.go b/beacon-chain/core/blocks/payload_test.go index d9b23ccb6e..d311490909 100644 --- a/beacon-chain/core/blocks/payload_test.go +++ b/beacon-chain/core/blocks/payload_test.go @@ -7,6 +7,7 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/beacon-chain/core/time" + "github.com/prysmaticlabs/prysm/beacon-chain/state" fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams" "github.com/prysmaticlabs/prysm/encoding/bytesutil" "github.com/prysmaticlabs/prysm/encoding/ssz" @@ -600,7 +601,7 @@ func Test_ValidatePayload(t *testing.T) { { name: "incorrect prev randao", payload: emptyPayload(), - err: errors.New("incorrect prev randao"), + err: blocks.ErrInvalidPayloadPrevRandao, }, { name: "incorrect timestamp", @@ -610,7 +611,7 @@ func Test_ValidatePayload(t *testing.T) { h.Timestamp = 1 return h }(), - err: errors.New("incorrect timestamp"), + err: blocks.ErrInvalidPayloadTimeStamp, }, } for _, tt := range tests { @@ -648,7 +649,7 @@ func Test_ProcessPayload(t *testing.T) { { name: "incorrect prev randao", payload: emptyPayload(), - err: errors.New("incorrect prev randao"), + err: blocks.ErrInvalidPayloadPrevRandao, }, { name: "incorrect timestamp", @@ -658,7 +659,7 @@ func Test_ProcessPayload(t *testing.T) { h.Timestamp = 1 return h }(), - err: errors.New("incorrect timestamp"), + err: blocks.ErrInvalidPayloadTimeStamp, }, } for _, tt := range tests { @@ -678,6 +679,149 @@ func Test_ProcessPayload(t *testing.T) { } } +func Test_ProcessPayloadHeader(t *testing.T) { + st, _ := util.DeterministicGenesisStateBellatrix(t, 1) + random, err := helpers.RandaoMix(st, time.CurrentEpoch(st)) + require.NoError(t, err) + ts, err := slots.ToTime(st.GenesisTime(), st.Slot()) + require.NoError(t, err) + tests := []struct { + name string + header *ethpb.ExecutionPayloadHeader + err error + }{ + { + name: "process passes", + header: func() *ethpb.ExecutionPayloadHeader { + h := emptyPayloadHeader() + h.PrevRandao = random + h.Timestamp = uint64(ts.Unix()) + return h + }(), err: nil, + }, + { + name: "incorrect prev randao", + header: emptyPayloadHeader(), + err: blocks.ErrInvalidPayloadPrevRandao, + }, + { + name: "incorrect timestamp", + header: func() *ethpb.ExecutionPayloadHeader { + h := emptyPayloadHeader() + h.PrevRandao = random + h.Timestamp = 1 + return h + }(), + err: blocks.ErrInvalidPayloadTimeStamp, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + st, err := blocks.ProcessPayloadHeader(st, tt.header) + if err != nil { + require.Equal(t, tt.err.Error(), err.Error()) + } else { + require.Equal(t, tt.err, err) + got, err := st.LatestExecutionPayloadHeader() + require.NoError(t, err) + require.DeepSSZEqual(t, tt.header, got) + } + }) + } +} + +func Test_ValidatePayloadHeader(t *testing.T) { + st, _ := util.DeterministicGenesisStateBellatrix(t, 1) + random, err := helpers.RandaoMix(st, time.CurrentEpoch(st)) + require.NoError(t, err) + ts, err := slots.ToTime(st.GenesisTime(), st.Slot()) + require.NoError(t, err) + tests := []struct { + name string + header *ethpb.ExecutionPayloadHeader + err error + }{ + { + name: "process passes", + header: func() *ethpb.ExecutionPayloadHeader { + h := emptyPayloadHeader() + h.PrevRandao = random + h.Timestamp = uint64(ts.Unix()) + return h + }(), err: nil, + }, + { + name: "incorrect prev randao", + header: emptyPayloadHeader(), + err: blocks.ErrInvalidPayloadPrevRandao, + }, + { + name: "incorrect timestamp", + header: func() *ethpb.ExecutionPayloadHeader { + h := emptyPayloadHeader() + h.PrevRandao = random + h.Timestamp = 1 + return h + }(), + err: blocks.ErrInvalidPayloadTimeStamp, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := blocks.ValidatePayloadHeader(st, tt.header) + require.Equal(t, tt.err, err) + }) + } +} + +func Test_ValidatePayloadHeaderWhenMergeCompletes(t *testing.T) { + st, _ := util.DeterministicGenesisStateBellatrix(t, 1) + emptySt := st.Copy() + require.NoError(t, st.SetLatestExecutionPayloadHeader(ðpb.ExecutionPayloadHeader{BlockHash: []byte{'a'}})) + tests := []struct { + name string + state state.BeaconState + header *ethpb.ExecutionPayloadHeader + err error + }{ + { + name: "no merge", + header: func() *ethpb.ExecutionPayloadHeader { + h := emptyPayloadHeader() + return h + }(), + state: emptySt, + err: nil, + }, + { + name: "process passes", + header: func() *ethpb.ExecutionPayloadHeader { + h := emptyPayloadHeader() + h.ParentHash = []byte{'a'} + return h + }(), + state: st, + err: nil, + }, + { + name: "invalid block hash", + header: func() *ethpb.ExecutionPayloadHeader { + h := emptyPayloadHeader() + h.ParentHash = []byte{'b'} + return h + }(), + state: st, + err: blocks.ErrInvalidPayloadBlockHash, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := blocks.ValidatePayloadHeaderWhenMergeCompletes(tt.state, tt.header) + require.Equal(t, tt.err, err) + }) + } +} + func Test_PayloadToHeader(t *testing.T) { p := emptyPayload() h, err := blocks.PayloadToHeader(p) diff --git a/beacon-chain/core/transition/transition_no_verify_sig.go b/beacon-chain/core/transition/transition_no_verify_sig.go index 6b6b00bc6d..cb4287d661 100644 --- a/beacon-chain/core/transition/transition_no_verify_sig.go +++ b/beacon-chain/core/transition/transition_no_verify_sig.go @@ -290,13 +290,24 @@ func ProcessBlockForStateRoot( return nil, errors.Wrap(err, "could not check if execution is enabled") } if enabled { - payload, err := blk.Body().ExecutionPayload() - if err != nil { - return nil, err - } - state, err = b.ProcessPayload(state, payload) - if err != nil { - return nil, errors.Wrap(err, "could not process execution payload") + if blk.IsBlinded() { + header, err := blk.Body().ExecutionPayloadHeader() + if err != nil { + return nil, err + } + state, err = b.ProcessPayloadHeader(state, header) + if err != nil { + return nil, errors.Wrap(err, "could not process execution payload header") + } + } else { + payload, err := blk.Body().ExecutionPayload() + if err != nil { + return nil, err + } + state, err = b.ProcessPayload(state, payload) + if err != nil { + return nil, errors.Wrap(err, "could not process execution payload") + } } } diff --git a/proto/prysm/v1alpha1/block/block_interfaces.go b/proto/prysm/v1alpha1/block/block_interfaces.go index 9e5c984570..667754b3ee 100644 --- a/proto/prysm/v1alpha1/block/block_interfaces.go +++ b/proto/prysm/v1alpha1/block/block_interfaces.go @@ -37,6 +37,7 @@ type BeaconBlock interface { StateRoot() []byte Body() BeaconBlockBody IsNil() bool + IsBlinded() bool HashTreeRoot() ([32]byte, error) Proto() proto.Message ssz.Marshaler diff --git a/proto/prysm/v1alpha1/block/mock/block.go b/proto/prysm/v1alpha1/block/mock/block.go index 1dd9ab9249..f4801c08f0 100644 --- a/proto/prysm/v1alpha1/block/mock/block.go +++ b/proto/prysm/v1alpha1/block/mock/block.go @@ -117,6 +117,10 @@ func (BeaconBlock) IsNil() bool { return false } +func (BeaconBlock) IsBlinded() bool { + return false +} + func (BeaconBlock) Proto() proto.Message { panic("implement me") } diff --git a/proto/prysm/v1alpha1/wrapper/beacon_block_altair.go b/proto/prysm/v1alpha1/wrapper/beacon_block_altair.go index 6e5490a89e..47f6ab7d50 100644 --- a/proto/prysm/v1alpha1/wrapper/beacon_block_altair.go +++ b/proto/prysm/v1alpha1/wrapper/beacon_block_altair.go @@ -183,6 +183,11 @@ func (w altairBeaconBlock) IsNil() bool { return w.b == nil } +// IsBlinded checks if the beacon block is a blinded block. +func (altairBeaconBlock) IsBlinded() bool { + return false +} + // HashTreeRoot returns the ssz root of the block. func (w altairBeaconBlock) HashTreeRoot() ([32]byte, error) { return w.b.HashTreeRoot() diff --git a/proto/prysm/v1alpha1/wrapper/beacon_block_altair_test.go b/proto/prysm/v1alpha1/wrapper/beacon_block_altair_test.go index 6ef2fcb094..ef3c32a540 100644 --- a/proto/prysm/v1alpha1/wrapper/beacon_block_altair_test.go +++ b/proto/prysm/v1alpha1/wrapper/beacon_block_altair_test.go @@ -159,6 +159,12 @@ func TestAltairBeaconBlock_IsNil(t *testing.T) { assert.Equal(t, false, wb.IsNil()) } +func TestAltairBeaconBlock_IsBlinded(t *testing.T) { + wsb, err := wrapper.WrappedBeaconBlock(ðpb.BeaconBlockAltair{}) + require.NoError(t, err) + require.Equal(t, false, wsb.IsNil()) +} + func TestAltairBeaconBlock_HashTreeRoot(t *testing.T) { wb, err := wrapper.WrappedAltairBeaconBlock(util.HydrateBeaconBlockAltair(ðpb.BeaconBlockAltair{})) require.NoError(t, err) diff --git a/proto/prysm/v1alpha1/wrapper/beacon_block_bellatrix.go b/proto/prysm/v1alpha1/wrapper/beacon_block_bellatrix.go index 10605eae7c..823c5d6660 100644 --- a/proto/prysm/v1alpha1/wrapper/beacon_block_bellatrix.go +++ b/proto/prysm/v1alpha1/wrapper/beacon_block_bellatrix.go @@ -178,6 +178,11 @@ func (w bellatrixBeaconBlock) IsNil() bool { return w.b == nil } +// IsBlinded checks if the beacon block is a blinded block. +func (bellatrixBeaconBlock) IsBlinded() bool { + return false +} + // HashTreeRoot returns the ssz root of the block. func (w bellatrixBeaconBlock) HashTreeRoot() ([32]byte, error) { return w.b.HashTreeRoot() diff --git a/proto/prysm/v1alpha1/wrapper/beacon_block_bellatrix_test.go b/proto/prysm/v1alpha1/wrapper/beacon_block_bellatrix_test.go index 506c1a3f2a..a8a6677a41 100644 --- a/proto/prysm/v1alpha1/wrapper/beacon_block_bellatrix_test.go +++ b/proto/prysm/v1alpha1/wrapper/beacon_block_bellatrix_test.go @@ -191,6 +191,12 @@ func TestBellatrixBeaconBlock_IsNil(t *testing.T) { assert.Equal(t, false, wb.IsNil()) } +func TesTBellatrixBeaconBlock_IsBlinded(t *testing.T) { + wsb, err := wrapper.WrappedBeaconBlock(ðpb.BeaconBlockBellatrix{}) + require.NoError(t, err) + require.Equal(t, false, wsb.IsNil()) +} + func TestBellatrixBeaconBlock_HashTreeRoot(t *testing.T) { wb, err := wrapper.WrappedBellatrixBeaconBlock(util.HydrateBeaconBlockBellatrix(ðpb.BeaconBlockBellatrix{})) require.NoError(t, err) diff --git a/proto/prysm/v1alpha1/wrapper/beacon_block_phase0.go b/proto/prysm/v1alpha1/wrapper/beacon_block_phase0.go index 446d30fd6e..e38f8ecdac 100644 --- a/proto/prysm/v1alpha1/wrapper/beacon_block_phase0.go +++ b/proto/prysm/v1alpha1/wrapper/beacon_block_phase0.go @@ -175,6 +175,11 @@ func (w Phase0BeaconBlock) IsNil() bool { return w.b == nil || w.Body().IsNil() } +// IsBlinded checks if the beacon block is a blinded block. +func (Phase0BeaconBlock) IsBlinded() bool { + return false +} + // HashTreeRoot returns the ssz root of the block. func (w Phase0BeaconBlock) HashTreeRoot() ([32]byte, error) { return w.b.HashTreeRoot() diff --git a/proto/prysm/v1alpha1/wrapper/blinded_beacon_block_bellatrix.go b/proto/prysm/v1alpha1/wrapper/blinded_beacon_block_bellatrix.go index 458fa1d868..e42a172e38 100644 --- a/proto/prysm/v1alpha1/wrapper/blinded_beacon_block_bellatrix.go +++ b/proto/prysm/v1alpha1/wrapper/blinded_beacon_block_bellatrix.go @@ -179,6 +179,11 @@ func (w blindedBeaconBlockBellatrix) IsNil() bool { return w.b == nil } +// IsBlinded checks if the beacon block is a blinded block. +func (blindedBeaconBlockBellatrix) IsBlinded() bool { + return true +} + // HashTreeRoot returns the ssz root of the block. func (w blindedBeaconBlockBellatrix) HashTreeRoot() ([32]byte, error) { return w.b.HashTreeRoot() diff --git a/proto/prysm/v1alpha1/wrapper/blinded_beacon_block_bellatrix_test.go b/proto/prysm/v1alpha1/wrapper/blinded_beacon_block_bellatrix_test.go index e1e2f73862..72d65cf91b 100644 --- a/proto/prysm/v1alpha1/wrapper/blinded_beacon_block_bellatrix_test.go +++ b/proto/prysm/v1alpha1/wrapper/blinded_beacon_block_bellatrix_test.go @@ -202,6 +202,13 @@ func TestBellatrixBlindedBeaconBlock_IsNil(t *testing.T) { assert.Equal(t, false, wb.IsNil()) } +func TestBellatrixBlindedBeaconBlock_IsBlinded(t *testing.T) { + wb, err := wrapper.WrappedBeaconBlock(ðpb.BlindedBeaconBlockBellatrix{}) + require.NoError(t, err) + + assert.Equal(t, true, wb.IsBlinded()) +} + func TestBellatrixBlindedBeaconBlock_HashTreeRoot(t *testing.T) { wb, err := wrapper.WrappedBeaconBlock(util.HydrateBlindedBeaconBlockBellatrix(ðpb.BlindedBeaconBlockBellatrix{})) require.NoError(t, err)