Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ssz): consolidate lists into one type #1666

Merged
merged 5 commits into from
Jul 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion mod/cli/pkg/commands/genesis/payload.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion mod/consensus-types/pkg/genesis/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ func DefaultGenesisExecutionPayloadHeaderDeneb() (

g.Go(func() error {
var err error
wds := ssz.ListCompositeFromElements(
wds := ssz.ListFromElements(
spec.DevnetChainSpec().MaxWithdrawalsPerPayload(),
[]*engineprimitives.Withdrawal{}...,
)
Expand Down
2 changes: 1 addition & 1 deletion mod/consensus-types/pkg/types/payload.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
6 changes: 3 additions & 3 deletions mod/engine-primitives/pkg/engine-primitives/withdrawal.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
26 changes: 26 additions & 0 deletions mod/primitives/pkg/ssz/errors.go
Original file line number Diff line number Diff line change
@@ -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")
200 changes: 67 additions & 133 deletions mod/primitives/pkg/ssz/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,199 +21,133 @@
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.
return false
}

// 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
Expand Down
3 changes: 1 addition & 2 deletions mod/primitives/pkg/ssz/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
10 changes: 7 additions & 3 deletions mod/primitives/pkg/ssz/vector.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
Loading