Add process payload header (#10529)

* Add process payload header

* Update block.go

* Remove unused receivers

* Use errors vars

* Add some comments

* Add some comments
This commit is contained in:
terence tsao
2022-04-17 00:02:59 -07:00
committed by GitHub
parent 1acedd5b01
commit 3ce26ae7e2
12 changed files with 277 additions and 13 deletions

View File

@@ -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
}

View File

@@ -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(&ethpb.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)

View File

@@ -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")
}
}
}

View File

@@ -37,6 +37,7 @@ type BeaconBlock interface {
StateRoot() []byte
Body() BeaconBlockBody
IsNil() bool
IsBlinded() bool
HashTreeRoot() ([32]byte, error)
Proto() proto.Message
ssz.Marshaler

View File

@@ -117,6 +117,10 @@ func (BeaconBlock) IsNil() bool {
return false
}
func (BeaconBlock) IsBlinded() bool {
return false
}
func (BeaconBlock) Proto() proto.Message {
panic("implement me")
}

View File

@@ -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()

View File

@@ -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(&ethpb.BeaconBlockAltair{})
require.NoError(t, err)
require.Equal(t, false, wsb.IsNil())
}
func TestAltairBeaconBlock_HashTreeRoot(t *testing.T) {
wb, err := wrapper.WrappedAltairBeaconBlock(util.HydrateBeaconBlockAltair(&ethpb.BeaconBlockAltair{}))
require.NoError(t, err)

View File

@@ -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()

View File

@@ -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(&ethpb.BeaconBlockBellatrix{})
require.NoError(t, err)
require.Equal(t, false, wsb.IsNil())
}
func TestBellatrixBeaconBlock_HashTreeRoot(t *testing.T) {
wb, err := wrapper.WrappedBellatrixBeaconBlock(util.HydrateBeaconBlockBellatrix(&ethpb.BeaconBlockBellatrix{}))
require.NoError(t, err)

View File

@@ -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()

View File

@@ -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()

View File

@@ -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(&ethpb.BlindedBeaconBlockBellatrix{})
require.NoError(t, err)
assert.Equal(t, true, wb.IsBlinded())
}
func TestBellatrixBlindedBeaconBlock_HashTreeRoot(t *testing.T) {
wb, err := wrapper.WrappedBeaconBlock(util.HydrateBlindedBeaconBlockBellatrix(&ethpb.BlindedBeaconBlockBellatrix{}))
require.NoError(t, err)