From a16610fc1e38d58c43d4b4ff4baec747ff70e82d Mon Sep 17 00:00:00 2001 From: VoR0220 Date: Fri, 10 Nov 2017 17:39:39 -0600 Subject: [PATCH] accounts/abi: implement struct parsing first attempt Signed-off-by: VoR0220 --- accounts/abi/argument.go | 23 +++-- accounts/abi/type.go | 193 ++++++++++++++++++++++++--------------- 2 files changed, 136 insertions(+), 80 deletions(-) diff --git a/accounts/abi/argument.go b/accounts/abi/argument.go index 4691318ce7..20cde78c41 100644 --- a/accounts/abi/argument.go +++ b/accounts/abi/argument.go @@ -29,21 +29,32 @@ type Argument struct { Indexed bool // indexed is only used by events } +// Type only used for unmarshalling json argument types +type unmarshalArg struct { + Name string + Type string + Components []unmarshalArg // used for tuples/structs + Indexed bool +} + func (a *Argument) UnmarshalJSON(data []byte) error { - var extarg struct { - Name string - Type string - Indexed bool - } + var extarg unmarshalArg + err := json.Unmarshal(data, &extarg) if err != nil { return fmt.Errorf("argument json err: %v", err) } - a.Type, err = NewType(extarg.Type) + if len(extarg.Components) > 0 { + a.Type, err = ParseStructType(extarg.Type, extarg.Components...) + } else { + a.Type, err = NewType(extarg.Type) + } + if err != nil { return err } + a.Name = extarg.Name a.Indexed = extarg.Indexed diff --git a/accounts/abi/type.go b/accounts/abi/type.go index fba10b96d2..d079212b3c 100644 --- a/accounts/abi/type.go +++ b/accounts/abi/type.go @@ -24,6 +24,7 @@ import ( "strings" ) +// Type enumerator const ( IntTy byte = iota UintTy @@ -37,6 +38,7 @@ const ( HashTy FixedPointTy FunctionTy + StructTy ) // Type is the reflection of the supported argument type @@ -58,21 +60,96 @@ var ( // NewType creates a new reflection type of abi type given in t. func NewType(t string) (typ Type, err error) { - // check that array brackets are equal if they exist - if strings.Count(t, "[") != strings.Count(t, "]") { - return Type{}, fmt.Errorf("invalid arg type in abi") - } - - typ.stringKind = t // if there are brackets, get ready to go into slice/array mode and // recursively create the type + var found bool + if typ, found, err = checkForSlices(t); found == true || err != nil { + return typ, err + } + + typ.stringKind = t + // parse the type and size of the abi-type. + parsedType := typeRegex.FindAllStringSubmatch(t, -1)[0] + // varSize is the size of the variable + var varSize int + if len(parsedType[3]) > 0 { + var err error + varSize, err = strconv.Atoi(parsedType[2]) + if err != nil { + return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err) + } + } else { + if parsedType[0] == "uint" || parsedType[0] == "int" { + // this should fail because it means that there's something wrong with + // the abi type (the compiler should always format it to the size...always) + return Type{}, fmt.Errorf("unsupported arg type: %s", t) + } + } + // varType is the parsed abi type + switch varType := parsedType[1]; varType { + case "int": + typ.Kind, typ.Type = reflectIntKindAndType(false, varSize) + typ.Size = varSize + typ.T = IntTy + case "uint": + typ.Kind, typ.Type = reflectIntKindAndType(true, varSize) + typ.Size = varSize + typ.T = UintTy + case "bool": + typ.Kind = reflect.Bool + typ.T = BoolTy + typ.Type = reflect.TypeOf(bool(false)) + case "address": + typ.Kind = reflect.Array + typ.Type = address_t + typ.Size = 20 + typ.T = AddressTy + case "string": + typ.Kind = reflect.String + typ.Type = reflect.TypeOf("") + typ.T = StringTy + case "bytes": + if varSize == 0 { + typ.T = BytesTy + typ.Kind = reflect.Slice + typ.Type = reflect.SliceOf(reflect.TypeOf(byte(0))) + } else { + typ.T = FixedBytesTy + typ.Kind = reflect.Array + typ.Size = varSize + typ.Type = reflect.ArrayOf(varSize, reflect.TypeOf(byte(0))) + } + case "function": + typ.Kind = reflect.Array + typ.T = FunctionTy + typ.Size = 24 + typ.Type = reflect.ArrayOf(24, reflect.TypeOf(byte(0))) + default: + return Type{}, fmt.Errorf("unsupported arg type: %s", t) + } + + return +} + +// if brackets surround the type, create it recursively, otherwise note that brackets weren't found +func checkForSlices(t string, structComponents ...unmarshalArg) (typ Type, found bool, err error) { + // check that array brackets are equal if they exist + if strings.Count(t, "[") != strings.Count(t, "]") { + return Type{}, false, fmt.Errorf("invalid arg type in abi") + } + if strings.Count(t, "[") != 0 { i := strings.LastIndex(t, "[") // recursively embed the type - embeddedType, err := NewType(t[:i]) + var embeddedType Type + if len(structComponents) > 0 { + embeddedType, err = ParseStructType(t, structComponents...) + } else { + embeddedType, err = NewType(t[:i]) + } if err != nil { - return Type{}, err + return Type{}, false, err } // grab the last cell and create a type from there sliced := t[i:] @@ -93,78 +170,46 @@ func NewType(t string) (typ Type, err error) { typ.Elem = &embeddedType typ.Size, err = strconv.Atoi(intz[0]) if err != nil { - return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err) + return Type{}, false, fmt.Errorf("abi: error parsing variable size: %v", err) } typ.Type = reflect.ArrayOf(typ.Size, embeddedType.Type) } else { - return Type{}, fmt.Errorf("invalid formatting of array type") - } - return typ, err - } else { - // parse the type and size of the abi-type. - parsedType := typeRegex.FindAllStringSubmatch(t, -1)[0] - // varSize is the size of the variable - var varSize int - if len(parsedType[3]) > 0 { - var err error - varSize, err = strconv.Atoi(parsedType[2]) - if err != nil { - return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err) - } - } else { - if parsedType[0] == "uint" || parsedType[0] == "int" { - // this should fail because it means that there's something wrong with - // the abi type (the compiler should always format it to the size...always) - return Type{}, fmt.Errorf("unsupported arg type: %s", t) - } - } - // varType is the parsed abi type - varType := parsedType[1] - - switch varType { - case "int": - typ.Kind, typ.Type = reflectIntKindAndType(false, varSize) - typ.Size = varSize - typ.T = IntTy - case "uint": - typ.Kind, typ.Type = reflectIntKindAndType(true, varSize) - typ.Size = varSize - typ.T = UintTy - case "bool": - typ.Kind = reflect.Bool - typ.T = BoolTy - typ.Type = reflect.TypeOf(bool(false)) - case "address": - typ.Kind = reflect.Array - typ.Type = address_t - typ.Size = 20 - typ.T = AddressTy - case "string": - typ.Kind = reflect.String - typ.Type = reflect.TypeOf("") - typ.T = StringTy - case "bytes": - if varSize == 0 { - typ.T = BytesTy - typ.Kind = reflect.Slice - typ.Type = reflect.SliceOf(reflect.TypeOf(byte(0))) - } else { - typ.T = FixedBytesTy - typ.Kind = reflect.Array - typ.Size = varSize - typ.Type = reflect.ArrayOf(varSize, reflect.TypeOf(byte(0))) - } - case "function": - typ.Kind = reflect.Array - typ.T = FunctionTy - typ.Size = 24 - typ.Type = reflect.ArrayOf(24, reflect.TypeOf(byte(0))) - default: - return Type{}, fmt.Errorf("unsupported arg type: %s", t) + return Type{}, false, fmt.Errorf("invalid formatting of array type") } + return typ, true, err } + return Type{}, false, nil +} - return +func ParseStructType(t string, components ...unmarshalArg) (typ Type, err error) { + // if there are brackets, get ready to go into slice/array mode and + // recursively create the type + var found bool + if typ, found, err = checkForSlices(t, components...); found == true || err != nil { + return typ, err + } + typ.stringKind = t + typ.T = StructTy + typ.Kind = reflect.Struct + // create the struct type + var fields []reflect.StructField + for i, component := range components { + // it's a embedded struct type + var fieldType Type + + if len(component.Components) > 0 { + fieldType, err = ParseStructType(component.Type, component.Components...) + } else { + fieldType, err = NewType(component.Type) + } + + if err != nil { + return Type{}, err + } + fields[i] = reflect.StructField{Name: component.Name, Type: fieldType.Type, Tag: reflect.StructTag(fmt.Sprintf(`json:"%v"`, component.Name))} + } + typ.Type = reflect.StructOf(fields) + return typ, nil } // String implements Stringer