diff --git a/accounts/abi/argument.go b/accounts/abi/argument.go index e48f763890a..73b140fb25e 100644 --- a/accounts/abi/argument.go +++ b/accounts/abi/argument.go @@ -109,6 +109,28 @@ func (arguments Arguments) UnpackIntoMap(v map[string]any, data []byte) error { return nil } +// UnpackIntoMap performs the operation hexdata -> mapping of argument name to argument value converted to string. +func (arguments Arguments) UnpackIntoMapAsStrings(v map[string]interface{}, data []byte) error { + // Make sure map is not nil + if v == nil { + return errors.New("abi: cannot unpack into a nil map") + } + if len(data) == 0 { + if len(arguments.NonIndexed()) != 0 { + return errors.New("abi: attempting to unmarshall an empty string while arguments are expected") + } + return nil // Nothing to unmarshal, return + } + marshalledValues, err := arguments.UnpackValuesAsStrings(data) + if err != nil { + return err + } + for i, arg := range arguments.NonIndexed() { + v[arg.Name] = marshalledValues[i] + } + return nil +} + // Copy performs the operation go format -> provided struct. func (arguments Arguments) Copy(v any, values []any) error { // make sure the passed value is arguments pointer @@ -219,6 +241,40 @@ func (arguments Arguments) UnpackValues(data []byte) ([]any, error) { return retval, nil } +// UnpackValuesAsStrings can be used to unpack ABI-encoded hexdata according to the ABI-specification, +// without supplying a struct to unpack into. Instead, this method returns a list containing the +// values converted to strings. An atomic argument will be a list with one element. +func (arguments Arguments) UnpackValuesAsStrings(data []byte) ([]interface{}, error) { + nonIndexedArgs := arguments.NonIndexed() + retval := make([]interface{}, 0, len(nonIndexedArgs)) + virtualArgs := 0 + for index, arg := range nonIndexedArgs { + marshalledValue, err := toString((index+virtualArgs)*32, arg.Type, data) + if err != nil { + return nil, err + } + if arg.Type.T == ArrayTy && !isDynamicType(arg.Type) { + // If we have a static array, like [3]uint256, these are coded as + // just like uint256,uint256,uint256. + // This means that we need to add two 'virtual' arguments when + // we count the index from now on. + // + // Array values nested multiple levels deep are also encoded inline: + // [2][3]uint256: uint256,uint256,uint256,uint256,uint256,uint256 + // + // Calculate the full array size to get the correct offset for the next argument. + // Decrement it by 1, as the normal index increment is still applied. + virtualArgs += getTypeSize(arg.Type)/32 - 1 + } else if arg.Type.T == TupleTy && !isDynamicType(arg.Type) { + // If we have a static tuple, like (uint256, bool, uint256), these are + // coded as just like uint256,bool,uint256 + virtualArgs += getTypeSize(arg.Type)/32 - 1 + } + retval = append(retval, marshalledValue) + } + return retval, nil +} + // PackValues performs the operation Go format -> Hexdata. // It is the semantic opposite of UnpackValues. func (arguments Arguments) PackValues(args []any) ([]byte, error) { diff --git a/accounts/abi/selector_parser.go b/accounts/abi/selector_parser.go index b8ddd7d656b..4ce8192c836 100644 --- a/accounts/abi/selector_parser.go +++ b/accounts/abi/selector_parser.go @@ -1,24 +1,9 @@ -// Copyright 2022 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - package abi import ( "errors" "fmt" + "strings" ) type SelectorMarshaling struct { @@ -35,6 +20,10 @@ func isAlpha(c byte) bool { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') } +func isSpace(c byte) bool { + return c == ' ' +} + func isIdentifierSymbol(c byte) bool { return c == '$' || c == '_' } @@ -50,7 +39,7 @@ func parseToken(unescapedSelector string, isIdent bool) (string, string, error) } for position < len(unescapedSelector) { char := unescapedSelector[position] - if !(isAlpha(char) || isDigit(char) || (isIdent && isIdentifierSymbol(char))) { + if !(isAlpha(char) || isDigit(char) || (isIdent && isIdentifierSymbol(char)) || (!isIdent && isSpace(char))) { break } position++ @@ -62,11 +51,15 @@ func parseIdentifier(unescapedSelector string) (string, string, error) { return parseToken(unescapedSelector, true) } -func parseElementaryType(unescapedSelector string) (string, string, error) { - parsedType, rest, err := parseToken(unescapedSelector, false) +func parseElementaryType(unescapedSelector string) (parsedType string, rest string, err error) { + parsedType, rest, err = parseToken(unescapedSelector, false) if err != nil { return "", "", fmt.Errorf("failed to parse elementary type: %v", err) } + parts := strings.Split(parsedType, " ") + if len(parts) > 1 { + parsedType = parsedType[len(parts[0])+1:] + } // handle arrays for len(rest) > 0 && rest[0] == '[' { parsedType = parsedType + string(rest[0]) @@ -84,15 +77,38 @@ func parseElementaryType(unescapedSelector string) (string, string, error) { return parsedType, rest, nil } -func parseCompositeType(unescapedSelector string) ([]interface{}, string, error) { +func parseElementaryTypeWithName(unescapedSelector string) (parsedType string, rest string, err error) { + parsedType, rest, err = parseToken(unescapedSelector, false) + if err != nil { + return "", "", fmt.Errorf("failed to parse elementary type: %v", err) + } + // handle arrays + for len(rest) > 0 && rest[0] == '[' { + parsedType = parsedType + string(rest[0]) + rest = rest[1:] + for len(rest) > 0 && isDigit(rest[0]) { + parsedType = parsedType + string(rest[0]) + rest = rest[1:] + } + if len(rest) == 0 || rest[0] != ']' { + return "", "", fmt.Errorf("failed to parse array: expected ']', got %c", unescapedSelector[0]) + } + parsedType = parsedType + string(rest[0]) + rest = rest[1:] + } + return parsedType, rest, nil +} + +func parseCompositeType(unescapedSelector string) (result []interface{}, rest string, err error) { if len(unescapedSelector) == 0 || unescapedSelector[0] != '(' { - return nil, "", fmt.Errorf("expected '(', got %c", unescapedSelector[0]) + return nil, "", fmt.Errorf("expected '(...', got %s", unescapedSelector) } - parsedType, rest, err := parseType(unescapedSelector[1:]) + var parsedType interface{} + parsedType, rest, err = parseType(unescapedSelector[1:]) if err != nil { return nil, "", fmt.Errorf("failed to parse type: %v", err) } - result := []interface{}{parsedType} + result = []interface{}{parsedType} for len(rest) > 0 && rest[0] != ')' { parsedType, rest, err = parseType(rest[1:]) if err != nil { @@ -109,7 +125,77 @@ func parseCompositeType(unescapedSelector string) ([]interface{}, string, error) return result, rest[1:], nil } +func parseCompositeTypeWithName(unescapedSelector string) (result []interface{}, rest string, err error) { + var name string + parts := strings.Split(unescapedSelector, " ") + if len(parts) < 2 { + return nil, "", fmt.Errorf("expected name in the beginning, got %s", unescapedSelector) + } else { + name = parts[0] + unescapedSelector = unescapedSelector[len(parts[0])+1:] + } + if len(unescapedSelector) == 0 || unescapedSelector[0] != '(' { + return nil, "", fmt.Errorf("expected '(...', got %s", unescapedSelector) + } + result = []interface{}{name} + var parsedType interface{} + var counter int64 + parsedType, rest, err = parseTypeWithName(unescapedSelector[1:], counter) + if err != nil { + return nil, "", fmt.Errorf("failed to parse type: %v", err) + } + result = append(result, parsedType) + for len(rest) > 0 && rest[0] != ')' { + counter += 1 + parsedType, rest, err = parseTypeWithName(rest[1:], counter) + if err != nil { + return nil, "", fmt.Errorf("failed to parse type: %v", err) + } + result = append(result, parsedType) + } + if len(rest) == 0 || rest[0] != ')' { + return nil, "", fmt.Errorf("expected ')', got '%s'", rest) + } + if len(rest) >= 3 && rest[1] == '[' && rest[2] == ']' { + return append(result, "[]"), rest[3:], nil + } + return result, rest[1:], nil +} + +func parseFunctionsArgs(unescapedSelector string) (result []interface{}, rest string, err error) { + if len(unescapedSelector) == 0 || unescapedSelector[0] != '(' { + return nil, "", fmt.Errorf("expected '(...', got %s", unescapedSelector) + } + var parsedType interface{} + var counter int64 + parsedType, rest, err = parseTypeWithName(unescapedSelector[1:], counter) + if err != nil { + return nil, "", fmt.Errorf("failed to parse type: %v", err) + } + result = []interface{}{parsedType} + + for len(rest) > 0 && rest[0] != ')' { + counter += 1 + parsedType, rest, err = parseTypeWithName(rest[1:], counter) + if err != nil { + return nil, "", fmt.Errorf("failed to parse type: %v", err) + } + result = append(result, parsedType) + } + if len(rest) == 0 || rest[0] != ')' { + return nil, "", fmt.Errorf("expected ')', got '%s'", rest) + } + if len(rest) >= 3 && rest[1] == '[' && rest[2] == ']' { + return append(result, "[]"), rest[3:], nil + } + return result, rest[1:], nil +} + func parseType(unescapedSelector string) (interface{}, string, error) { + parts := strings.Split(unescapedSelector, " ") + if len(parts) > 1 { + unescapedSelector = unescapedSelector[len(parts[0])+1:] + } if len(unescapedSelector) == 0 { return nil, "", errors.New("empty type") } @@ -120,15 +206,49 @@ func parseType(unescapedSelector string) (interface{}, string, error) { } } -func assembleArgs(args []interface{}) ([]ArgumentMarshaling, error) { - arguments := make([]ArgumentMarshaling, 0) - for i, arg := range args { - // generate dummy name to avoid unmarshal issues - name := fmt.Sprintf("name%d", i) +func parseTypeWithName(unescapedSelector string, counter int64) (interface{}, string, error) { + name, rest, _ := parseIdentifier(unescapedSelector) + if len(rest) > 0 && rest[0] == ' ' { + unescapedSelector = unescapedSelector[len(name)+1:] + } else { + name = fmt.Sprintf("name%d", counter) + } + if len(unescapedSelector) == 0 { + return nil, "", errors.New("empty type") + } + if unescapedSelector[0] == '(' { + return parseCompositeTypeWithName(fmt.Sprintf("%v %v", name, unescapedSelector)) + } else { + return parseElementaryTypeWithName(fmt.Sprintf("%v %v", name, unescapedSelector)) + } +} + +func assembleArgs(args []interface{}) (arguments []ArgumentMarshaling, err error) { + arguments = make([]ArgumentMarshaling, 0) + for _, arg := range args { + var name string if s, ok := arg.(string); ok { - arguments = append(arguments, ArgumentMarshaling{name, s, s, nil, false}) + if s == "[]" { + arguments = append(arguments, ArgumentMarshaling{Name: name, Type: s, InternalType: s}) + continue + } + parts := strings.Split(s, " ") + if len(parts) < 2 { + return nil, fmt.Errorf("no name in arg %s", s) + } else { + name = parts[0] + s = s[len(name)+1:] + } + arguments = append(arguments, ArgumentMarshaling{Name: name, Type: s, InternalType: s}) } else if components, ok := arg.([]interface{}); ok { - subArgs, err := assembleArgs(components) + var subArgs []ArgumentMarshaling + if len(components) < 2 { + return nil, fmt.Errorf("no name in components %s", components) + } else { + name = components[0].(string) + components = components[1:] + } + subArgs, err = assembleArgs(components) if err != nil { return nil, fmt.Errorf("failed to assemble components: %v", err) } @@ -137,7 +257,7 @@ func assembleArgs(args []interface{}) ([]ArgumentMarshaling, error) { subArgs = subArgs[:len(subArgs)-1] tupleType = "tuple[]" } - arguments = append(arguments, ArgumentMarshaling{name, tupleType, tupleType, subArgs, false}) + arguments = append(arguments, ArgumentMarshaling{Name: name, Type: tupleType, InternalType: tupleType, Components: subArgs}) } else { return nil, fmt.Errorf("failed to assemble args: unexpected type %T", arg) } @@ -149,16 +269,17 @@ func assembleArgs(args []interface{}) ([]ArgumentMarshaling, error) { // and consumed by other functions in this package. // Note, although uppercase letters are not part of the ABI spec, this function // still accepts it as the general format is valid. -func ParseSelector(unescapedSelector string) (SelectorMarshaling, error) { - name, rest, err := parseIdentifier(unescapedSelector) +func ParseSelector(unescapedSelector string) (m SelectorMarshaling, err error) { + var name, rest string + name, rest, err = parseIdentifier(unescapedSelector) if err != nil { return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': %v", unescapedSelector, err) } - args := []interface{}{} + args := make([]interface{}, 0) if len(rest) >= 2 && rest[0] == '(' && rest[1] == ')' { rest = rest[2:] } else { - args, rest, err = parseCompositeType(rest) + args, rest, err = parseFunctionsArgs(rest) if err != nil { return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': %v", unescapedSelector, err) } @@ -168,7 +289,8 @@ func ParseSelector(unescapedSelector string) (SelectorMarshaling, error) { } // Reassemble the fake ABI and construct the JSON - fakeArgs, err := assembleArgs(args) + var fakeArgs []ArgumentMarshaling + fakeArgs, err = assembleArgs(args) if err != nil { return SelectorMarshaling{}, fmt.Errorf("failed to parse selector: %v", err) } diff --git a/accounts/abi/selector_parser_test.go b/accounts/abi/selector_parser_test.go index 6cb0ae0e70b..e5559f01386 100644 --- a/accounts/abi/selector_parser_test.go +++ b/accounts/abi/selector_parser_test.go @@ -20,6 +20,7 @@ import ( "fmt" "log" "reflect" + "strings" "testing" ) @@ -30,6 +31,11 @@ func TestParseSelector(t *testing.T) { for i, typeOrComponents := range types { name := fmt.Sprintf("name%d", i) if typeName, ok := typeOrComponents.(string); ok { + names := strings.Split(typeName, " ") + if len(names) > 1 { + name = names[0] + typeName = typeName[len(name)+1:] + } result = append(result, ArgumentMarshaling{name, typeName, typeName, nil, false}) } else if components, ok := typeOrComponents.([]ArgumentMarshaling); ok { result = append(result, ArgumentMarshaling{name, "tuple", "tuple", components, false}) @@ -60,6 +66,10 @@ func TestParseSelector(t *testing.T) { mkType([][]ArgumentMarshaling{mkType("uint256", "uint256")}, "bytes32[]")}, {"singleArrayNestWithArrayAndArray((uint256[],address[2],uint8[4][][5])[],bytes32[])", "singleArrayNestWithArrayAndArray", mkType([][]ArgumentMarshaling{mkType("uint256[]", "address[2]", "uint8[4][][5]")}, "bytes32[]")}, + {"transfer(to address,amount uint256)", "transfer", + mkType("to address", "amount uint256")}, + {"execute(((a address,c uint256,b uint8),(d uint8,e bytes)),(address,uint256),(uint8,(uint256,address),(uint256,address),address,address,bytes),(uint256,bytes),(uint256,bytes))", "execute", + mkType(mkType(mkType("a address", "c uint256", "b uint8"), mkType("d uint8", "e bytes")), mkType("address", "uint256"), mkType("uint8", mkType("uint256", "address"), mkType("uint256", "address"), "address", "address", "bytes"), mkType("uint256", "bytes"), mkType("uint256", "bytes"))}, } for i, tt := range tests { selector, err := ParseSelector(tt.input) diff --git a/accounts/abi/type.go b/accounts/abi/type.go index 2fd11ac1239..85fea8741ee 100644 --- a/accounts/abi/type.go +++ b/accounts/abi/type.go @@ -230,6 +230,166 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty return } +// NewTypeAsString creates a new reflection type of abi type given in t. +func NewTypeAsString(t string, internalType string, components []ArgumentMarshaling) (typ Type, err error) { + // check that array brackets are equal if they exist + if strings.Count(t, "[") != strings.Count(t, "]") { + return Type{}, errors.New("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 + if strings.Count(t, "[") != 0 { + // Note internalType can be empty here. + subInternal := internalType + if i := strings.LastIndex(internalType, "["); i != -1 { + subInternal = subInternal[:i] + } + // recursively embed the type + i := strings.LastIndex(t, "[") + embeddedType, err := NewTypeAsString(t[:i], subInternal, components) + if err != nil { + return Type{}, err + } + // grab the last cell and create a type from there + sliced := t[i:] + // grab the slice size with regexp + re := regexp.MustCompile("[0-9]+") + intz := re.FindAllString(sliced, -1) + + if len(intz) == 0 { + // is a slice + typ.T = SliceTy + typ.Elem = &embeddedType + typ.stringKind = embeddedType.stringKind + sliced + } else if len(intz) == 1 { + // is an array + typ.T = ArrayTy + typ.Elem = &embeddedType + typ.Size, err = strconv.Atoi(intz[0]) + if err != nil { + return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err) + } + typ.stringKind = embeddedType.stringKind + sliced + } else { + return Type{}, errors.New("invalid formatting of array type") + } + return typ, err + } + // parse the type and size of the abi-type. + matches := typeRegex.FindAllStringSubmatch(t, -1) + if len(matches) == 0 { + return Type{}, fmt.Errorf("invalid type '%v'", t) + } + parsedType := matches[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.Size = varSize + typ.T = IntTy + case "uint": + typ.Size = varSize + typ.T = UintTy + case "bool": + typ.T = BoolTy + case "address": + typ.Size = 20 + typ.T = AddressTy + case "string": + typ.T = StringTy + case "bytes": + if varSize == 0 { + typ.T = BytesTy + } else { + if varSize > 32 { + return Type{}, fmt.Errorf("unsupported arg type: %s", t) + } + typ.T = FixedBytesTy + typ.Size = varSize + } + case "tuple": + var ( + fields []reflect.StructField + elems []*Type + names []string + expression string // canonical parameter expression + used = make(map[string]bool) + ) + expression += "(" + for idx, c := range components { + cType, err := NewTypeAsString(c.Type, c.InternalType, c.Components) + if err != nil { + return Type{}, err + } + name := ToCamelCase(c.Name) + if name == "" { + return Type{}, errors.New("abi: purely anonymous or underscored field is not supported") + } + fieldName := ResolveNameConflict(name, func(s string) bool { return used[s] }) + if err != nil { + return Type{}, err + } + used[fieldName] = true + if !isValidFieldName(fieldName) { + return Type{}, fmt.Errorf("field %d has invalid name", idx) + } + fields = append(fields, reflect.StructField{ + Name: fieldName, // reflect.StructOf will panic for any exported field. + Type: cType.GetTypeAsString(), + Tag: reflect.StructTag("json:\"" + c.Name + "\""), + }) + elems = append(elems, &cType) + names = append(names, c.Name) + expression += cType.stringKind + if idx != len(components)-1 { + expression += "," + } + } + expression += ")" + + typ.TupleType = reflect.StructOf(fields) + typ.TupleElems = elems + typ.TupleRawNames = names + typ.T = TupleTy + typ.stringKind = expression + + const structPrefix = "struct " + // After solidity 0.5.10, a new field of abi "internalType" + // is introduced. From that we can obtain the struct name + // user defined in the source code. + if internalType != "" && strings.HasPrefix(internalType, structPrefix) { + // Foo.Bar type definition is not allowed in golang, + // convert the format to FooBar + typ.TupleRawName = strings.ReplaceAll(internalType[len(structPrefix):], ".", "") + } + + case "function": + typ.T = FunctionTy + typ.Size = 24 + default: + return Type{}, fmt.Errorf("unsupported arg type: %s", t) + } + + return +} + // GetType returns the reflection type of the ABI type. func (t Type) GetType() reflect.Type { switch t.T { @@ -262,6 +422,42 @@ func (t Type) GetType() reflect.Type { } } +// GetTypeAsString returns the reflection type of the ABI type (always string). +func (t Type) GetTypeAsString() reflect.Type { + switch t.T { + case IntTy: + return reflect.TypeOf("") + case UintTy: + return reflect.TypeOf("") + case BoolTy: + return reflect.TypeOf("") + case StringTy: + return reflect.TypeOf("") + case SliceTy: + return reflect.SliceOf(t.Elem.GetTypeAsString()) + case ArrayTy: + return reflect.ArrayOf(t.Size, t.Elem.GetTypeAsString()) + case TupleTy: + return t.TupleType + case AddressTy: + return reflect.TypeOf("") + case FixedBytesTy: + return reflect.TypeOf("") + case BytesTy: + return reflect.TypeOf("") + case HashTy: + // hashtype currently not used + return reflect.TypeOf("") + case FixedPointTy: + // fixedpoint type currently not used + return reflect.TypeOf("") + case FunctionTy: + return reflect.TypeOf("") + default: + panic("Invalid type") + } +} + // String implements Stringer. func (t Type) String() (out string) { return t.stringKind diff --git a/accounts/abi/unpack.go b/accounts/abi/unpack.go index 905b5ce629d..d877fbf8c3a 100644 --- a/accounts/abi/unpack.go +++ b/accounts/abi/unpack.go @@ -23,6 +23,7 @@ import ( "math" "math/big" "reflect" + "strconv" "github.com/ethereum/go-ethereum/common" ) @@ -31,7 +32,7 @@ var ( // MaxUint256 is the maximum value that can be represented by a uint256. MaxUint256 = new(big.Int).Sub(new(big.Int).Lsh(common.Big1, 256), common.Big1) // MaxInt256 is the maximum value that can be represented by a int256. - MaxInt256 = new(big.Int).Sub(new(big.Int).Lsh(common.Big1, 255), common.Big1) + //MaxInt256 = new(big.Int).Sub(new(big.Int).Lsh(common.Big1, 255), common.Big1) ) // ReadInteger reads the integer based on its kind and returns the appropriate value. @@ -189,6 +190,46 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error) return refSlice.Interface(), nil } +// forEachUnpack iteratively unpack elements. +func forEachUnpackAsString(t Type, output []byte, start, size int) (interface{}, error) { + if size < 0 { + return nil, fmt.Errorf("cannot marshal input to array, size is negative (%d)", size) + } + if start+32*size > len(output) { + return nil, fmt.Errorf("abi: cannot marshal into go array: offset %d would go over slice boundary (len=%d)", len(output), start+32*size) + } + + // this value will become our slice or our array, depending on the type + var refSlice reflect.Value + + if t.T == SliceTy { + // declare our slice + refSlice = reflect.MakeSlice(t.GetTypeAsString(), size, size) + } else if t.T == ArrayTy { + // declare our array + refSlice = reflect.New(t.GetTypeAsString()).Elem() + } else { + return nil, errors.New("abi: invalid type in array/slice unpacking stage") + } + + // Arrays have packed elements, resulting in longer unpack steps. + // Slices have just 32 bytes per element (pointing to the contents). + elemSize := getTypeSize(*t.Elem) + + for i, j := start, 0; j < size; i, j = i+elemSize, j+1 { + inter, err := toString(i, *t.Elem, output) + if err != nil { + return nil, err + } + + // append the item to our reflect slice + refSlice.Index(j).Set(reflect.ValueOf(inter)) + } + + // return the interface + return refSlice.Interface(), nil +} + func forTupleUnpack(t Type, output []byte) (interface{}, error) { retval := reflect.New(t.GetType()).Elem() virtualArgs := 0 @@ -219,6 +260,36 @@ func forTupleUnpack(t Type, output []byte) (interface{}, error) { return retval.Interface(), nil } +func forTupleUnpackAsString(t Type, output []byte) (interface{}, error) { + retval := reflect.New(t.GetTypeAsString()).Elem() + virtualArgs := 0 + for index, elem := range t.TupleElems { + marshalledValue, err := toString((index+virtualArgs)*32, *elem, output) + if err != nil { + return nil, err + } + if elem.T == ArrayTy && !isDynamicType(*elem) { + // If we have a static array, like [3]uint256, these are coded as + // just like uint256,uint256,uint256. + // This means that we need to add two 'virtual' arguments when + // we count the index from now on. + // + // Array values nested multiple levels deep are also encoded inline: + // [2][3]uint256: uint256,uint256,uint256,uint256,uint256,uint256 + // + // Calculate the full array size to get the correct offset for the next argument. + // Decrement it by 1, as the normal index increment is still applied. + virtualArgs += getTypeSize(*elem)/32 - 1 + } else if elem.T == TupleTy && !isDynamicType(*elem) { + // If we have a static tuple, like (uint256, bool, uint256), these are + // coded as just like uint256,bool,uint256 + virtualArgs += getTypeSize(*elem)/32 - 1 + } + retval.Field(index).Set(reflect.ValueOf(marshalledValue)) + } + return retval.Interface(), nil +} + // toGoType parses the output bytes and recursively assigns the value of these bytes // into a go type with accordance with the ABI spec. func toGoType(index int, t Type, output []byte) (interface{}, error) { @@ -284,6 +355,90 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) { } } +// toString parses the output bytes and recursively assigns the value of these bytes into string. +func toString(index int, t Type, output []byte) (interface{}, error) { + if index+32 > len(output) { + return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), index+32) + } + + var ( + returnOutput []byte + begin, length int + err error + ) + + // if we require a length prefix, find the beginning word and size returned. + if t.requiresLengthPrefix() { + begin, length, err = lengthPrefixPointsTo(index, output) + if err != nil { + return nil, err + } + } else { + returnOutput = output[index : index+32] + } + + switch t.T { + case TupleTy: + if isDynamicType(t) { + begin, err := tuplePointsTo(index, output) + if err != nil { + return nil, err + } + return forTupleUnpackAsString(t, output[begin:]) + } + return forTupleUnpackAsString(t, output[index:]) + case SliceTy: + return forEachUnpackAsString(t, output[begin:], 0, length) + case ArrayTy: + if isDynamicType(*t.Elem) { + offset := binary.BigEndian.Uint64(returnOutput[len(returnOutput)-8:]) + if offset > uint64(len(output)) { + return nil, fmt.Errorf("abi: toGoType offset greater than output length: offset: %d, len(output): %d", offset, len(output)) + } + return forEachUnpackAsString(t, output[offset:], 0, t.Size) + } + return forEachUnpackAsString(t, output[index:], 0, t.Size) + case StringTy: // variable arrays are written at the end of the return bytes + return string(output[begin : begin+length]), nil + case IntTy, UintTy: + var n interface{} + n, err = ReadInteger(t, returnOutput) + if err != nil { + return nil, fmt.Errorf("abi: cannot convert value as integer: %v", returnOutput) + } + return fmt.Sprintf("%d", n), nil + case BoolTy: + var b bool + b, err = readBool(returnOutput) + if err != nil { + return nil, fmt.Errorf("abi: cannot convert value as bool: %v", returnOutput) + } + return strconv.FormatBool(b), nil + case AddressTy: + return common.Bytes2HexWithPrefix(common.BytesToAddress(returnOutput).Bytes()), nil + case HashTy: + return common.Bytes2HexWithPrefix(common.BytesToHash(returnOutput).Bytes()), nil + case BytesTy: + return common.Bytes2HexWithPrefix(output[begin : begin+length]), nil + case FixedBytesTy: + var b interface{} + b, err = ReadFixedBytes(t, returnOutput) + if err != nil { + return nil, fmt.Errorf("abi: cannot convert value as fixed bytes array: %v", returnOutput) + } + return common.Bytes2HexWithPrefix(fixedBytesToSlice(b)), nil + case FunctionTy: + var b interface{} + b, err = ReadFixedBytes(t, returnOutput) + if err != nil { + return nil, fmt.Errorf("abi: cannot convert value as function: %v", returnOutput) + } + return common.Bytes2HexWithPrefix(fixedBytesToSlice(b)), nil + default: + return nil, fmt.Errorf("abi: unknown type %v", t.T) + } +} + // lengthPrefixPointsTo interprets a 32 byte slice as an offset and then determines which indices to look to decode the type. func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err error) { bigOffsetEnd := new(big.Int).SetBytes(output[index : index+32]) @@ -327,3 +482,14 @@ func tuplePointsTo(index int, output []byte) (start int, err error) { } return int(offset.Uint64()), nil } + +func fixedBytesToSlice(b interface{}) (res []byte) { + arr := reflect.ValueOf(b) + length := arr.Len() + res = make([]byte, length) + for i := 0; i < length; i++ { + res[i] = arr.Index(i).Interface().(byte) + } + + return res +} diff --git a/accounts/abi/unpack_test.go b/accounts/abi/unpack_test.go index 90713c03ca2..edd9d37dc00 100644 --- a/accounts/abi/unpack_test.go +++ b/accounts/abi/unpack_test.go @@ -243,6 +243,15 @@ var unpackTests = []unpackTest{ IntOne *big.Int }{big.NewInt(1)}, }, + { + def: `[{"name":"one","type":"string"},{"name":"two","type":"bytes32"},{"name":"three","type":"bytes"}]`, + enc: "0000000000000000000000000000000000000000000000000000000000000060972ed6e068cd66d9a90b0e300ccf142da753a68f9c75c85b1aff5b85cce09c3e00000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002465303532313337362d613332312d346633372d393161632d3331376330623765353038350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414341b07dad558e91104ac08d3e939b73ea8d723e9cfb8eb9dc7c532154dbb5df2e5e93514338ecf8c512ba87546238353ff4d28366bb5d91e2938b692ce725cf1c00000000000000000000000000000000000000000000000000000000000000", + want: struct { + One string + Two [32]uint8 + Three []uint8 + }{"e0521376-a321-4f37-91ac-317c0b7e5085", [32]uint8{151, 46, 214, 224, 104, 205, 102, 217, 169, 11, 14, 48, 12, 207, 20, 45, 167, 83, 166, 143, 156, 117, 200, 91, 26, 255, 91, 133, 204, 224, 156, 62}, []uint8{67, 65, 176, 125, 173, 85, 142, 145, 16, 74, 192, 141, 62, 147, 155, 115, 234, 141, 114, 62, 156, 251, 142, 185, 220, 124, 83, 33, 84, 219, 181, 223, 46, 94, 147, 81, 67, 56, 236, 248, 197, 18, 186, 135, 84, 98, 56, 53, 63, 244, 210, 131, 102, 187, 93, 145, 226, 147, 139, 105, 44, 231, 37, 207, 28}}, + }, { def: `[{"type":"bool"}]`, enc: "", diff --git a/common/bytes.go b/common/bytes.go index d1f5c6c9958..9ae51324a53 100644 --- a/common/bytes.go +++ b/common/bytes.go @@ -75,6 +75,11 @@ func Bytes2Hex(d []byte) string { return hex.EncodeToString(d) } +// Bytes2Hex returns the hexadecimal encoding of d. +func Bytes2HexWithPrefix(d []byte) string { + return "0x" + Bytes2Hex(d) +} + // Hex2Bytes returns the bytes represented by the hexadecimal string str. func Hex2Bytes(str string) []byte { h, _ := hex.DecodeString(str) diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 1195929f7d2..cc1567c1e0c 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -696,6 +696,14 @@ func (ec *Client) SendTransaction(ctx context.Context, tx *types.Transaction) er return ec.c.CallContext(ctx, nil, "eth_sendRawTransaction", hexutil.Encode(data)) } +// SendRawTransaction injects a raw transaction into the pending pool for execution. +// +// If the transaction was a contract creation use the TransactionReceipt method to get the +// contract address after the transaction has been mined. +func (ec *Client) SendRawTransaction(ctx context.Context, rawTx string) error { + return ec.c.CallContext(ctx, nil, "eth_sendRawTransaction", rawTx) +} + // RevertErrorData returns the 'revert reason' data of a contract call. // // This can be used with CallContract and EstimateGas, and only when the server is Geth. @@ -734,7 +742,7 @@ func toCallArg(msg ethereum.CallMsg) interface{} { "to": msg.To, } if len(msg.Data) > 0 { - arg["input"] = hexutil.Bytes(msg.Data) + arg["data"] = hexutil.Bytes(msg.Data) } if msg.Value != nil { arg["value"] = (*hexutil.Big)(msg.Value)