github.com/ledgerwatch/erigon-lib@v1.0.0/types/ssz/ssz.go (about)

     1  /*
     2     Copyright 2021 The Erigon contributors
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package ssz
    18  
    19  import (
    20  	"encoding/binary"
    21  
    22  	"github.com/ledgerwatch/erigon-lib/common"
    23  	"github.com/ledgerwatch/erigon-lib/common/length"
    24  	"github.com/ledgerwatch/erigon-lib/types/clonable"
    25  )
    26  
    27  var (
    28  	BaseExtraDataSSZOffsetHeader = 536
    29  	BaseExtraDataSSZOffsetBlock  = 508
    30  )
    31  
    32  type HashableSSZ interface {
    33  	HashSSZ() ([32]byte, error)
    34  }
    35  
    36  type EncodableSSZ interface {
    37  	Marshaler
    38  	Unmarshaler
    39  }
    40  
    41  type Marshaler interface {
    42  	EncodeSSZ([]byte) ([]byte, error)
    43  	EncodingSizeSSZ() int
    44  }
    45  
    46  type Unmarshaler interface {
    47  	DecodeSSZ(buf []byte, version int) error
    48  	clonable.Clonable
    49  }
    50  
    51  func MarshalUint64SSZ(buf []byte, x uint64) {
    52  	binary.LittleEndian.PutUint64(buf, x)
    53  }
    54  
    55  func Uint64SSZ(x uint64) []byte {
    56  	b := make([]byte, 8)
    57  	binary.LittleEndian.PutUint64(b, x)
    58  	return b
    59  }
    60  
    61  func BoolSSZ(b bool) byte {
    62  	if b {
    63  		return 1
    64  	}
    65  	return 0
    66  }
    67  
    68  func OffsetSSZ(x uint32) []byte {
    69  	b := make([]byte, 4)
    70  	binary.LittleEndian.PutUint32(b, x)
    71  	return b
    72  }
    73  
    74  // EncodeOffset marshals a little endian uint32 to buf
    75  func EncodeOffset(buf []byte, offset uint32) {
    76  	binary.LittleEndian.PutUint32(buf, offset)
    77  }
    78  
    79  // ReadOffset unmarshals a little endian uint32 to dst
    80  func DecodeOffset(x []byte) uint32 {
    81  	return binary.LittleEndian.Uint32(x)
    82  }
    83  
    84  func UnmarshalUint64SSZ(x []byte) uint64 {
    85  	return binary.LittleEndian.Uint64(x)
    86  }
    87  
    88  func DecodeDynamicList[T Unmarshaler](bytes []byte, start, end uint32, max uint64, version int) ([]T, error) {
    89  	if start > end || len(bytes) < int(end) {
    90  		return nil, ErrBadOffset
    91  	}
    92  	buf := bytes[start:end]
    93  	var elementsNum, currentOffset uint32
    94  	if len(buf) > 4 {
    95  		currentOffset = DecodeOffset(buf)
    96  		elementsNum = currentOffset / 4
    97  	}
    98  	inPos := 4
    99  	if uint64(elementsNum) > max {
   100  		return nil, ErrTooBigList
   101  	}
   102  	objs := make([]T, elementsNum)
   103  	for i := range objs {
   104  		endOffset := uint32(len(buf))
   105  		if i != len(objs)-1 {
   106  			if len(buf[inPos:]) < 4 {
   107  				return nil, ErrLowBufferSize
   108  			}
   109  			endOffset = DecodeOffset(buf[inPos:])
   110  		}
   111  		inPos += 4
   112  		if endOffset < currentOffset || len(buf) < int(endOffset) {
   113  			return nil, ErrBadOffset
   114  		}
   115  		objs[i] = objs[i].Clone().(T)
   116  		if err := objs[i].DecodeSSZ(buf[currentOffset:endOffset], version); err != nil {
   117  			return nil, err
   118  		}
   119  		currentOffset = endOffset
   120  	}
   121  	return objs, nil
   122  }
   123  
   124  func DecodeStaticList[T Unmarshaler](bytes []byte, start, end, bytesPerElement uint32, max uint64, version int) ([]T, error) {
   125  	if start > end || len(bytes) < int(end) {
   126  		return nil, ErrBadOffset
   127  	}
   128  	buf := bytes[start:end]
   129  	elementsNum := uint64(len(buf)) / uint64(bytesPerElement)
   130  	// Check for errors
   131  	if uint32(len(buf))%bytesPerElement != 0 {
   132  		return nil, ErrBufferNotRounded
   133  	}
   134  	if elementsNum > max {
   135  		return nil, ErrTooBigList
   136  	}
   137  	objs := make([]T, elementsNum)
   138  	for i := range objs {
   139  		objs[i] = objs[i].Clone().(T)
   140  		if err := objs[i].DecodeSSZ(buf[i*int(bytesPerElement):], version); err != nil {
   141  			return nil, err
   142  		}
   143  	}
   144  	return objs, nil
   145  }
   146  
   147  func DecodeHashList(bytes []byte, start, end, max uint32) ([]common.Hash, error) {
   148  	if start > end || len(bytes) < int(end) {
   149  		return nil, ErrBadOffset
   150  	}
   151  	buf := bytes[start:end]
   152  	elementsNum := uint32(len(buf)) / length.Hash
   153  	// Check for errors
   154  	if uint32(len(buf))%length.Hash != 0 {
   155  		return nil, ErrBufferNotRounded
   156  	}
   157  	if elementsNum > max {
   158  		return nil, ErrTooBigList
   159  	}
   160  	objs := make([]common.Hash, elementsNum)
   161  	for i := range objs {
   162  		copy(objs[i][:], buf[i*length.Hash:])
   163  	}
   164  	return objs, nil
   165  }
   166  
   167  func DecodeNumbersList(bytes []byte, start, end uint32, max uint64) ([]uint64, error) {
   168  	if start > end || len(bytes) < int(end) {
   169  		return nil, ErrBadOffset
   170  	}
   171  	buf := bytes[start:end]
   172  	elementsNum := uint64(len(buf)) / length.BlockNum
   173  	// Check for errors
   174  	if uint64(len(buf))%length.BlockNum != 0 {
   175  		return nil, ErrBufferNotRounded
   176  	}
   177  	if elementsNum > max {
   178  		return nil, ErrTooBigList
   179  	}
   180  	objs := make([]uint64, elementsNum)
   181  	for i := range objs {
   182  		objs[i] = UnmarshalUint64SSZ(buf[i*length.BlockNum:])
   183  	}
   184  	return objs, nil
   185  }
   186  
   187  func CalculateIndiciesLimit(maxCapacity, numItems, size uint64) uint64 {
   188  	limit := (maxCapacity*size + 31) / 32
   189  	if limit != 0 {
   190  		return limit
   191  	}
   192  	if numItems == 0 {
   193  		return 1
   194  	}
   195  	return numItems
   196  }
   197  
   198  func DecodeString(bytes []byte, start, end, max uint64) ([]byte, error) {
   199  	if start > end || len(bytes) < int(end) {
   200  		return nil, ErrBadOffset
   201  	}
   202  	buf := bytes[start:end]
   203  	if uint64(len(buf)) > max {
   204  		return nil, ErrTooBigList
   205  	}
   206  	return buf, nil
   207  }
   208  
   209  func EncodeDynamicList[T Marshaler](buf []byte, objs []T) (dst []byte, err error) {
   210  	dst = buf
   211  	// Attestation
   212  	subOffset := len(objs) * 4
   213  	for _, attestation := range objs {
   214  		dst = append(dst, OffsetSSZ(uint32(subOffset))...)
   215  		subOffset += attestation.EncodingSizeSSZ()
   216  	}
   217  	for _, obj := range objs {
   218  		dst, err = obj.EncodeSSZ(dst)
   219  		if err != nil {
   220  			return
   221  		}
   222  	}
   223  	return
   224  }