github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/accounts/abi/argument.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/json" 16 "fmt" 17 "reflect" 18 "strings" 19 ) 20 21 // Argument holds the name of the argument and the corresponding type. 22 // Types are used when packing and testing arguments. 23 type Argument struct { 24 Name string 25 Type Type 26 Indexed bool // indexed is only used by events 27 } 28 29 type Arguments []Argument 30 31 // UnmarshalJSON implements json.Unmarshaler interface 32 func (argument *Argument) UnmarshalJSON(data []byte) error { 33 var extarg struct { 34 Name string 35 Type string 36 Indexed bool 37 } 38 err := json.Unmarshal(data, &extarg) 39 if err != nil { 40 return fmt.Errorf("argument json err: %v", err) 41 } 42 43 argument.Type, err = NewType(extarg.Type) 44 if err != nil { 45 return err 46 } 47 argument.Name = extarg.Name 48 argument.Indexed = extarg.Indexed 49 50 return nil 51 } 52 53 // LengthNonIndexed returns the number of arguments when not counting 'indexed' ones. Only events 54 // can ever have 'indexed' arguments, it should always be false on arguments for method input/output 55 func (arguments Arguments) LengthNonIndexed() int { 56 out := 0 57 for _, arg := range arguments { 58 if !arg.Indexed { 59 out++ 60 } 61 } 62 return out 63 } 64 65 // NonIndexed returns the arguments with indexed arguments filtered out 66 func (arguments Arguments) NonIndexed() Arguments { 67 var ret []Argument 68 for _, arg := range arguments { 69 if !arg.Indexed { 70 ret = append(ret, arg) 71 } 72 } 73 return ret 74 } 75 76 // isTuple returns true for non-atomic constructs, like (uint,uint) or uint[] 77 func (arguments Arguments) isTuple() bool { 78 return len(arguments) > 1 79 } 80 81 // Unpack performs the operation hexdata -> Go format 82 func (arguments Arguments) Unpack(v interface{}, data []byte) error { 83 84 // make sure the passed value is arguments pointer 85 if reflect.Ptr != reflect.ValueOf(v).Kind() { 86 return fmt.Errorf("abi: Unpack(non-pointer %T)", v) 87 } 88 marshalledValues, err := arguments.UnpackValues(data) 89 if err != nil { 90 return err 91 } 92 if arguments.isTuple() { 93 return arguments.unpackTuple(v, marshalledValues) 94 } 95 return arguments.unpackAtomic(v, marshalledValues) 96 } 97 98 func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interface{}) error { 99 100 var ( 101 value = reflect.ValueOf(v).Elem() 102 typ = value.Type() 103 kind = value.Kind() 104 ) 105 106 if err := requireUnpackKind(value, typ, kind, arguments); err != nil { 107 return err 108 } 109 // If the output interface is a struct, make sure names don't collide 110 if kind == reflect.Struct { 111 exists := make(map[string]bool) 112 for _, arg := range arguments { 113 field := capitalise(arg.Name) 114 if field == "" { 115 return fmt.Errorf("abi: purely underscored output cannot unpack to struct") 116 } 117 if exists[field] { 118 return fmt.Errorf("abi: multiple outputs mapping to the same struct field '%s'", field) 119 } 120 exists[field] = true 121 } 122 } 123 for i, arg := range arguments.NonIndexed() { 124 125 reflectValue := reflect.ValueOf(marshalledValues[i]) 126 127 switch kind { 128 case reflect.Struct: 129 name := capitalise(arg.Name) 130 for j := 0; j < typ.NumField(); j++ { 131 // TODO read tags: `abi:"fieldName"` 132 if typ.Field(j).Name == name { 133 if err := set(value.Field(j), reflectValue, arg); err != nil { 134 return err 135 } 136 } 137 } 138 case reflect.Slice, reflect.Array: 139 if value.Len() < i { 140 return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(arguments), value.Len()) 141 } 142 v := value.Index(i) 143 if err := requireAssignable(v, reflectValue); err != nil { 144 return err 145 } 146 147 if err := set(v.Elem(), reflectValue, arg); err != nil { 148 return err 149 } 150 default: 151 return fmt.Errorf("abi:[2] cannot unmarshal tuple in to %v", typ) 152 } 153 } 154 return nil 155 } 156 157 // unpackAtomic unpacks ( hexdata -> go ) a single value 158 func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues []interface{}) error { 159 if len(marshalledValues) != 1 { 160 return fmt.Errorf("abi: wrong length, expected single value, got %d", len(marshalledValues)) 161 } 162 elem := reflect.ValueOf(v).Elem() 163 reflectValue := reflect.ValueOf(marshalledValues[0]) 164 return set(elem, reflectValue, arguments.NonIndexed()[0]) 165 } 166 167 // UnpackValues can be used to unpack ABI-encoded hexdata according to the ABI-specification, 168 // without supplying a struct to unpack into. Instead, this method returns a list containing the 169 // values. An atomic argument will be a list with one element. 170 func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) { 171 retval := make([]interface{}, 0, arguments.LengthNonIndexed()) 172 virtualArgs := 0 173 for index, arg := range arguments.NonIndexed() { 174 marshalledValue, err := toGoType((index+virtualArgs)*32, arg.Type, data) 175 if arg.Type.T == ArrayTy { 176 // If we have a static array, like [3]uint256, these are coded as 177 // just like uint256,uint256,uint256. 178 // This means that we need to add two 'virtual' arguments when 179 // we count the index from now on 180 181 virtualArgs += arg.Type.Size - 1 182 } 183 if err != nil { 184 return nil, err 185 } 186 retval = append(retval, marshalledValue) 187 } 188 return retval, nil 189 } 190 191 // PackValues performs the operation Go format -> Hexdata 192 // It is the semantic opposite of UnpackValues 193 func (arguments Arguments) PackValues(args []interface{}) ([]byte, error) { 194 return arguments.Pack(args...) 195 } 196 197 // Pack performs the operation Go format -> Hexdata 198 func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) { 199 // Make sure arguments match up and pack them 200 abiArgs := arguments 201 if len(args) != len(abiArgs) { 202 return nil, fmt.Errorf("argument count mismatch: %d for %d", len(args), len(abiArgs)) 203 } 204 // variable input is the output appended at the end of packed 205 // output. This is used for strings and bytes types input. 206 var variableInput []byte 207 208 // input offset is the bytes offset for packed output 209 inputOffset := 0 210 for _, abiArg := range abiArgs { 211 if abiArg.Type.T == ArrayTy { 212 inputOffset += 32 * abiArg.Type.Size 213 } else { 214 inputOffset += 32 215 } 216 } 217 var ret []byte 218 for i, a := range args { 219 input := abiArgs[i] 220 // pack the input 221 packed, err := input.Type.pack(reflect.ValueOf(a)) 222 if err != nil { 223 return nil, err 224 } 225 // check for a slice type (string, bytes, slice) 226 if input.Type.requiresLengthPrefix() { 227 // calculate the offset 228 offset := inputOffset + len(variableInput) 229 // set the offset 230 ret = append(ret, packNum(reflect.ValueOf(offset))...) 231 // Append the packed output to the variable input. The variable input 232 // will be appended at the end of the input. 233 variableInput = append(variableInput, packed...) 234 } else { 235 // append the packed value to the input 236 ret = append(ret, packed...) 237 } 238 } 239 // append the variable input at the end of the packed input 240 ret = append(ret, variableInput...) 241 242 return ret, nil 243 } 244 245 // capitalise makes the first character of a string upper case, also removing any 246 // prefixing underscores from the variable names. 247 func capitalise(input string) string { 248 for len(input) > 0 && input[0] == '_' { 249 input = input[1:] 250 } 251 if len(input) == 0 { 252 return "" 253 } 254 return strings.ToUpper(input[:1]) + input[1:] 255 }