diff --git a/mod/cli/pkg/commands/genesis/payload.go b/mod/cli/pkg/commands/genesis/payload.go index e9e63ab402..364ac178ca 100644 --- a/mod/cli/pkg/commands/genesis/payload.go +++ b/mod/cli/pkg/commands/genesis/payload.go @@ -183,7 +183,7 @@ func executableDataToExecutionPayloadHeader( g.Go(func() error { var withdrawalsRootErr error - wds := ssz.ListCompositeFromElements( + wds := ssz.ListFromElements( maxWithdrawalsPerPayload, withdrawals..., ) withdrawalsRoot, withdrawalsRootErr = wds.HashTreeRoot() diff --git a/mod/consensus-types/pkg/genesis/genesis.go b/mod/consensus-types/pkg/genesis/genesis.go index 3505024cf8..213aa76da7 100644 --- a/mod/consensus-types/pkg/genesis/genesis.go +++ b/mod/consensus-types/pkg/genesis/genesis.go @@ -152,7 +152,7 @@ func DefaultGenesisExecutionPayloadHeaderDeneb() ( g.Go(func() error { var err error - wds := ssz.ListCompositeFromElements( + wds := ssz.ListFromElements( spec.DevnetChainSpec().MaxWithdrawalsPerPayload(), []*engineprimitives.Withdrawal{}..., ) diff --git a/mod/consensus-types/pkg/types/payload.go b/mod/consensus-types/pkg/types/payload.go index c57ad0aedc..ab48493b78 100644 --- a/mod/consensus-types/pkg/types/payload.go +++ b/mod/consensus-types/pkg/types/payload.go @@ -81,7 +81,7 @@ func (e *ExecutionPayload) ToHeader( g.Go(func() error { var withdrawalsRootErr error - wds := ssz.ListCompositeFromElements( + wds := ssz.ListFromElements( maxWithdrawalsPerPayload, e.GetWithdrawals()...) withdrawalsRoot, withdrawalsRootErr = wds.HashTreeRoot() diff --git a/mod/engine-primitives/pkg/engine-primitives/withdrawal.go b/mod/engine-primitives/pkg/engine-primitives/withdrawal.go index af721d0836..37e6f3af69 100644 --- a/mod/engine-primitives/pkg/engine-primitives/withdrawal.go +++ b/mod/engine-primitives/pkg/engine-primitives/withdrawal.go @@ -71,7 +71,7 @@ func (w *Withdrawal) GetAmount() math.Gwei { } // NewFromSSZ returns a new Withdrawal from the provided SSZ bytes. -func (Withdrawal) NewFromSSZ(bytes []byte) (*Withdrawal, error) { +func (*Withdrawal) NewFromSSZ(bytes []byte) (*Withdrawal, error) { w := new(Withdrawal) if err := w.UnmarshalSSZ(bytes); err != nil { return nil, err @@ -80,11 +80,11 @@ func (Withdrawal) NewFromSSZ(bytes []byte) (*Withdrawal, error) { } // IsFixed returns true if the Withdrawal is fixed size. -func (Withdrawal) IsFixed() bool { +func (*Withdrawal) IsFixed() bool { return true } // Type returns the type of the Withdrawal. -func (Withdrawal) Type() types.Type { +func (*Withdrawal) Type() types.Type { return types.Composite } diff --git a/mod/primitives/pkg/ssz/errors.go b/mod/primitives/pkg/ssz/errors.go new file mode 100644 index 0000000000..2223a0feba --- /dev/null +++ b/mod/primitives/pkg/ssz/errors.go @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: BUSL-1.1 +// +// Copyright (C) 2024, Berachain Foundation. All rights reserved. +// Use of this software is governed by the Business Source License included +// in the LICENSE file of this repository and at www.mariadb.com/bsl11. +// +// ANY USE OF THE LICENSED WORK IN VIOLATION OF THIS LICENSE WILL AUTOMATICALLY +// TERMINATE YOUR RIGHTS UNDER THIS LICENSE FOR THE CURRENT AND ALL OTHER +// VERSIONS OF THE LICENSED WORK. +// +// THIS LICENSE DOES NOT GRANT YOU ANY RIGHT IN ANY TRADEMARK OR LOGO OF +// LICENSOR OR ITS AFFILIATES (PROVIDED THAT YOU MAY USE A TRADEMARK OR LOGO OF +// LICENSOR AS EXPRESSLY REQUIRED BY THIS LICENSE). +// +// TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON +// AN “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, +// EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND +// TITLE. + +package ssz + +import "github.com/berachain/beacon-kit/mod/errors" + +// ErrUnknownType is returned when an unknown type is encountered. +var ErrUnknownType = errors.New("unknown type") diff --git a/mod/primitives/pkg/ssz/list.go b/mod/primitives/pkg/ssz/list.go index 23bce0f08b..914e07de01 100644 --- a/mod/primitives/pkg/ssz/list.go +++ b/mod/primitives/pkg/ssz/list.go @@ -21,127 +21,50 @@ package ssz import ( - "github.com/berachain/beacon-kit/mod/primitives/pkg/common" + "github.com/berachain/beacon-kit/mod/errors" "github.com/berachain/beacon-kit/mod/primitives/pkg/ssz/constants" "github.com/berachain/beacon-kit/mod/primitives/pkg/ssz/merkleizer" "github.com/berachain/beacon-kit/mod/primitives/pkg/ssz/serializer" + "github.com/berachain/beacon-kit/mod/primitives/pkg/ssz/types" ) /* -------------------------------------------------------------------------- */ -/* Basic */ +/* Type Definitions */ /* -------------------------------------------------------------------------- */ -// ListBasic is a list of basic types. -type ListBasic[B Basic[B]] struct { +// Vector conforms to the SSZEenumerable interface. +var _ types.SSZEnumerable[U64] = (*List[U64])(nil) + +// List is a list of basic types. +type List[B types.SSZType[B]] struct { elements []B limit uint64 } -// ListBasicFromElements creates a new ListComposite from elements. +// ListFromElements creates a new ListComposite from elements. // TODO: Deprecate once off of Fastssz -func ListBasicFromElements[B Basic[B]]( +func ListFromElements[B types.SSZType[B]]( limit uint64, elements ...B, -) *ListBasic[B] { - return &ListBasic[B]{ +) *List[B] { + return &List[B]{ elements: elements, limit: limit, } } -// IsFixed returns true if the ListBasic is fixed size. -func (l ListBasic[B]) IsFixed() bool { - // We recursively define "variable-size" types to be lists, unions, - // Bitlists. - // Therefore all Lists are NOT fixed. - return false -} - -// N returns the N value as defined in the SSZ specification. -func (l ListBasic[B]) N() uint64 { - // list: ordered variable-length homogeneous collection, limited to N values - // notation List[type, N], e.g. List[uint64, N] - return l.limit -} - -// ChunkCount returns the number of chunks in the ListBasic. -func (l ListBasic[B]) ChunkCount() uint64 { - // List[B, N] and Vector[B, N], where B is a basic type: - // (N * size_of(B) + 31) // 32 (dividing by chunk size, rounding up) - var b B - //#nosec:G701 // its fine. - //nolint:mnd // 31 is okay. - return (l.N()*uint64(b.SizeSSZ()) + 31) / constants.BytesPerChunk -} - -// SizeSSZ returns the size of the list in bytes. -func (l ListBasic[B]) SizeSSZ() int { - // The same for ListBasic as for Vector. - return Vector[B](l.elements).SizeSSZ() -} - -// HashTreeRootWith returns the Merkle root of the ListBasic -// with a given merkleizer. -func (l ListBasic[B]) HashTreeRootWith( - merkleizer BasicMerkleizer[[32]byte, B], -) ([32]byte, error) { - return merkleizer.MerkleizeListBasic(l.elements, l.ChunkCount()) -} - -// HashTreeRoot returns the Merkle root of the ListBasic. -func (l ListBasic[B]) HashTreeRoot() ([32]byte, error) { - // Create a merkleizer - return l.HashTreeRootWith(merkleizer.New[[32]byte, B]()) -} - -// MarshalSSZTo marshals the ListBasic into SSZ format. -func (l ListBasic[B]) MarshalSSZTo(out []byte) ([]byte, error) { - return Vector[B](l.elements).MarshalSSZTo(out) -} - -// MarshalSSZ marshals the ListBasic into SSZ format. -func (l ListBasic[B]) MarshalSSZ() ([]byte, error) { - // The same for ListBasic as for Vector. - return Vector[B](l.elements).MarshalSSZ() -} - -// NewFromSSZ creates a new ListBasic from SSZ format. -func (l ListBasic[B]) NewFromSSZ(buf []byte) (*ListBasic[B], error) { - // The same for ListBasic as for Vector - var ( - elements = make(Vector[B], 0) - err error - ) - - elements, err = elements.NewFromSSZ(buf) - return &ListBasic[B]{ - elements: elements, - }, err -} - /* -------------------------------------------------------------------------- */ -/* Composite */ +/* BaseSSZType */ /* -------------------------------------------------------------------------- */ -// ListComposite is a list of Composite types. -type ListComposite[C Composite[C]] struct { - elements []C - limit uint64 -} - -// ListCompositeFromElements creates a new ListComposite from elements. -// TODO: Deprecate once off of Fastssz -func ListCompositeFromElements[C Composite[C]]( - limit uint64, elements ...C, -) *ListComposite[C] { - return &ListComposite[C]{ - elements: elements, - limit: limit, - } +// SizeSSZ returns the size of the list in bytes. +func (l List[B]) SizeSSZ() int { + // The same for List as for Vector. + return Vector[B](l.elements).SizeSSZ() } -// IsFixed returns true if the ListBasic is fixed size. -func (l ListComposite[C]) IsFixed() bool { +// IsFixed returns true if the List is fixed size. +func (l List[B]) IsFixed() bool { // We recursively define "variable-size" types to be lists, unions, // Bitlists. // Therefore all Lists are NOT fixed. @@ -149,71 +72,82 @@ func (l ListComposite[C]) IsFixed() bool { } // N returns the N value as defined in the SSZ specification. -func (l ListComposite[C]) N() uint64 { +func (l List[B]) N() uint64 { // list: ordered variable-length homogeneous collection, limited to N values // notation List[type, N], e.g. List[uint64, N] return l.limit } -// ChunkCount returns the number of chunks in the VectorComposite. -func (l ListComposite[C]) ChunkCount() uint64 { - // List[C, N] and Vector[C, N], where C is a composite type: N - return l.N() +// ChunkCount returns the number of chunks in the List. +func (l List[B]) ChunkCount() uint64 { + var b B + switch b.Type() { + case types.Basic: + //#nosec:G701 // its fine. + //nolint:mnd // 31 is okay. + return (l.N()*uint64(b.SizeSSZ()) + 31) / constants.BytesPerChunk + default: + return l.N() + } } -// SizeSSZ returns the size of the list in bytes. -func (l ListComposite[C]) SizeSSZ() int { - // The same for ListComposite as for VectorComposite. - return Vector[C](l.elements).SizeSSZ() +// Type returns the type of the List. +func (l List[B]) Type() types.Type { + return types.Composite +} + +// Elements returns the elements of the List. +func (l List[B]) Elements() []B { + return l.elements } -// HashTreeRootWith returns the Merkle root of the ListComposite +// HashTreeRootWith returns the Merkle root of the List // with a given merkleizer. -func (l ListComposite[C]) HashTreeRootWith( - merkleizer CompositeMerkleizer[common.ChainSpec, [32]byte, C], +func (l List[B]) HashTreeRootWith( + merkleizer ListMerkleizer[[32]byte, B], ) ([32]byte, error) { - return merkleizer.MerkleizeListComposite(l.elements, l.ChunkCount()) + var b B + switch b.Type() { + case types.Basic: + return merkleizer.MerkleizeListBasic(l.elements, l.ChunkCount()) + case types.Composite: + return merkleizer.MerkleizeListComposite(l.elements, l.ChunkCount()) + default: + return [32]byte{}, errors.Wrapf(ErrUnknownType, "%v", b.Type()) + } } -// HashTreeRoot returns the Merkle root of the ListComposite. -func (l ListComposite[C]) HashTreeRoot() ([32]byte, error) { +// HashTreeRoot returns the Merkle root of the List. +func (l List[B]) HashTreeRoot() ([32]byte, error) { // Create a merkleizer - return l.HashTreeRootWith(merkleizer.New[[32]byte, C]()) + return l.HashTreeRootWith(merkleizer.New[[32]byte, B]()) } -// MarshalSSZTo marshals the ListComposite into SSZ format. -func (l ListComposite[C]) MarshalSSZTo(out []byte) ([]byte, error) { - var c C - if !c.IsFixed() { - panic("not implemented yet") - } - - // Safe to use Vector helper for a list here. - return serializer.MarshalVectorFixed(out, l.elements) +// MarshalSSZTo marshals the List into SSZ format. +func (l List[B]) MarshalSSZTo(out []byte) ([]byte, error) { + return Vector[B](l.elements).MarshalSSZTo(out) } -// MarshalSSZ marshals the ListComposite into SSZ format. -func (l ListComposite[C]) MarshalSSZ() ([]byte, error) { - return l.MarshalSSZTo(make([]byte, 0, l.SizeSSZ())) +// MarshalSSZ marshals the List into SSZ format. +func (l List[B]) MarshalSSZ() ([]byte, error) { + // The same for List as for Vector. + return Vector[B](l.elements).MarshalSSZ() } -// NewFromSSZ creates a new ListComposite from SSZ format. -func (ListComposite[C]) NewFromSSZ( - buf []byte, - limit uint64, -) (*ListComposite[C], error) { - var c C - if !c.IsFixed() { +// NewFromSSZ creates a new List from SSZ format. +func (l List[B]) NewFromSSZ(buf []byte, limit uint64) (*List[B], error) { + var b B + if !b.IsFixed() { panic("not implemented yet") } // We can use Vector helper for a list here, it is safe. - elements, err := serializer.UnmarshalVectorFixed[C](buf) + elements, err := serializer.UnmarshalVectorFixed[B](buf) if err != nil { return nil, err } - return &ListComposite[C]{ + return &List[B]{ elements: elements, limit: limit, }, nil diff --git a/mod/primitives/pkg/ssz/types/types.go b/mod/primitives/pkg/ssz/types/types.go index 6ac1870ea9..e38145f9a8 100644 --- a/mod/primitives/pkg/ssz/types/types.go +++ b/mod/primitives/pkg/ssz/types/types.go @@ -52,10 +52,9 @@ type SSZType[T any] interface { // SSZEnumerable is the interface for all SSZ enumerable types must implement. type SSZEnumerable[ - SelfT BaseSSZType, ElementT any, ] interface { - SSZType[SelfT] + BaseSSZType // N returns the N value as defined in the SSZ specification. N() uint64 // Elements returns the elements of the enumerable type. diff --git a/mod/primitives/pkg/ssz/vector.go b/mod/primitives/pkg/ssz/vector.go index 8624078918..9ecdf1e85f 100644 --- a/mod/primitives/pkg/ssz/vector.go +++ b/mod/primitives/pkg/ssz/vector.go @@ -33,7 +33,7 @@ import ( /* -------------------------------------------------------------------------- */ // Vector conforms to the SSZEenumerable interface. -var _ types.SSZEnumerable[Vector[U64], U64] = (Vector[U64])(nil) +var _ types.SSZEnumerable[U64] = (Vector[U64])(nil) // Vector represents a vector of elements. type Vector[B types.SSZType[B]] []B @@ -102,10 +102,14 @@ func (v Vector[B]) HashTreeRootWith( merkleizer VectorMerkleizer[[32]byte, B], ) ([32]byte, error) { var b B - if b.Type() == types.Basic { + switch b.Type() { + case types.Basic: return merkleizer.MerkleizeVectorBasic(v) + case types.Composite: + return merkleizer.MerkleizeVectorCompositeOrContainer(v) + default: + return [32]byte{}, errors.Wrapf(ErrUnknownType, "%v", b.Type()) } - return merkleizer.MerkleizeVectorCompositeOrContainer(v) } // HashTreeRoot returns the Merkle root of the VectorBasic.