github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/fvm/evm/precompiles/abi.go (about) 1 package precompiles 2 3 import ( 4 "encoding/binary" 5 "errors" 6 "math/big" 7 8 gethCommon "github.com/onflow/go-ethereum/common" 9 ) 10 11 // This package provides fast and efficient 12 // utilities needed for abi encoding and decoding 13 // encodings are mostly used for testing purpose 14 // if more complex encoding and decoding is needed please 15 // use the abi package and pass the ABIs, though 16 // that has a performance overhead. 17 const ( 18 FixedSizeUnitDataReadSize = 32 19 Bytes4DataReadSize = 4 20 Bytes8DataReadSize = 8 21 Bytes32DataReadSize = 32 22 Uint64ByteSize = 8 23 24 EncodedBoolSize = FixedSizeUnitDataReadSize 25 EncodedAddressSize = FixedSizeUnitDataReadSize 26 EncodedBytes32Size = FixedSizeUnitDataReadSize 27 EncodedBytes4Size = FixedSizeUnitDataReadSize 28 EncodedBytes8Size = FixedSizeUnitDataReadSize 29 EncodedUint64Size = FixedSizeUnitDataReadSize 30 EncodedUint256Size = FixedSizeUnitDataReadSize 31 ) 32 33 var ErrInputDataTooSmall = errors.New("input data is too small for decoding") 34 var ErrBufferTooSmall = errors.New("buffer too small for encoding") 35 var ErrDataTooLarge = errors.New("input data is too large for encoding") 36 37 // ReadAddress reads an address from the buffer at index 38 func ReadAddress(buffer []byte, index int) (gethCommon.Address, error) { 39 if len(buffer) < index+FixedSizeUnitDataReadSize { 40 return gethCommon.Address{}, ErrInputDataTooSmall 41 } 42 paddedData := buffer[index : index+FixedSizeUnitDataReadSize] 43 // addresses are zero-padded on the left side. 44 addr := gethCommon.BytesToAddress( 45 paddedData[FixedSizeUnitDataReadSize-gethCommon.AddressLength:]) 46 return addr, nil 47 } 48 49 // EncodeAddress encodes the address and add it to the buffer at the index 50 func EncodeAddress(address gethCommon.Address, buffer []byte, index int) error { 51 if len(buffer) < index+EncodedAddressSize { 52 return ErrBufferTooSmall 53 } 54 copy(buffer[index:index+EncodedAddressSize], 55 gethCommon.LeftPadBytes(address[:], EncodedAddressSize)) 56 return nil 57 } 58 59 // ReadBool reads a boolean from the buffer at the index 60 func ReadBool(buffer []byte, index int) (bool, error) { 61 if len(buffer) < index+EncodedBoolSize { 62 return false, ErrInputDataTooSmall 63 } 64 // bools are zero-padded on the left side 65 // so we only need to read the last byte 66 return uint8(buffer[index+EncodedBoolSize-1]) > 0, nil 67 } 68 69 // EncodeBool encodes a boolean into fixed size unit of encoded data 70 func EncodeBool(bitSet bool, buffer []byte, index int) error { 71 if len(buffer) < index+EncodedBoolSize { 72 return ErrBufferTooSmall 73 } 74 // bit set with left padding 75 for i := 0; i < EncodedBoolSize; i++ { 76 buffer[index+i] = 0 77 } 78 if bitSet { 79 buffer[index+EncodedBoolSize-1] = 1 80 } 81 return nil 82 } 83 84 // ReadUint64 reads a uint64 from the buffer at index 85 func ReadUint64(buffer []byte, index int) (uint64, error) { 86 if len(buffer) < index+EncodedUint64Size { 87 return 0, ErrInputDataTooSmall 88 } 89 // data is expected to be big endian (zero-padded on the left side) 90 return binary.BigEndian.Uint64( 91 buffer[index+EncodedUint64Size-Uint64ByteSize : index+EncodedUint64Size]), nil 92 } 93 94 // EncodeUint64 encodes a uint64 into fixed size unit of encoded data (zero-padded on the left side) 95 func EncodeUint64(inp uint64, buffer []byte, index int) error { 96 if len(buffer) < index+EncodedUint64Size { 97 return ErrBufferTooSmall 98 } 99 encoded := make([]byte, 8) 100 binary.BigEndian.PutUint64(encoded, inp) 101 copy(buffer[index:index+EncodedUint64Size], 102 gethCommon.LeftPadBytes(encoded, EncodedUint64Size), 103 ) 104 return nil 105 } 106 107 // ReadUint256 reads an address from the buffer at index 108 func ReadUint256(buffer []byte, index int) (*big.Int, error) { 109 if len(buffer) < index+EncodedUint256Size { 110 return nil, ErrInputDataTooSmall 111 } 112 // data is expected to be big endian (zero-padded on the left side) 113 return new(big.Int).SetBytes(buffer[index : index+EncodedUint256Size]), nil 114 } 115 116 // ReadBytes4 reads a 4 byte slice from the buffer at index 117 func ReadBytes4(buffer []byte, index int) ([]byte, error) { 118 if len(buffer) < index+EncodedBytes4Size { 119 return nil, ErrInputDataTooSmall 120 } 121 // fixed-size byte values are zero-padded on the right side. 122 return buffer[index : index+Bytes4DataReadSize], nil 123 } 124 125 // ReadBytes8 reads a 8 byte slice from the buffer at index 126 func ReadBytes8(buffer []byte, index int) ([]byte, error) { 127 if len(buffer) < index+EncodedBytes8Size { 128 return nil, ErrInputDataTooSmall 129 } 130 // fixed-size byte values are zero-padded on the right side. 131 return buffer[index : index+Bytes8DataReadSize], nil 132 } 133 134 // ReadBytes32 reads a 32 byte slice from the buffer at index 135 func ReadBytes32(buffer []byte, index int) ([]byte, error) { 136 if len(buffer) < index+Bytes32DataReadSize { 137 return nil, ErrInputDataTooSmall 138 } 139 return buffer[index : index+Bytes32DataReadSize], nil 140 } 141 142 // EncodeBytes32 encodes data into a bytes 32 143 func EncodeBytes32(data []byte, buffer []byte, index int) error { 144 if len(data) > EncodedBytes32Size { 145 return ErrDataTooLarge 146 } 147 if len(buffer) < index+EncodedBytes32Size { 148 return ErrBufferTooSmall 149 } 150 copy(buffer[index:index+EncodedBytes32Size], 151 gethCommon.RightPadBytes(data, EncodedBytes32Size), 152 ) 153 return nil 154 } 155 156 // ReadBytes reads a variable length bytes from the buffer 157 func ReadBytes(buffer []byte, index int) ([]byte, error) { 158 if len(buffer) < index+EncodedUint64Size { 159 return nil, ErrInputDataTooSmall 160 } 161 // reading offset (we read into uint64) and adjust index 162 offset, err := ReadUint64(buffer, index) 163 if err != nil { 164 return nil, err 165 } 166 index = int(offset) 167 if len(buffer) < index+EncodedUint64Size { 168 return nil, ErrInputDataTooSmall 169 } 170 // reading length of byte slice 171 length, err := ReadUint64(buffer, index) 172 if err != nil { 173 return nil, err 174 } 175 index += EncodedUint64Size 176 if len(buffer) < index+int(length) { 177 return nil, ErrInputDataTooSmall 178 } 179 return buffer[index : index+int(length)], nil 180 } 181 182 // SizeNeededForBytesEncoding computes the number of bytes needed for bytes encoding 183 func SizeNeededForBytesEncoding(data []byte) int { 184 if len(data) == 0 { 185 return EncodedUint64Size + EncodedUint64Size + FixedSizeUnitDataReadSize 186 } 187 paddedSize := (len(data) / FixedSizeUnitDataReadSize) 188 if len(data)%FixedSizeUnitDataReadSize != 0 { 189 paddedSize += 1 190 } 191 return EncodedUint64Size + EncodedUint64Size + paddedSize*FixedSizeUnitDataReadSize 192 } 193 194 // EncodeBytes encodes the data into the buffer at index and append payload to the 195 // end of buffer 196 func EncodeBytes(data []byte, buffer []byte, headerIndex, payloadIndex int) error { 197 //// updating offset 198 if len(buffer) < headerIndex+EncodedUint64Size { 199 return ErrBufferTooSmall 200 } 201 dataSize := len(data) 202 // compute padded data size 203 paddedSize := (dataSize / FixedSizeUnitDataReadSize) 204 if dataSize%FixedSizeUnitDataReadSize != 0 { 205 paddedSize += FixedSizeUnitDataReadSize 206 } 207 if len(buffer) < payloadIndex+EncodedUint64Size+paddedSize { 208 return ErrBufferTooSmall 209 } 210 211 err := EncodeUint64(uint64(payloadIndex), buffer, headerIndex) 212 if err != nil { 213 return err 214 } 215 216 //// updating payload 217 // padding data 218 if dataSize%FixedSizeUnitDataReadSize != 0 { 219 data = gethCommon.RightPadBytes(data, paddedSize) 220 } 221 222 // adding length 223 err = EncodeUint64(uint64(dataSize), buffer, payloadIndex) 224 if err != nil { 225 return err 226 } 227 payloadIndex += EncodedUint64Size 228 // adding data 229 copy(buffer[payloadIndex:payloadIndex+len(data)], data) 230 return nil 231 }