Skip to content

Commit

Permalink
Ensure epbs state getters & setters check versions (#14276)
Browse files Browse the repository at this point in the history
* Ensure EPBS state getters and setters check versions

* Rename to LatestExecutionPayloadHeaderEPBS

* Add minimal beacon state
  • Loading branch information
potuz committed Sep 13, 2024
1 parent a2cc300 commit 533bc70
Show file tree
Hide file tree
Showing 11 changed files with 138 additions and 61 deletions.
18 changes: 9 additions & 9 deletions beacon-chain/state/interfaces_epbs.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ import (
)

type ReadOnlyEpbsFields interface {
IsParentBlockFull() bool
ExecutionPayloadHeader() *enginev1.ExecutionPayloadHeaderEPBS
LatestBlockHash() []byte
LatestFullSlot() primitives.Slot
LastWithdrawalsRoot() []byte
IsParentBlockFull() (bool, error)
LatestExecutionPayloadHeaderEPBS() (*enginev1.ExecutionPayloadHeaderEPBS, error)
LatestBlockHash() ([]byte, error)
LatestFullSlot() (primitives.Slot, error)
LastWithdrawalsRoot() ([]byte, error)
}

type WriteOnlyEpbsFields interface {
SetExecutionPayloadHeader(val *enginev1.ExecutionPayloadHeaderEPBS)
SetLatestBlockHash(val []byte)
SetLatestFullSlot(val primitives.Slot)
SetLastWithdrawalsRoot(val []byte)
SetLatestExecutionPayloadHeaderEPBS(val *enginev1.ExecutionPayloadHeaderEPBS) error
SetLatestBlockHash(val []byte) error
SetLatestFullSlot(val primitives.Slot) error
SetLastWithdrawalsRoot(val []byte) error
}
8 changes: 4 additions & 4 deletions beacon-chain/state/state-native/beacon_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ type BeaconState struct {
nextWithdrawalIndex uint64
nextWithdrawalValidatorIndex primitives.ValidatorIndex
// ePBS fields
latestBlockHash [32]byte
latestFullSlot primitives.Slot
executionPayloadHeader *enginev1.ExecutionPayloadHeaderEPBS
lastWithdrawalsRoot [32]byte
latestBlockHash [32]byte
latestFullSlot primitives.Slot
latestExecutionPayloadHeaderEPBS *enginev1.ExecutionPayloadHeaderEPBS
lastWithdrawalsRoot [32]byte

// Electra fields
depositRequestsStartIndex uint64
Expand Down
48 changes: 36 additions & 12 deletions beacon-chain/state/state-native/getters_epbs.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,72 @@ import (
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
)

// ExecutionPayloadHeader retrieves a copy of the execution payload header.
// LatestExecutionPayloadHeaderEPBS retrieves a copy of the execution payload header from epbs state.
// It returns an error if the operation is not supported for the beacon state's version.
func (b *BeaconState) ExecutionPayloadHeader() *enginev1.ExecutionPayloadHeaderEPBS {
func (b *BeaconState) LatestExecutionPayloadHeaderEPBS() (*enginev1.ExecutionPayloadHeaderEPBS, error) {
if b.version < version.EPBS {
return nil, errNotSupported("LatestExecutionPayloadHeaderEPBS", b.version)
}
b.lock.RLock()
defer b.lock.RUnlock()

return b.executionPayloadHeaderVal()
return b.executionPayloadHeaderVal(), nil
}

// IsParentBlockFull checks if the last committed payload header was fulfilled.
// Returns true if both the beacon block and payload were present.
// Call this function on a beacon state before processing the execution payload header.
func (b *BeaconState) IsParentBlockFull() bool {
// It returns an error if the operation is not supported for the beacon state's version.
func (b *BeaconState) IsParentBlockFull() (bool, error) {
if b.version < version.EPBS {
return false, errNotSupported("IsParentBlockFull", b.version)
}

b.lock.RLock()
defer b.lock.RUnlock()

headerBlockHash := bytesutil.ToBytes32(b.executionPayloadHeader.BlockHash)
return headerBlockHash == b.latestBlockHash
headerBlockHash := bytesutil.ToBytes32(b.latestExecutionPayloadHeaderEPBS.BlockHash)
return headerBlockHash == b.latestBlockHash, nil
}

// LatestBlockHash returns the latest block hash.
func (b *BeaconState) LatestBlockHash() []byte {
// It returns an error if the operation is not supported for the beacon state's version.
func (b *BeaconState) LatestBlockHash() ([]byte, error) {
if b.version < version.EPBS {
return nil, errNotSupported("LatestBlockHash", b.version)
}

b.lock.RLock()
defer b.lock.RUnlock()

return b.latestBlockHash[:]
return b.latestBlockHash[:], nil
}

// LatestFullSlot returns the slot of the latest full block.
func (b *BeaconState) LatestFullSlot() primitives.Slot {
// It returns an error if the operation is not supported for the beacon state's version.
func (b *BeaconState) LatestFullSlot() (primitives.Slot, error) {
if b.version < version.EPBS {
return 0, errNotSupported("LatestFullSlot", b.version)
}

b.lock.RLock()
defer b.lock.RUnlock()

return b.latestFullSlot
return b.latestFullSlot, nil
}

// LastWithdrawalsRoot returns the latest withdrawal root.
func (b *BeaconState) LastWithdrawalsRoot() []byte {
// It returns an error if the operation is not supported for the beacon state's version.
func (b *BeaconState) LastWithdrawalsRoot() ([]byte, error) {
if b.version < version.EPBS {
return nil, errNotSupported("LastWithdrawalsRoot", b.version)
}

b.lock.RLock()
defer b.lock.RUnlock()

return b.lastWithdrawalsRoot[:]
return b.lastWithdrawalsRoot[:], nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ import (
)

func (b *BeaconState) executionPayloadHeaderVal() *enginev1.ExecutionPayloadHeaderEPBS {
return eth.CopyExecutionPayloadHeaderEPBS(b.executionPayloadHeader)
return eth.CopyExecutionPayloadHeaderEPBS(b.latestExecutionPayloadHeaderEPBS)
}
43 changes: 33 additions & 10 deletions beacon-chain/state/state-native/getters_setters_epbs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/testing/util/random"
)

func Test_LatestExecutionPayloadHeader(t *testing.T) {
func Test_LatestExecutionPayloadHeaderEPBS(t *testing.T) {
s := &BeaconState{version: version.EPBS}
_, err := s.LatestExecutionPayloadHeader()
require.ErrorContains(t, "unsupported version (epbs) for latest execution payload header", err)
Expand All @@ -23,13 +23,14 @@ func Test_SetLatestExecutionPayloadHeader(t *testing.T) {
require.ErrorContains(t, "SetLatestExecutionPayloadHeader is not supported for epbs", s.SetLatestExecutionPayloadHeader(nil))
}

func Test_SetExecutionPayloadHeader(t *testing.T) {
func Test_SetLatestExecutionPayloadHeaderEPBS(t *testing.T) {
s := &BeaconState{version: version.EPBS, dirtyFields: make(map[types.FieldIndex]bool)}
header := random.ExecutionPayloadHeader(t)
s.SetExecutionPayloadHeader(header)
require.NoError(t, s.SetLatestExecutionPayloadHeaderEPBS(header))
require.Equal(t, true, s.dirtyFields[types.ExecutionPayloadHeader])

got := s.ExecutionPayloadHeader()
got, err := s.LatestExecutionPayloadHeaderEPBS()
require.NoError(t, err)
require.DeepEqual(t, got, header)
}

Expand All @@ -38,19 +39,21 @@ func Test_SetLatestBlockHash(t *testing.T) {
b := make([]byte, fieldparams.RootLength)
_, err := rand.Read(b)
require.NoError(t, err)
s.SetLatestBlockHash(b)
require.NoError(t, s.SetLatestBlockHash(b))
require.Equal(t, true, s.dirtyFields[types.LatestBlockHash])

got := s.LatestBlockHash()
got, err := s.LatestBlockHash()
require.NoError(t, err)
require.DeepEqual(t, got, b)
}

func Test_SetLatestFullSlot(t *testing.T) {
s := &BeaconState{version: version.EPBS, dirtyFields: make(map[types.FieldIndex]bool)}
s.SetLatestFullSlot(3)
require.NoError(t, s.SetLatestFullSlot(primitives.Slot(3)))
require.Equal(t, true, s.dirtyFields[types.LatestFullSlot])

got := s.LatestFullSlot()
got, err := s.LatestFullSlot()
require.NoError(t, err)
require.Equal(t, primitives.Slot(3), got)
}

Expand All @@ -59,9 +62,29 @@ func Test_SetLastWithdrawalsRoot(t *testing.T) {
b := make([]byte, fieldparams.RootLength)
_, err := rand.Read(b)
require.NoError(t, err)
s.SetLastWithdrawalsRoot(b)
require.NoError(t, s.SetLastWithdrawalsRoot(b))
require.Equal(t, true, s.dirtyFields[types.LastWithdrawalsRoot])

got := s.LastWithdrawalsRoot()
got, err := s.LastWithdrawalsRoot()
require.NoError(t, err)
require.DeepEqual(t, got, b)
}

func Test_UnsupportedStateVersionEpbs(t *testing.T) {
s := &BeaconState{version: version.Electra}
_, err := s.IsParentBlockFull()
require.ErrorContains(t, "IsParentBlockFull is not supported for electra", err)
_, err = s.LatestBlockHash()
require.ErrorContains(t, "LatestBlockHash is not supported for electra", err)
_, err = s.LatestFullSlot()
require.ErrorContains(t, "LatestFullSlot is not supported for electra", err)
_, err = s.LastWithdrawalsRoot()
require.ErrorContains(t, "LastWithdrawalsRoot is not supported for electra", err)
_, err = s.LatestExecutionPayloadHeaderEPBS()
require.ErrorContains(t, "LatestExecutionPayloadHeaderEPBS is not supported for electra", err)

require.ErrorContains(t, "LastWithdrawalsRoot is not supported for electra", s.SetLastWithdrawalsRoot(nil))
require.ErrorContains(t, "SetLatestBlockHash is not supported for electra", s.SetLatestBlockHash(nil))
require.ErrorContains(t, "SetLatestFullSlot is not supported for electra", s.SetLatestFullSlot(0))
require.ErrorContains(t, "SetLatestExecutionPayloadHeaderEPBS is not supported for electra", s.SetLatestExecutionPayloadHeaderEPBS(nil))
}
2 changes: 1 addition & 1 deletion beacon-chain/state/state-native/getters_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ func (b *BeaconState) ToProtoUnsafe() interface{} {
PendingConsolidations: b.pendingConsolidations,
LatestBlockHash: b.latestBlockHash[:],
LatestFullSlot: b.latestFullSlot,
LatestExecutionPayloadHeader: b.executionPayloadHeader,
LatestExecutionPayloadHeader: b.latestExecutionPayloadHeaderEPBS,
LastWithdrawalsRoot: b.lastWithdrawalsRoot[:],
}
default:
Expand Down
2 changes: 1 addition & 1 deletion beacon-chain/state/state-native/hasher.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ func ComputeFieldRootsWithHasher(ctx context.Context, state *BeaconState) ([][]b
}
if state.version == version.EPBS {
// Execution payload header root.
executionPayloadRoot, err := state.executionPayloadHeader.HashTreeRoot()
executionPayloadRoot, err := state.latestExecutionPayloadHeaderEPBS.HashTreeRoot()
if err != nil {
return nil, err
}
Expand Down
37 changes: 31 additions & 6 deletions beacon-chain/state/state-native/setters_epbs.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,65 @@ import (
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
)

// SetExecutionPayloadHeader sets the execution payload header for the beacon state.
func (b *BeaconState) SetExecutionPayloadHeader(h *enginev1.ExecutionPayloadHeaderEPBS) {
// SetLatestExecutionPayloadHeaderEPBS sets the latest execution payload header for the epbs beacon state.
func (b *BeaconState) SetLatestExecutionPayloadHeaderEPBS(h *enginev1.ExecutionPayloadHeaderEPBS) error {
if b.version < version.EPBS {
return errNotSupported("SetLatestExecutionPayloadHeaderEPBS", b.version)
}

b.lock.Lock()
defer b.lock.Unlock()

b.executionPayloadHeader = h
b.latestExecutionPayloadHeaderEPBS = h
b.markFieldAsDirty(types.ExecutionPayloadHeader)

return nil
}

// SetLatestBlockHash sets the latest block hash for the beacon state.
func (b *BeaconState) SetLatestBlockHash(h []byte) {
func (b *BeaconState) SetLatestBlockHash(h []byte) error {
if b.version < version.EPBS {
return errNotSupported("SetLatestBlockHash", b.version)
}

b.lock.Lock()
defer b.lock.Unlock()

b.latestBlockHash = bytesutil.ToBytes32(h)
b.markFieldAsDirty(types.LatestBlockHash)

return nil
}

// SetLatestFullSlot sets the latest full slot for the beacon state.
func (b *BeaconState) SetLatestFullSlot(s primitives.Slot) {
func (b *BeaconState) SetLatestFullSlot(s primitives.Slot) error {
if b.version < version.EPBS {
return errNotSupported("SetLatestFullSlot", b.version)
}

b.lock.Lock()
defer b.lock.Unlock()

b.latestFullSlot = s
b.markFieldAsDirty(types.LatestFullSlot)

return nil
}

// SetLastWithdrawalsRoot sets the latest withdrawals root for the beacon state.
func (b *BeaconState) SetLastWithdrawalsRoot(r []byte) {
func (b *BeaconState) SetLastWithdrawalsRoot(r []byte) error {
if b.version < version.EPBS {
return errNotSupported("SetLastWithdrawalsRoot", b.version)
}

b.lock.Lock()
defer b.lock.Unlock()

b.lastWithdrawalsRoot = bytesutil.ToBytes32(r)
b.markFieldAsDirty(types.LastWithdrawalsRoot)

return nil
}
4 changes: 2 additions & 2 deletions beacon-chain/state/state-native/state_trie.go
Original file line number Diff line number Diff line change
Expand Up @@ -950,7 +950,7 @@ func (b *BeaconState) Copy() state.BeaconState {
latestExecutionPayloadHeaderCapella: b.latestExecutionPayloadHeaderCapella.Copy(),
latestExecutionPayloadHeaderDeneb: b.latestExecutionPayloadHeaderDeneb.Copy(),
latestExecutionPayloadHeaderElectra: b.latestExecutionPayloadHeaderElectra.Copy(),
executionPayloadHeader: b.executionPayloadHeaderVal(),
latestExecutionPayloadHeaderEPBS: b.executionPayloadHeaderVal(),

id: types.Enumerator.Inc(),

Expand Down Expand Up @@ -1355,7 +1355,7 @@ func (b *BeaconState) rootSelector(ctx context.Context, field types.FieldIndex)
case types.LatestFullSlot:
return ssz.Uint64Root(uint64(b.latestFullSlot)), nil
case types.ExecutionPayloadHeader:
return b.executionPayloadHeader.HashTreeRoot()
return b.latestExecutionPayloadHeaderEPBS.HashTreeRoot()
case types.LastWithdrawalsRoot:
return b.lastWithdrawalsRoot, nil
}
Expand Down
8 changes: 4 additions & 4 deletions beacon-chain/state/state-native/state_trie_epbs.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ func InitializeFromProtoUnsafeEpbs(st *ethpb.BeaconStateEPBS) (*BeaconState, err
pendingConsolidations: st.PendingConsolidations,

// ePBS fields
latestBlockHash: bytesutil.ToBytes32(st.LatestBlockHash),
latestFullSlot: st.LatestFullSlot,
executionPayloadHeader: st.LatestExecutionPayloadHeader,
lastWithdrawalsRoot: bytesutil.ToBytes32(st.LastWithdrawalsRoot),
latestBlockHash: bytesutil.ToBytes32(st.LatestBlockHash),
latestFullSlot: st.LatestFullSlot,
latestExecutionPayloadHeaderEPBS: st.LatestExecutionPayloadHeader,
lastWithdrawalsRoot: bytesutil.ToBytes32(st.LastWithdrawalsRoot),

dirtyFields: make(map[types.FieldIndex]bool, fieldCount),
dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount),
Expand Down
27 changes: 16 additions & 11 deletions beacon-chain/state/state-native/state_trie_epbs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,17 @@ func Test_InitializeFromProtoEpbs(t *testing.T) {
require.NoError(t, err)

// Assert that initial values match those in the new state.
gotLatestBlockHash := s.LatestBlockHash()
gotLatestBlockHash, err := s.LatestBlockHash()
require.NoError(t, err)
require.DeepEqual(t, latestBlockHash, gotLatestBlockHash)
gotLatestFullSlot := s.LatestFullSlot()
gotLatestFullSlot, err := s.LatestFullSlot()
require.NoError(t, err)
require.Equal(t, latestFullSlot, gotLatestFullSlot)
gotHeader := s.ExecutionPayloadHeader()
gotHeader, err := s.LatestExecutionPayloadHeaderEPBS()
require.NoError(t, err)
require.DeepEqual(t, header, gotHeader)
gotLastWithdrawalsRoot := s.LastWithdrawalsRoot()
gotLastWithdrawalsRoot, err := s.LastWithdrawalsRoot()
require.NoError(t, err)
require.DeepEqual(t, lastWithdrawalsRoot, gotLastWithdrawalsRoot)
}

Expand All @@ -38,21 +42,22 @@ func Test_CopyEpbs(t *testing.T) {

// Test shallow copy.
sNoCopy := s
require.DeepEqual(t, s.executionPayloadHeader, sNoCopy.executionPayloadHeader)
require.DeepEqual(t, s.latestExecutionPayloadHeaderEPBS, sNoCopy.latestExecutionPayloadHeaderEPBS)

// Modify a field to check if it reflects in the shallow copy.
s.executionPayloadHeader.Slot = 100
require.Equal(t, s.executionPayloadHeader, sNoCopy.executionPayloadHeader)
s.latestExecutionPayloadHeaderEPBS.Slot = 100
require.Equal(t, s.latestExecutionPayloadHeaderEPBS, sNoCopy.latestExecutionPayloadHeaderEPBS)

// Copy the state
sCopy := s.Copy()
require.NoError(t, err)
header := sCopy.ExecutionPayloadHeader()
require.DeepEqual(t, s.executionPayloadHeader, header)
header, err := sCopy.LatestExecutionPayloadHeaderEPBS()
require.NoError(t, err)
require.DeepEqual(t, s.latestExecutionPayloadHeaderEPBS, header)

// Modify the original to check if the copied state is independent.
s.executionPayloadHeader.Slot = 200
require.DeepNotEqual(t, s.executionPayloadHeader, header)
s.latestExecutionPayloadHeaderEPBS.Slot = 200
require.DeepNotEqual(t, s.latestExecutionPayloadHeaderEPBS, header)
}

func Test_HashTreeRootEpbs(t *testing.T) {
Expand Down

0 comments on commit 533bc70

Please sign in to comment.