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 }