github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/chain/accounts/abi/type.go (about) 1 package abi 2 3 import ( 4 "errors" 5 "fmt" 6 "reflect" 7 "regexp" 8 "strconv" 9 "strings" 10 ) 11 12 const ( 13 IntTy byte = iota 14 UintTy 15 BoolTy 16 StringTy 17 SliceTy 18 ArrayTy 19 TupleTy 20 AddressTy 21 FixedBytesTy 22 BytesTy 23 HashTy 24 FixedPointTy 25 FunctionTy 26 ) 27 28 type Type struct { 29 Elem *Type 30 Kind reflect.Kind 31 Type reflect.Type 32 Size int 33 T byte 34 35 stringKind string 36 37 TupleRawName string 38 TupleElems []*Type 39 TupleRawNames []string 40 } 41 42 var ( 43 typeRegex = regexp.MustCompile("([a-zA-Z]+)(([0-9]+)(x([0-9]+))?)?") 44 ) 45 46 func NewType(t string, internalType string, components []ArgumentMarshaling) (typ Type, err error) { 47 48 if strings.Count(t, "[") != strings.Count(t, "]") { 49 return Type{}, fmt.Errorf("invalid arg type in abi") 50 } 51 typ.stringKind = t 52 53 if strings.Count(t, "[") != 0 { 54 55 subInternal := internalType 56 if i := strings.LastIndex(internalType, "["); i != -1 { 57 subInternal = subInternal[:i] 58 } 59 60 i := strings.LastIndex(t, "[") 61 embeddedType, err := NewType(t[:i], subInternal, components) 62 if err != nil { 63 return Type{}, err 64 } 65 66 sliced := t[i:] 67 68 re := regexp.MustCompile("[0-9]+") 69 intz := re.FindAllString(sliced, -1) 70 71 if len(intz) == 0 { 72 73 typ.T = SliceTy 74 typ.Kind = reflect.Slice 75 typ.Elem = &embeddedType 76 typ.Type = reflect.SliceOf(embeddedType.Type) 77 typ.stringKind = embeddedType.stringKind + sliced 78 } else if len(intz) == 1 { 79 80 typ.T = ArrayTy 81 typ.Kind = reflect.Array 82 typ.Elem = &embeddedType 83 typ.Size, err = strconv.Atoi(intz[0]) 84 if err != nil { 85 return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err) 86 } 87 typ.Type = reflect.ArrayOf(typ.Size, embeddedType.Type) 88 typ.stringKind = embeddedType.stringKind + sliced 89 } else { 90 return Type{}, fmt.Errorf("invalid formatting of array type") 91 } 92 return typ, err 93 } 94 95 matches := typeRegex.FindAllStringSubmatch(t, -1) 96 if len(matches) == 0 { 97 return Type{}, fmt.Errorf("invalid type '%v'", t) 98 } 99 parsedType := matches[0] 100 101 var varSize int 102 if len(parsedType[3]) > 0 { 103 var err error 104 varSize, err = strconv.Atoi(parsedType[2]) 105 if err != nil { 106 return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err) 107 } 108 } else { 109 if parsedType[0] == "uint" || parsedType[0] == "int" { 110 111 return Type{}, fmt.Errorf("unsupported arg type: %s", t) 112 } 113 } 114 115 switch varType := parsedType[1]; varType { 116 case "int": 117 typ.Kind, typ.Type = reflectIntKindAndType(false, varSize) 118 typ.Size = varSize 119 typ.T = IntTy 120 case "uint": 121 typ.Kind, typ.Type = reflectIntKindAndType(true, varSize) 122 typ.Size = varSize 123 typ.T = UintTy 124 case "bool": 125 typ.Kind = reflect.Bool 126 typ.T = BoolTy 127 typ.Type = reflect.TypeOf(bool(false)) 128 case "address": 129 typ.Kind = reflect.Array 130 typ.Type = addressT 131 typ.Size = 20 132 typ.T = AddressTy 133 case "string": 134 typ.Kind = reflect.String 135 typ.Type = reflect.TypeOf("") 136 typ.T = StringTy 137 case "bytes": 138 if varSize == 0 { 139 typ.T = BytesTy 140 typ.Kind = reflect.Slice 141 typ.Type = reflect.SliceOf(reflect.TypeOf(byte(0))) 142 } else { 143 typ.T = FixedBytesTy 144 typ.Kind = reflect.Array 145 typ.Size = varSize 146 typ.Type = reflect.ArrayOf(varSize, reflect.TypeOf(byte(0))) 147 } 148 case "tuple": 149 var ( 150 fields []reflect.StructField 151 elems []*Type 152 names []string 153 expression string 154 ) 155 expression += "(" 156 for idx, c := range components { 157 cType, err := NewType(c.Type, c.InternalType, c.Components) 158 if err != nil { 159 return Type{}, err 160 } 161 if ToCamelCase(c.Name) == "" { 162 return Type{}, errors.New("abi: purely anonymous or underscored field is not supported") 163 } 164 fields = append(fields, reflect.StructField{ 165 Name: ToCamelCase(c.Name), 166 Type: cType.Type, 167 Tag: reflect.StructTag("json:\"" + c.Name + "\""), 168 }) 169 elems = append(elems, &cType) 170 names = append(names, c.Name) 171 expression += cType.stringKind 172 if idx != len(components)-1 { 173 expression += "," 174 } 175 } 176 expression += ")" 177 typ.Kind = reflect.Struct 178 typ.Type = reflect.StructOf(fields) 179 typ.TupleElems = elems 180 typ.TupleRawNames = names 181 typ.T = TupleTy 182 typ.stringKind = expression 183 184 const structPrefix = "struct " 185 186 if internalType != "" && strings.HasPrefix(internalType, structPrefix) { 187 188 typ.TupleRawName = strings.Replace(internalType[len(structPrefix):], ".", "", -1) 189 } 190 191 case "function": 192 typ.Kind = reflect.Array 193 typ.T = FunctionTy 194 typ.Size = 24 195 typ.Type = reflect.ArrayOf(24, reflect.TypeOf(byte(0))) 196 default: 197 return Type{}, fmt.Errorf("unsupported arg type: %s", t) 198 } 199 200 return 201 } 202 203 func (t Type) String() (out string) { 204 return t.stringKind 205 } 206 207 func (t Type) pack(v reflect.Value) ([]byte, error) { 208 209 v = indirect(v) 210 if err := typeCheck(t, v); err != nil { 211 return nil, err 212 } 213 214 switch t.T { 215 case SliceTy, ArrayTy: 216 var ret []byte 217 218 if t.requiresLengthPrefix() { 219 220 ret = append(ret, packNum(reflect.ValueOf(v.Len()))...) 221 } 222 223 offset := 0 224 offsetReq := isDynamicType(*t.Elem) 225 if offsetReq { 226 offset = getTypeSize(*t.Elem) * v.Len() 227 } 228 var tail []byte 229 for i := 0; i < v.Len(); i++ { 230 val, err := t.Elem.pack(v.Index(i)) 231 if err != nil { 232 return nil, err 233 } 234 if !offsetReq { 235 ret = append(ret, val...) 236 continue 237 } 238 ret = append(ret, packNum(reflect.ValueOf(offset))...) 239 offset += len(val) 240 tail = append(tail, val...) 241 } 242 return append(ret, tail...), nil 243 case TupleTy: 244 245 fieldmap, err := mapArgNamesToStructFields(t.TupleRawNames, v) 246 if err != nil { 247 return nil, err 248 } 249 250 offset := 0 251 for _, elem := range t.TupleElems { 252 offset += getTypeSize(*elem) 253 } 254 var ret, tail []byte 255 for i, elem := range t.TupleElems { 256 field := v.FieldByName(fieldmap[t.TupleRawNames[i]]) 257 if !field.IsValid() { 258 return nil, fmt.Errorf("field %s for tuple not found in the given struct", t.TupleRawNames[i]) 259 } 260 val, err := elem.pack(field) 261 if err != nil { 262 return nil, err 263 } 264 if isDynamicType(*elem) { 265 ret = append(ret, packNum(reflect.ValueOf(offset))...) 266 tail = append(tail, val...) 267 offset += len(val) 268 } else { 269 ret = append(ret, val...) 270 } 271 } 272 return append(ret, tail...), nil 273 274 default: 275 return packElement(t, v), nil 276 } 277 } 278 279 func (t Type) requiresLengthPrefix() bool { 280 return t.T == StringTy || t.T == BytesTy || t.T == SliceTy 281 } 282 283 func isDynamicType(t Type) bool { 284 if t.T == TupleTy { 285 for _, elem := range t.TupleElems { 286 if isDynamicType(*elem) { 287 return true 288 } 289 } 290 return false 291 } 292 return t.T == StringTy || t.T == BytesTy || t.T == SliceTy || (t.T == ArrayTy && isDynamicType(*t.Elem)) 293 } 294 295 func getTypeSize(t Type) int { 296 if t.T == ArrayTy && !isDynamicType(*t.Elem) { 297 298 if t.Elem.T == ArrayTy { 299 return t.Size * getTypeSize(*t.Elem) 300 } 301 return t.Size * 32 302 } else if t.T == TupleTy && !isDynamicType(t) { 303 total := 0 304 for _, elem := range t.TupleElems { 305 total += getTypeSize(*elem) 306 } 307 return total 308 } 309 return 32 310 }