Skip to content
This repository has been archived by the owner on Sep 23, 2023. It is now read-only.

Commit

Permalink
move ssz from erigon/cl/cltypes to erigon-lib/types so it can be used…
Browse files Browse the repository at this point in the history
… by txpool blob tx parser
  • Loading branch information
roberto-bayardo committed May 7, 2023
1 parent b182304 commit 1fde101
Show file tree
Hide file tree
Showing 3 changed files with 226 additions and 0 deletions.
5 changes: 5 additions & 0 deletions types/clonable/clonable.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package clonable

type Clonable interface {
Clone() Clonable
}
11 changes: 11 additions & 0 deletions types/ssz/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package ssz

import "errors"

var (
ErrLowBufferSize = errors.New("ssz(DecodeSSZ): bad encoding size")
ErrBadDynamicLength = errors.New("ssz(DecodeSSZ): bad dynamic length")
ErrBadOffset = errors.New("ssz(DecodeSSZ): invalid offset")
ErrBufferNotRounded = errors.New("ssz(DecodeSSZ): badly rounded operator")
ErrTooBigList = errors.New("ssz(DecodeSSZ): list too big")
)
210 changes: 210 additions & 0 deletions types/ssz/ssz.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
package ssz

import (
"encoding/binary"

libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/common/length"

"github.com/ledgerwatch/erigon-lib/types/clonable"
)

var (
BaseExtraDataSSZOffsetHeader = 536
BaseExtraDataSSZOffsetBlock = 508
)

type HashableSSZ interface {
HashSSZ() ([32]byte, error)
}

type EncodableSSZ interface {
Marshaler
Unmarshaler
}

type Marshaler interface {
EncodeSSZ([]byte) ([]byte, error)
EncodingSizeSSZ() int
}

type Unmarshaler interface {
DecodeSSZ(buf []byte) error
DecodeSSZWithVersion(buf []byte, version int) error
clonable.Clonable
}

func MarshalUint64SSZ(buf []byte, x uint64) {
binary.LittleEndian.PutUint64(buf, x)
}

func Uint64SSZ(x uint64) []byte {
b := make([]byte, 8)
binary.LittleEndian.PutUint64(b, x)
return b
}

func BoolSSZ(b bool) byte {
if b {
return 1
}
return 0
}

func OffsetSSZ(x uint32) []byte {
b := make([]byte, 4)
binary.LittleEndian.PutUint32(b, x)
return b
}

// EncodeOffset marshals a little endian uint32 to buf
func EncodeOffset(buf []byte, offset uint32) {
binary.LittleEndian.PutUint32(buf, offset)
}

// ReadOffset unmarshals a little endian uint32 to dst
func DecodeOffset(x []byte) uint32 {
return binary.LittleEndian.Uint32(x)
}

func UnmarshalUint64SSZ(x []byte) uint64 {
return binary.LittleEndian.Uint64(x)
}

func DecodeDynamicList[T Unmarshaler](bytes []byte, start, end uint32, max uint64) ([]T, error) {
if start > end || len(bytes) < int(end) {
return nil, ErrBadOffset
}
buf := bytes[start:end]
var elementsNum, currentOffset uint32
if len(buf) > 4 {
currentOffset = DecodeOffset(buf)
elementsNum = currentOffset / 4
}
inPos := 4
if uint64(elementsNum) > max {
return nil, ErrTooBigList
}
objs := make([]T, elementsNum)
for i := range objs {
endOffset := uint32(len(buf))
if i != len(objs)-1 {
if len(buf[inPos:]) < 4 {
return nil, ErrLowBufferSize
}
endOffset = DecodeOffset(buf[inPos:])
}
inPos += 4
if endOffset < currentOffset || len(buf) < int(endOffset) {
return nil, ErrBadOffset
}
objs[i] = objs[i].Clone().(T)
if err := objs[i].DecodeSSZ(buf[currentOffset:endOffset]); err != nil {
return nil, err
}
currentOffset = endOffset
}
return objs, nil
}

func DecodeStaticList[T Unmarshaler](bytes []byte, start, end, bytesPerElement uint32, max uint64) ([]T, error) {
if start > end || len(bytes) < int(end) {
return nil, ErrBadOffset
}
buf := bytes[start:end]
elementsNum := uint64(len(buf)) / uint64(bytesPerElement)
// Check for errors
if uint32(len(buf))%bytesPerElement != 0 {
return nil, ErrBufferNotRounded
}
if elementsNum > max {
return nil, ErrTooBigList
}
objs := make([]T, elementsNum)
for i := range objs {
objs[i] = objs[i].Clone().(T)
if err := objs[i].DecodeSSZ(buf[i*int(bytesPerElement):]); err != nil {
return nil, err
}
}
return objs, nil
}

func DecodeHashList(bytes []byte, start, end, max uint32) ([]libcommon.Hash, error) {
if start > end || len(bytes) < int(end) {
return nil, ErrBadOffset
}
buf := bytes[start:end]
elementsNum := uint32(len(buf)) / length.Hash
// Check for errors
if uint32(len(buf))%length.Hash != 0 {
return nil, ErrBufferNotRounded
}
if elementsNum > max {
return nil, ErrTooBigList
}
objs := make([]libcommon.Hash, elementsNum)
for i := range objs {
copy(objs[i][:], buf[i*length.Hash:])
}
return objs, nil
}

func DecodeNumbersList(bytes []byte, start, end uint32, max uint64) ([]uint64, error) {
if start > end || len(bytes) < int(end) {
return nil, ErrBadOffset
}
buf := bytes[start:end]
elementsNum := uint64(len(buf)) / length.BlockNum
// Check for errors
if uint64(len(buf))%length.BlockNum != 0 {
return nil, ErrBufferNotRounded
}
if elementsNum > max {
return nil, ErrTooBigList
}
objs := make([]uint64, elementsNum)
for i := range objs {
objs[i] = UnmarshalUint64SSZ(buf[i*length.BlockNum:])
}
return objs, nil
}

func CalculateIndiciesLimit(maxCapacity, numItems, size uint64) uint64 {
limit := (maxCapacity*size + 31) / 32
if limit != 0 {
return limit
}
if numItems == 0 {
return 1
}
return numItems
}

func DecodeString(bytes []byte, start, end, max uint64) ([]byte, error) {
if start > end || len(bytes) < int(end) {
return nil, ErrBadOffset
}
buf := bytes[start:end]
if uint64(len(buf)) > max {
return nil, ErrTooBigList
}
return buf, nil
}

func EncodeDynamicList[T Marshaler](buf []byte, objs []T) (dst []byte, err error) {
dst = buf
// Attestation
subOffset := len(objs) * 4
for _, attestation := range objs {
dst = append(dst, OffsetSSZ(uint32(subOffset))...)
subOffset += attestation.EncodingSizeSSZ()
}
for _, obj := range objs {
dst, err = obj.EncodeSSZ(dst)
if err != nil {
return
}
}
return
}

0 comments on commit 1fde101

Please sign in to comment.