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