github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/accounts/abi/unpack.go (about) 1 // This file is part of the go-sberex library. The go-sberex library is 2 // free software: you can redistribute it and/or modify it under the terms 3 // of the GNU Lesser General Public License as published by the Free 4 // Software Foundation, either version 3 of the License, or (at your option) 5 // any later version. 6 // 7 // The go-sberex library is distributed in the hope that it will be useful, 8 // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 // General Public License <http://www.gnu.org/licenses/> for more details. 11 12 package abi 13 14 import ( 15 "encoding/binary" 16 "fmt" 17 "math/big" 18 "reflect" 19 20 "github.com/Sberex/go-sberex/common" 21 ) 22 23 // reads the integer based on its kind 24 func readInteger(kind reflect.Kind, b []byte) interface{} { 25 switch kind { 26 case reflect.Uint8: 27 return b[len(b)-1] 28 case reflect.Uint16: 29 return binary.BigEndian.Uint16(b[len(b)-2:]) 30 case reflect.Uint32: 31 return binary.BigEndian.Uint32(b[len(b)-4:]) 32 case reflect.Uint64: 33 return binary.BigEndian.Uint64(b[len(b)-8:]) 34 case reflect.Int8: 35 return int8(b[len(b)-1]) 36 case reflect.Int16: 37 return int16(binary.BigEndian.Uint16(b[len(b)-2:])) 38 case reflect.Int32: 39 return int32(binary.BigEndian.Uint32(b[len(b)-4:])) 40 case reflect.Int64: 41 return int64(binary.BigEndian.Uint64(b[len(b)-8:])) 42 default: 43 return new(big.Int).SetBytes(b) 44 } 45 } 46 47 // reads a bool 48 func readBool(word []byte) (bool, error) { 49 for _, b := range word[:31] { 50 if b != 0 { 51 return false, errBadBool 52 } 53 } 54 switch word[31] { 55 case 0: 56 return false, nil 57 case 1: 58 return true, nil 59 default: 60 return false, errBadBool 61 } 62 } 63 64 // A function type is simply the address with the function selection signature at the end. 65 // This enforces that standard by always presenting it as a 24-array (address + sig = 24 bytes) 66 func readFunctionType(t Type, word []byte) (funcTy [24]byte, err error) { 67 if t.T != FunctionTy { 68 return [24]byte{}, fmt.Errorf("abi: invalid type in call to make function type byte array") 69 } 70 if garbage := binary.BigEndian.Uint64(word[24:32]); garbage != 0 { 71 err = fmt.Errorf("abi: got improperly encoded function type, got %v", word) 72 } else { 73 copy(funcTy[:], word[0:24]) 74 } 75 return 76 } 77 78 // through reflection, creates a fixed array to be read from 79 func readFixedBytes(t Type, word []byte) (interface{}, error) { 80 if t.T != FixedBytesTy { 81 return nil, fmt.Errorf("abi: invalid type in call to make fixed byte array") 82 } 83 // convert 84 array := reflect.New(t.Type).Elem() 85 86 reflect.Copy(array, reflect.ValueOf(word[0:t.Size])) 87 return array.Interface(), nil 88 89 } 90 91 // iteratively unpack elements 92 func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error) { 93 if size < 0 { 94 return nil, fmt.Errorf("cannot marshal input to array, size is negative (%d)", size) 95 } 96 if start+32*size > len(output) { 97 return nil, fmt.Errorf("abi: cannot marshal in to go array: offset %d would go over slice boundary (len=%d)", len(output), start+32*size) 98 } 99 100 // this value will become our slice or our array, depending on the type 101 var refSlice reflect.Value 102 slice := output[start : start+size*32] 103 104 if t.T == SliceTy { 105 // declare our slice 106 refSlice = reflect.MakeSlice(t.Type, size, size) 107 } else if t.T == ArrayTy { 108 // declare our array 109 refSlice = reflect.New(t.Type).Elem() 110 } else { 111 return nil, fmt.Errorf("abi: invalid type in array/slice unpacking stage") 112 } 113 114 for i, j := start, 0; j*32 < len(slice); i, j = i+32, j+1 { 115 // this corrects the arrangement so that we get all the underlying array values 116 if t.Elem.T == ArrayTy && j != 0 { 117 i = start + t.Elem.Size*32*j 118 } 119 inter, err := toGoType(i, *t.Elem, output) 120 if err != nil { 121 return nil, err 122 } 123 // append the item to our reflect slice 124 refSlice.Index(j).Set(reflect.ValueOf(inter)) 125 } 126 127 // return the interface 128 return refSlice.Interface(), nil 129 } 130 131 // toGoType parses the output bytes and recursively assigns the value of these bytes 132 // into a go type with accordance with the ABI spec. 133 func toGoType(index int, t Type, output []byte) (interface{}, error) { 134 if index+32 > len(output) { 135 return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), index+32) 136 } 137 138 var ( 139 returnOutput []byte 140 begin, end int 141 err error 142 ) 143 144 // if we require a length prefix, find the beginning word and size returned. 145 if t.requiresLengthPrefix() { 146 begin, end, err = lengthPrefixPointsTo(index, output) 147 if err != nil { 148 return nil, err 149 } 150 } else { 151 returnOutput = output[index : index+32] 152 } 153 154 switch t.T { 155 case SliceTy: 156 return forEachUnpack(t, output, begin, end) 157 case ArrayTy: 158 return forEachUnpack(t, output, index, t.Size) 159 case StringTy: // variable arrays are written at the end of the return bytes 160 return string(output[begin : begin+end]), nil 161 case IntTy, UintTy: 162 return readInteger(t.Kind, returnOutput), nil 163 case BoolTy: 164 return readBool(returnOutput) 165 case AddressTy: 166 return common.BytesToAddress(returnOutput), nil 167 case HashTy: 168 return common.BytesToHash(returnOutput), nil 169 case BytesTy: 170 return output[begin : begin+end], nil 171 case FixedBytesTy: 172 return readFixedBytes(t, returnOutput) 173 case FunctionTy: 174 return readFunctionType(t, returnOutput) 175 default: 176 return nil, fmt.Errorf("abi: unknown type %v", t.T) 177 } 178 } 179 180 // interprets a 32 byte slice as an offset and then determines which indice to look to decode the type. 181 func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err error) { 182 bigOffsetEnd := big.NewInt(0).SetBytes(output[index : index+32]) 183 bigOffsetEnd.Add(bigOffsetEnd, common.Big32) 184 outputLength := big.NewInt(int64(len(output))) 185 186 if bigOffsetEnd.Cmp(outputLength) > 0 { 187 return 0, 0, fmt.Errorf("abi: cannot marshal in to go slice: offset %v would go over slice boundary (len=%v)", bigOffsetEnd, outputLength) 188 } 189 190 if bigOffsetEnd.BitLen() > 63 { 191 return 0, 0, fmt.Errorf("abi offset larger than int64: %v", bigOffsetEnd) 192 } 193 194 offsetEnd := int(bigOffsetEnd.Uint64()) 195 lengthBig := big.NewInt(0).SetBytes(output[offsetEnd-32 : offsetEnd]) 196 197 totalSize := big.NewInt(0) 198 totalSize.Add(totalSize, bigOffsetEnd) 199 totalSize.Add(totalSize, lengthBig) 200 if totalSize.BitLen() > 63 { 201 return 0, 0, fmt.Errorf("abi length larger than int64: %v", totalSize) 202 } 203 204 if totalSize.Cmp(outputLength) > 0 { 205 return 0, 0, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %v require %v", outputLength, totalSize) 206 } 207 start = int(bigOffsetEnd.Uint64()) 208 length = int(lengthBig.Uint64()) 209 return 210 }