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  }