summaryrefslogtreecommitdiff
path: root/data/abi/abi_encode.go
diff options
context:
space:
mode:
Diffstat (limited to 'data/abi/abi_encode.go')
-rw-r--r--data/abi/abi_encode.go665
1 files changed, 372 insertions, 293 deletions
diff --git a/data/abi/abi_encode.go b/data/abi/abi_encode.go
index db4750bd0..fa5dbd57c 100644
--- a/data/abi/abi_encode.go
+++ b/data/abi/abi_encode.go
@@ -20,214 +20,242 @@ import (
"encoding/binary"
"fmt"
"math/big"
+ "reflect"
+ "strings"
)
-// arrayToTuple casts an array-like ABI Value into an ABI Value of Tuple type.
-// This is used in both ABI Encoding and Decoding.
-func (v Value) arrayToTuple() (Value, error) {
+// typeCastToTuple cast an array-like ABI type into an ABI tuple type.
+func (t Type) typeCastToTuple(tupLen ...int) (Type, error) {
var childT []Type
- var valueArr []Value
- switch v.ABIType.abiTypeID {
+ switch t.abiTypeID {
case String:
- strValue, err := v.GetString()
- if err != nil {
- return Value{}, err
+ if len(tupLen) != 1 {
+ return Type{}, fmt.Errorf("string type conversion to tuple need 1 length argument")
}
- strByte := []byte(strValue)
-
- childT = make([]Type, len(strByte))
- valueArr = make([]Value, len(strByte))
-
- for i := 0; i < len(strByte); i++ {
- childT[i] = MakeByteType()
- valueArr[i] = MakeByte(strByte[i])
+ childT = make([]Type, tupLen[0])
+ for i := 0; i < tupLen[0]; i++ {
+ childT[i] = byteType
}
case Address:
- addr, err := v.GetAddress()
- if err != nil {
- return Value{}, err
- }
-
childT = make([]Type, addressByteSize)
- valueArr = make([]Value, addressByteSize)
-
for i := 0; i < addressByteSize; i++ {
- childT[i] = MakeByteType()
- valueArr[i] = MakeByte(addr[i])
+ childT[i] = byteType
}
case ArrayStatic:
- childT = make([]Type, v.ABIType.staticLength)
- for i := 0; i < int(v.ABIType.staticLength); i++ {
- childT[i] = v.ABIType.childTypes[0]
+ childT = make([]Type, t.staticLength)
+ for i := 0; i < int(t.staticLength); i++ {
+ childT[i] = t.childTypes[0]
}
- valueArr = v.value.([]Value)
case ArrayDynamic:
- arrayElems := v.value.([]Value)
- childT = make([]Type, len(arrayElems))
- for i := 0; i < len(arrayElems); i++ {
- childT[i] = v.ABIType.childTypes[0]
+ if len(tupLen) != 1 {
+ return Type{}, fmt.Errorf("dynamic array type conversion to tuple need 1 length argument")
+ }
+ childT = make([]Type, tupLen[0])
+ for i := 0; i < tupLen[0]; i++ {
+ childT[i] = t.childTypes[0]
}
- valueArr = arrayElems
default:
- return Value{}, fmt.Errorf("value type not supported to conversion to tuple")
+ return Type{}, fmt.Errorf("type cannot support conversion to tuple")
}
- castedTupleType, err := MakeTupleType(childT)
+ tuple, err := MakeTupleType(childT)
if err != nil {
- return Value{}, err
+ return Type{}, err
}
-
- return Value{
- ABIType: castedTupleType,
- value: valueArr,
- }, nil
+ return tuple, nil
}
-// Encode method serialize the ABI value into a byte string of ABI encoding rule.
-func (v Value) Encode() ([]byte, error) {
- switch v.ABIType.abiTypeID {
- case Uint:
- bigIntValue, err := v.GetUint()
- if err != nil {
- return []byte{}, err
- }
- // NOTE: ugly work-round for golang 1.14. if upgraded to 1.15, should use `fillbytes`
- bigIntBytes := bigIntValue.Bytes()
- buffer := make([]byte, v.ABIType.bitSize/8-uint16(len(bigIntBytes)))
- buffer = append(buffer, bigIntBytes...)
- return buffer, nil
- case Ufixed:
- ufixedValue, err := v.GetUfixed()
- if err != nil {
- return []byte{}, err
- }
- // NOTE: ugly work-round for golang 1.14. if upgraded to 1.15, should use `fillbytes`
- encodeBuffer := ufixedValue.Bytes()
- buffer := make([]byte, v.ABIType.bitSize/8-uint16(len(encodeBuffer)))
- buffer = append(buffer, encodeBuffer...)
- return buffer, nil
+// Encode is an ABI type method to encode go values into bytes following ABI encoding rules
+func (t Type) Encode(value interface{}) ([]byte, error) {
+ switch t.abiTypeID {
+ case Uint, Ufixed:
+ return encodeInt(value, t.bitSize)
case Bool:
- boolValue, err := v.GetBool()
- if err != nil {
- return []byte{}, err
+ boolValue, ok := value.(bool)
+ if !ok {
+ return nil, fmt.Errorf("cannot cast value to bool in bool encoding")
}
if boolValue {
return []byte{0x80}, nil
}
return []byte{0x00}, nil
case Byte:
- bytesValue, err := v.GetByte()
- if err != nil {
- return []byte{}, nil
+ byteValue, ok := value.(byte)
+ if !ok {
+ return nil, fmt.Errorf("cannot cast value to byte in byte encoding")
}
- return []byte{bytesValue}, nil
+ return []byte{byteValue}, nil
case ArrayStatic, Address:
- convertedTuple, err := v.arrayToTuple()
+ castedType, err := t.typeCastToTuple()
+ if err != nil {
+ return nil, err
+ }
+ return castedType.Encode(value)
+ case ArrayDynamic:
+ dynamicArray, err := inferToSlice(value)
if err != nil {
- return []byte{}, err
+ return nil, err
}
- return tupleEncoding(convertedTuple)
- case ArrayDynamic, String:
- convertedTuple, err := v.arrayToTuple()
+ castedType, err := t.typeCastToTuple(len(dynamicArray))
if err != nil {
- return []byte{}, err
+ return nil, err
}
- length := len(convertedTuple.ABIType.childTypes)
lengthEncode := make([]byte, lengthEncodeByteSize)
- binary.BigEndian.PutUint16(lengthEncode, uint16(length))
-
- encoded, err := tupleEncoding(convertedTuple)
+ binary.BigEndian.PutUint16(lengthEncode, uint16(len(dynamicArray)))
+ encoded, err := castedType.Encode(value)
+ if err != nil {
+ return nil, err
+ }
+ encoded = append(lengthEncode, encoded...)
+ return encoded, nil
+ case String:
+ stringValue, okString := value.(string)
+ if !okString {
+ return nil, fmt.Errorf("cannot cast value to string or array dynamic in encoding")
+ }
+ byteValue := []byte(stringValue)
+ castedType, err := t.typeCastToTuple(len(byteValue))
if err != nil {
- return []byte{}, err
+ return nil, err
}
- return append(lengthEncode, encoded...), nil
+ lengthEncode := make([]byte, lengthEncodeByteSize)
+ binary.BigEndian.PutUint16(lengthEncode, uint16(len(byteValue)))
+ encoded, err := castedType.Encode(byteValue)
+ if err != nil {
+ return nil, err
+ }
+ encoded = append(lengthEncode, encoded...)
+ return encoded, nil
case Tuple:
- return tupleEncoding(v)
+ return encodeTuple(value, t.childTypes)
default:
- return []byte{}, fmt.Errorf("Encoding: unknown type error (bruh why you are here)")
+ return nil, fmt.Errorf("cannot infer type for encoding")
}
}
-// compressMultipleBool compress consecutive bool values into a byte in ABI tuple/array value.
-func compressMultipleBool(valueList []Value) (uint8, error) {
- var res uint8 = 0
- if len(valueList) > 8 {
- return 0, fmt.Errorf("value list passed in should be no greater than length 8")
+// encodeInt encodes int-alike golang values to bytes, following ABI encoding rules
+func encodeInt(intValue interface{}, bitSize uint16) ([]byte, error) {
+ var bigInt *big.Int
+
+ switch intValue := intValue.(type) {
+ case int8:
+ bigInt = big.NewInt(int64(intValue))
+ case uint8:
+ bigInt = new(big.Int).SetUint64(uint64(intValue))
+ case int16:
+ bigInt = big.NewInt(int64(intValue))
+ case uint16:
+ bigInt = new(big.Int).SetUint64(uint64(intValue))
+ case int32:
+ bigInt = big.NewInt(int64(intValue))
+ case uint32:
+ bigInt = new(big.Int).SetUint64(uint64(intValue))
+ case int64:
+ bigInt = big.NewInt(intValue)
+ case uint64:
+ bigInt = new(big.Int).SetUint64(intValue)
+ case uint:
+ bigInt = new(big.Int).SetUint64(uint64(intValue))
+ case int:
+ bigInt = big.NewInt(int64(intValue))
+ case *big.Int:
+ bigInt = intValue
+ default:
+ return nil, fmt.Errorf("cannot infer go type for uint encode")
}
- for i := 0; i < len(valueList); i++ {
- if valueList[i].ABIType.abiTypeID != Bool {
- return 0, fmt.Errorf("bool type not matching in compressMultipleBool")
- }
- boolVal, err := valueList[i].GetBool()
- if err != nil {
- return 0, err
- }
- if boolVal {
- res |= 1 << uint(7-i)
+
+ if bigInt.Sign() < 0 {
+ return nil, fmt.Errorf("passed in numeric value should be non negative")
+ }
+
+ bytes := bigInt.Bytes()
+ if len(bytes) > int(bitSize/8) {
+ return nil, fmt.Errorf("input value bit size %d > abi type bit size %d", len(bytes)*8, bitSize)
+ }
+
+ zeroPadding := make([]byte, bitSize/8-uint16(len(bytes)))
+ buffer := append(zeroPadding, bytes...)
+ return buffer, nil
+}
+
+// inferToSlice infers an interface element to a slice of interface{}, returns error if it cannot infer successfully
+func inferToSlice(value interface{}) ([]interface{}, error) {
+ reflectVal := reflect.ValueOf(value)
+ if reflectVal.Kind() != reflect.Slice && reflectVal.Kind() != reflect.Array {
+ return nil, fmt.Errorf("cannot infer an interface value as a slice of interface element")
+ }
+ if reflectVal.IsNil() {
+ if reflectVal.Kind() == reflect.Slice {
+ return nil, nil
}
+ return nil, fmt.Errorf("cannot infer nil value for array kind interface")
}
- return res, nil
+ values := make([]interface{}, reflectVal.Len())
+ for i := 0; i < reflectVal.Len(); i++ {
+ values[i] = reflectVal.Index(i).Interface()
+ }
+ return values, nil
}
-// tupleEncoding encodes an ABI value of tuple type into an ABI encoded byte string.
-func tupleEncoding(v Value) ([]byte, error) {
- if v.ABIType.abiTypeID != Tuple {
- return []byte{}, fmt.Errorf("type not supported in tupleEncoding")
+// encodeTuple encodes slice-of-interface of golang values to bytes, following ABI encoding rules
+func encodeTuple(value interface{}, childT []Type) ([]byte, error) {
+ if len(childT) >= (1 << 16) {
+ return nil, fmt.Errorf("abi child type number exceeds uint16 maximum")
}
- if len(v.ABIType.childTypes) >= (1 << 16) {
- return []byte{}, fmt.Errorf("value abi type exceed 2^16")
+ values, err := inferToSlice(value)
+ if err != nil {
+ return nil, err
}
- tupleElems := v.value.([]Value)
- if len(tupleElems) != len(v.ABIType.childTypes) {
- return []byte{}, fmt.Errorf("tuple abi child type number unmatch with tuple argument number")
+ if len(values) != len(childT) {
+ return nil, fmt.Errorf("cannot encode abi tuple: value slice length != child type number")
}
// for each tuple element value, it has a head/tail component
// we create slots for head/tail bytes now, store them and concat them later
- heads := make([][]byte, len(v.ABIType.childTypes))
- tails := make([][]byte, len(v.ABIType.childTypes))
+ heads := make([][]byte, len(childT))
+ tails := make([][]byte, len(childT))
isDynamicIndex := make(map[int]bool)
- for i := 0; i < len(v.ABIType.childTypes); i++ {
- if tupleElems[i].ABIType.IsDynamic() {
+ for i := 0; i < len(childT); i++ {
+ if childT[i].IsDynamic() {
// if it is a dynamic value, the head component is not pre-determined
// we store an empty placeholder first, since we will need it in byte length calculation
headsPlaceholder := []byte{0x00, 0x00}
heads[i] = headsPlaceholder
// we keep track that the index points to a dynamic value
isDynamicIndex[i] = true
- tailEncoding, err := tupleElems[i].Encode()
+ tailEncoding, err := childT[i].Encode(values[i])
if err != nil {
- return []byte{}, err
+ return nil, err
}
tails[i] = tailEncoding
+ isDynamicIndex[i] = true
+ } else if childT[i].abiTypeID == Bool {
+ // search previous bool
+ before := findBoolLR(childT, i, -1)
+ // search after bool
+ after := findBoolLR(childT, i, 1)
+ // append to heads and tails
+ if before%8 != 0 {
+ return nil, fmt.Errorf("cannot encode abi tuple: expected before has number of bool mod 8 == 0")
+ }
+ if after > 7 {
+ after = 7
+ }
+ compressed, err := compressBools(values[i : i+after+1])
+ if err != nil {
+ return nil, err
+ }
+ heads[i] = []byte{compressed}
+ i += after
+ isDynamicIndex[i] = false
} else {
- if tupleElems[i].ABIType.abiTypeID == Bool {
- // search previous bool
- before := findBoolLR(v.ABIType.childTypes, i, -1)
- // search after bool
- after := findBoolLR(v.ABIType.childTypes, i, 1)
- // append to heads and tails
- if before%8 != 0 {
- return []byte{}, fmt.Errorf("expected before has number of bool mod 8 = 0")
- }
- if after > 7 {
- after = 7
- }
- compressed, err := compressMultipleBool(tupleElems[i : i+after+1])
- if err != nil {
- return []byte{}, err
- }
- heads[i] = []byte{compressed}
- i += after
- } else {
- encodeTi, err := tupleElems[i].Encode()
- if err != nil {
- return []byte{}, err
- }
- heads[i] = encodeTi
+ encodeTi, err := childT[i].Encode(values[i])
+ if err != nil {
+ return nil, err
}
+ heads[i] = encodeTi
isDynamicIndex[i] = false
}
}
@@ -249,7 +277,7 @@ func tupleEncoding(v Value) ([]byte, error) {
// calculate where the index of dynamic value encoding byte start
headValue := headLength + tailCurrLength
if headValue >= (1 << 16) {
- return []byte{}, fmt.Errorf("encoding error: byte length exceed 2^16")
+ return nil, fmt.Errorf("cannot encode abi tuple: encode length exceeds uint16 maximum")
}
binary.BigEndian.PutUint16(heads[i], uint16(headValue))
}
@@ -268,203 +296,254 @@ func tupleEncoding(v Value) ([]byte, error) {
return encoded, nil
}
-// Decode takes an ABI encoded byte string and a target ABI type,
-// and decodes the bytes into an ABI Value.
-func Decode(valueByte []byte, valueType Type) (Value, error) {
- switch valueType.abiTypeID {
- case Uint:
- if len(valueByte) != int(valueType.bitSize)/8 {
- return Value{},
- fmt.Errorf("uint%d decode: expected byte length %d, but got byte length %d",
- valueType.bitSize, valueType.bitSize/8, len(valueByte))
- }
- uintValue := new(big.Int).SetBytes(valueByte)
- return MakeUint(uintValue, valueType.bitSize)
- case Ufixed:
- if len(valueByte) != int(valueType.bitSize)/8 {
- return Value{},
- fmt.Errorf("ufixed%dx%d decode: expected length %d, got byte length %d",
- valueType.bitSize, valueType.precision, valueType.bitSize/8, len(valueByte))
- }
- ufixedNumerator := new(big.Int).SetBytes(valueByte)
- return MakeUfixed(ufixedNumerator, valueType.bitSize, valueType.precision)
+// compressBools takes a slice of interface{} (which can be casted to bools) length <= 8
+// and compress the bool values into a uint8 integer
+func compressBools(boolSlice []interface{}) (uint8, error) {
+ var res uint8 = 0
+ if len(boolSlice) > 8 {
+ return 0, fmt.Errorf("compressBools: cannot have slice length > 8")
+ }
+ for i := 0; i < len(boolSlice); i++ {
+ temp, ok := boolSlice[i].(bool)
+ if !ok {
+ return 0, fmt.Errorf("compressBools: cannot cast slice element to bool")
+ }
+ if temp {
+ res |= 1 << uint(7-i)
+ }
+ }
+ return res, nil
+}
+
+// decodeUint decodes byte slice into golang int/big.Int
+func decodeUint(encoded []byte, bitSize uint16) (interface{}, error) {
+ if len(encoded) != int(bitSize)/8 {
+ return nil,
+ fmt.Errorf("uint/ufixed decode: expected byte length %d, but got byte length %d", bitSize/8, len(encoded))
+ }
+ switch bitSize / 8 {
+ case 1:
+ return encoded[0], nil
+ case 2:
+ return uint16(new(big.Int).SetBytes(encoded).Uint64()), nil
+ case 3, 4:
+ return uint32(new(big.Int).SetBytes(encoded).Uint64()), nil
+ case 5, 6, 7, 8:
+ return new(big.Int).SetBytes(encoded).Uint64(), nil
+ default:
+ return new(big.Int).SetBytes(encoded), nil
+ }
+}
+
+// Decode is an ABI type method to decode bytes to go values from ABI encoding rules
+func (t Type) Decode(encoded []byte) (interface{}, error) {
+ switch t.abiTypeID {
+ case Uint, Ufixed:
+ return decodeUint(encoded, t.bitSize)
case Bool:
- if len(valueByte) != 1 {
- return Value{}, fmt.Errorf("boolean byte should be length 1 byte")
- }
- var boolValue bool
- if valueByte[0] == 0x00 {
- boolValue = false
- } else if valueByte[0] == 0x80 {
- boolValue = true
- } else {
- return Value{}, fmt.Errorf("sinble boolean encoded byte should be of form 0x80 or 0x00")
+ if len(encoded) != 1 {
+ return nil, fmt.Errorf("boolean byte should be length 1 byte")
+ }
+ if encoded[0] == 0x00 {
+ return false, nil
+ } else if encoded[0] == 0x80 {
+ return true, nil
}
- return MakeBool(boolValue), nil
+ return nil, fmt.Errorf("single boolean encoded byte should be of form 0x80 or 0x00")
case Byte:
- if len(valueByte) != 1 {
- return Value{}, fmt.Errorf("byte should be length 1")
+ if len(encoded) != 1 {
+ return nil, fmt.Errorf("byte should be length 1")
}
- return MakeByte(valueByte[0]), nil
+ return encoded[0], nil
case ArrayStatic:
- childT := make([]Type, valueType.staticLength)
- for i := 0; i < int(valueType.staticLength); i++ {
- childT[i] = valueType.childTypes[0]
- }
- converted, err := MakeTupleType(childT)
+ castedType, err := t.typeCastToTuple()
if err != nil {
- return Value{}, err
+ return nil, err
}
- tupleDecoded, err := tupleDecoding(valueByte, converted)
- if err != nil {
- return Value{}, err
- }
- tupleDecoded.ABIType = valueType
- return tupleDecoded, nil
+ return castedType.Decode(encoded)
case Address:
- if len(valueByte) != addressByteSize {
- return Value{}, fmt.Errorf("address should be length 32")
+ if len(encoded) != addressByteSize {
+ return nil, fmt.Errorf("address should be length 32")
}
- var byteAssign [addressByteSize]byte
- copy(byteAssign[:], valueByte)
- return MakeAddress(byteAssign), nil
+ return encoded, nil
case ArrayDynamic:
- if len(valueByte) < lengthEncodeByteSize {
- return Value{}, fmt.Errorf("dynamic array format corrupted")
- }
- dynamicLen := binary.BigEndian.Uint16(valueByte[:lengthEncodeByteSize])
- childT := make([]Type, dynamicLen)
- for i := 0; i < int(dynamicLen); i++ {
- childT[i] = valueType.childTypes[0]
- }
- converted, err := MakeTupleType(childT)
- if err != nil {
- return Value{}, err
+ if len(encoded) < lengthEncodeByteSize {
+ return nil, fmt.Errorf("dynamic array format corrupted")
}
- tupleDecoded, err := tupleDecoding(valueByte[lengthEncodeByteSize:], converted)
+ dynamicLen := binary.BigEndian.Uint16(encoded[:lengthEncodeByteSize])
+ castedType, err := t.typeCastToTuple(int(dynamicLen))
if err != nil {
- return Value{}, err
+ return nil, err
}
- tupleDecoded.ABIType = valueType
- return tupleDecoded, nil
+ return castedType.Decode(encoded[lengthEncodeByteSize:])
case String:
- if len(valueByte) < lengthEncodeByteSize {
- return Value{}, fmt.Errorf("string format corrupted")
+ if len(encoded) < lengthEncodeByteSize {
+ return nil, fmt.Errorf("string format corrupted")
}
- stringLenBytes := valueByte[:lengthEncodeByteSize]
+ stringLenBytes := encoded[:lengthEncodeByteSize]
byteLen := binary.BigEndian.Uint16(stringLenBytes)
- if len(valueByte[lengthEncodeByteSize:]) != int(byteLen) {
- return Value{}, fmt.Errorf("string representation in byte: length not matching")
+ if len(encoded[lengthEncodeByteSize:]) != int(byteLen) {
+ return nil, fmt.Errorf("string representation in byte: length not matching")
}
- return MakeString(string(valueByte[lengthEncodeByteSize:])), nil
+ return string(encoded[lengthEncodeByteSize:]), nil
case Tuple:
- return tupleDecoding(valueByte, valueType)
+ return decodeTuple(encoded, t.childTypes)
default:
- return Value{}, fmt.Errorf("decode: unknown type error")
+ return nil, fmt.Errorf("cannot infer type for decoding")
}
}
-// tupleDecoding takes a byte string and an ABI tuple type,
-// and decodes the bytes into an ABI tuple value.
-func tupleDecoding(valueBytes []byte, valueType Type) (Value, error) {
- dynamicSegments := make([]segment, 0)
- valuePartition := make([][]byte, 0)
+// decodeTuple decodes byte slice with ABI type slice, outputting a slice of golang interface values
+// following ABI encoding rules
+func decodeTuple(encoded []byte, childT []Type) ([]interface{}, error) {
+ dynamicSegments := make([]int, 0, len(childT)+1)
+ valuePartition := make([][]byte, 0, len(childT))
iterIndex := 0
- for i := 0; i < len(valueType.childTypes); i++ {
- if valueType.childTypes[i].IsDynamic() {
- if len(valueBytes[iterIndex:]) < lengthEncodeByteSize {
- return Value{}, fmt.Errorf("ill formed tuple dynamic typed value encoding")
- }
- dynamicIndex := binary.BigEndian.Uint16(valueBytes[iterIndex : iterIndex+lengthEncodeByteSize])
- if len(dynamicSegments) > 0 {
- dynamicSegments[len(dynamicSegments)-1].right = int(dynamicIndex)
+ for i := 0; i < len(childT); i++ {
+ if childT[i].IsDynamic() {
+ if len(encoded[iterIndex:]) < lengthEncodeByteSize {
+ return nil, fmt.Errorf("ill formed tuple dynamic typed value encoding")
}
- // we know where encoded bytes for dynamic value start, but we do not know where it ends
- // unless we see the start of the next encoded bytes for dynamic value
- dynamicSegments = append(dynamicSegments, segment{
- left: int(dynamicIndex),
- right: -1,
- })
+ dynamicIndex := binary.BigEndian.Uint16(encoded[iterIndex : iterIndex+lengthEncodeByteSize])
+ dynamicSegments = append(dynamicSegments, int(dynamicIndex))
valuePartition = append(valuePartition, nil)
iterIndex += lengthEncodeByteSize
- } else {
- // if bool ...
- if valueType.childTypes[i].abiTypeID == Bool {
- // search previous bool
- before := findBoolLR(valueType.childTypes, i, -1)
- // search after bool
- after := findBoolLR(valueType.childTypes, i, 1)
- if before%8 == 0 {
- if after > 7 {
- after = 7
- }
- // parse bool in a byte to multiple byte strings
- for boolIndex := uint(0); boolIndex <= uint(after); boolIndex++ {
- boolMask := 0x80 >> boolIndex
- if valueBytes[iterIndex]&byte(boolMask) > 0 {
- valuePartition = append(valuePartition, []byte{0x80})
- } else {
- valuePartition = append(valuePartition, []byte{0x00})
- }
+ } else if childT[i].abiTypeID == Bool {
+ // search previous bool
+ before := findBoolLR(childT, i, -1)
+ // search after bool
+ after := findBoolLR(childT, i, 1)
+ if before%8 == 0 {
+ if after > 7 {
+ after = 7
+ }
+ // parse bool in a byte to multiple byte strings
+ for boolIndex := uint(0); boolIndex <= uint(after); boolIndex++ {
+ boolMask := 0x80 >> boolIndex
+ if encoded[iterIndex]&byte(boolMask) > 0 {
+ valuePartition = append(valuePartition, []byte{0x80})
+ } else {
+ valuePartition = append(valuePartition, []byte{0x00})
}
- i += after
- iterIndex++
- } else {
- return Value{}, fmt.Errorf("expected before bool number mod 8 == 0")
}
+ i += after
+ iterIndex++
} else {
- // not bool ...
- currLen, err := valueType.childTypes[i].ByteLen()
- if err != nil {
- return Value{}, err
- }
- valuePartition = append(valuePartition, valueBytes[iterIndex:iterIndex+currLen])
- iterIndex += currLen
+ return nil, fmt.Errorf("expected before bool number mod 8 == 0")
}
+ } else {
+ // not bool ...
+ currLen, err := childT[i].ByteLen()
+ if err != nil {
+ return nil, err
+ }
+ valuePartition = append(valuePartition, encoded[iterIndex:iterIndex+currLen])
+ iterIndex += currLen
}
- if i != len(valueType.childTypes)-1 && iterIndex >= len(valueBytes) {
- return Value{}, fmt.Errorf("input byte not enough to decode")
+ if i != len(childT)-1 && iterIndex >= len(encoded) {
+ return nil, fmt.Errorf("input byte not enough to decode")
}
}
+
if len(dynamicSegments) > 0 {
- dynamicSegments[len(dynamicSegments)-1].right = len(valueBytes)
- iterIndex = len(valueBytes)
+ dynamicSegments = append(dynamicSegments, len(encoded))
+ iterIndex = len(encoded)
}
- if iterIndex < len(valueBytes) {
- return Value{}, fmt.Errorf("input byte not fully consumed")
+ if iterIndex < len(encoded) {
+ return nil, fmt.Errorf("input byte not fully consumed")
}
-
- // check segment indices are valid
- // if the dynamic segment are not consecutive and well-ordered, we return error
- for index, seg := range dynamicSegments {
- if seg.left > seg.right {
- return Value{}, fmt.Errorf("dynamic segment should display a [l, r] space with l <= r")
- }
- if index != len(dynamicSegments)-1 && seg.right != dynamicSegments[index+1].left {
- return Value{}, fmt.Errorf("dynamic segment should be consecutive")
+ for i := 0; i < len(dynamicSegments)-1; i++ {
+ if dynamicSegments[i] > dynamicSegments[i+1] {
+ return nil, fmt.Errorf("dynamic segment should display a [l, r] space with l <= r")
}
}
segIndex := 0
- for i := 0; i < len(valueType.childTypes); i++ {
- if valueType.childTypes[i].IsDynamic() {
- valuePartition[i] = valueBytes[dynamicSegments[segIndex].left:dynamicSegments[segIndex].right]
+ for i := 0; i < len(childT); i++ {
+ if childT[i].IsDynamic() {
+ valuePartition[i] = encoded[dynamicSegments[segIndex]:dynamicSegments[segIndex+1]]
segIndex++
}
}
- // decode each tuple element bytes
- values := make([]Value, 0)
- for i := 0; i < len(valueType.childTypes); i++ {
- valueTi, err := Decode(valuePartition[i], valueType.childTypes[i])
+ values := make([]interface{}, len(childT))
+ for i := 0; i < len(childT); i++ {
+ var err error
+ values[i], err = childT[i].Decode(valuePartition[i])
if err != nil {
- return Value{}, err
+ return nil, err
+ }
+ }
+ return values, nil
+}
+
+// ParseArgJSONtoByteSlice convert input method arguments to ABI encoded bytes
+// it converts funcArgTypes into a tuple type and apply changes over input argument string (in JSON format)
+// if there are greater or equal to 15 inputs, then we compact the tailing inputs into one tuple
+func ParseArgJSONtoByteSlice(funcArgTypes string, jsonArgs []string, applicationArgs *[][]byte) error {
+ abiTupleT, err := TypeOf(funcArgTypes)
+ if err != nil {
+ return err
+ }
+ if len(abiTupleT.childTypes) != len(jsonArgs) {
+ return fmt.Errorf("input argument number %d != method argument number %d", len(jsonArgs), len(abiTupleT.childTypes))
+ }
+
+ // change the input args to be 1 - 14 + 15 (compacting everything together)
+ if len(jsonArgs) > 14 {
+ compactedType, err := MakeTupleType(abiTupleT.childTypes[14:])
+ if err != nil {
+ return err
+ }
+ abiTupleT.childTypes = abiTupleT.childTypes[:14]
+ abiTupleT.childTypes = append(abiTupleT.childTypes, compactedType)
+ abiTupleT.staticLength = 15
+
+ remainingJSON := "[" + strings.Join(jsonArgs[14:], ",") + "]"
+ jsonArgs = jsonArgs[:14]
+ jsonArgs = append(jsonArgs, remainingJSON)
+ }
+
+ // parse JSON value to ABI encoded bytes
+ for i := 0; i < len(jsonArgs); i++ {
+ interfaceVal, err := abiTupleT.childTypes[i].UnmarshalFromJSON([]byte(jsonArgs[i]))
+ if err != nil {
+ return err
+ }
+ abiEncoded, err := abiTupleT.childTypes[i].Encode(interfaceVal)
+ if err != nil {
+ return err
+ }
+ *applicationArgs = append(*applicationArgs, abiEncoded)
+ }
+ return nil
+}
+
+// ParseMethodSignature parses a method of format `method(...argTypes...)retType`
+// into `(...argTypes)` and `retType`
+func ParseMethodSignature(methodSig string) (string, string, error) {
+ var stack []int
+
+ for index, chr := range methodSig {
+ if chr == '(' {
+ stack = append(stack, index)
+ } else if chr == ')' {
+ if len(stack) == 0 {
+ break
+ }
+ leftParenIndex := stack[len(stack)-1]
+ stack = stack[:len(stack)-1]
+ if len(stack) == 0 {
+ returnType := methodSig[index+1:]
+ if _, err := TypeOf(returnType); err != nil {
+ if returnType != "void" {
+ return "", "", fmt.Errorf("cannot infer return type: %s", returnType)
+ }
+ }
+ return methodSig[leftParenIndex : index+1], methodSig[index+1:], nil
+ }
}
- values = append(values, valueTi)
}
- return Value{
- ABIType: valueType,
- value: values,
- }, nil
+ return "", "", fmt.Errorf("unpaired parentheses: %s", methodSig)
}