diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go index b0765e7db..9ef7c0f0d 100644 --- a/accounts/abi/abi.go +++ b/accounts/abi/abi.go @@ -186,7 +186,7 @@ func toGoSlice(i int, t Argument, output []byte) (interface{}, error) { // argument in T. func toGoType(i int, t Argument, output []byte) (interface{}, error) { // we need to treat slices differently - if t.Type.Kind == reflect.Slice { + if t.Type.IsSlice { return toGoSlice(i, t, output) } diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go index a0f0d1034..a1b3e62d9 100644 --- a/accounts/abi/abi_test.go +++ b/accounts/abi/abi_test.go @@ -59,7 +59,7 @@ func TestType(t *testing.T) { if err != nil { t.Error(err) } - if typ.Kind != reflect.Ptr { + if typ.Kind != reflect.Uint { t.Error("expected uint32 to have kind Ptr") } @@ -67,8 +67,8 @@ func TestType(t *testing.T) { if err != nil { t.Error(err) } - if typ.Kind != reflect.Slice { - t.Error("expected uint32[] to have type slice") + if !typ.IsSlice { + t.Error("expected uint32[] to be slice") } if typ.Type != ubig_t { t.Error("expcted uith32[] to have type uint64") @@ -78,13 +78,13 @@ func TestType(t *testing.T) { if err != nil { t.Error(err) } - if typ.Kind != reflect.Slice { - t.Error("expected uint32[2] to have kind slice") + if !typ.IsSlice { + t.Error("expected uint32[2] to be slice") } if typ.Type != ubig_t { t.Error("expcted uith32[2] to have type uint64") } - if typ.Size != 2 { + if typ.SliceSize != 2 { t.Error("expected uint32[2] to have a size of 2") } } @@ -149,10 +149,6 @@ func TestTestNumbers(t *testing.T) { t.Errorf("expected send( ptr ) to throw, requires *big.Int instead of *int") } - if _, err := abi.Pack("send", 1000); err != nil { - t.Error("expected send(1000) to cast to big") - } - if _, err := abi.Pack("test", uint32(1000)); err != nil { t.Error(err) } @@ -204,7 +200,7 @@ func TestTestSlice(t *testing.T) { t.FailNow() } - slice := make([]byte, 2) + slice := make([]uint64, 2) if _, err := abi.Pack("uint64[2]", slice); err != nil { t.Error(err) } @@ -214,6 +210,21 @@ func TestTestSlice(t *testing.T) { } } +func TestImplicitTypeCasts(t *testing.T) { + abi, err := JSON(strings.NewReader(jsondata2)) + if err != nil { + t.Error(err) + t.FailNow() + } + + slice := make([]uint8, 2) + _, err = abi.Pack("uint64[2]", slice) + expStr := "`uint64[2]` abi: cannot use type uint8 as type uint64" + if err.Error() != expStr { + t.Errorf("expected %v, got %v", expStr, err) + } +} + func TestMethodSignature(t *testing.T) { String, _ := NewType("string") String32, _ := NewType("string32") diff --git a/accounts/abi/type.go b/accounts/abi/type.go index b7ce6a13b..5a5a5ac49 100644 --- a/accounts/abi/type.go +++ b/accounts/abi/type.go @@ -40,6 +40,9 @@ const ( // Type is the reflection of the supported argument type type Type struct { + IsSlice bool + SliceSize int + Kind reflect.Kind Type reflect.Type Size int @@ -47,6 +50,11 @@ type Type struct { stringKind string // holds the unparsed string for deriving signatures } +var ( + fullTypeRegex = regexp.MustCompile("([a-zA-Z0-9]+)(\\[([0-9]*)?\\])?") + typeRegex = regexp.MustCompile("([a-zA-Z]+)([0-9]*)?") +) + // NewType returns a fully parsed Type given by the input string or an error if it can't be parsed. // // Strings can be in the format of: @@ -61,51 +69,54 @@ type Type struct { // address int256 uint256 real[2] func NewType(t string) (typ Type, err error) { // 1. full string 2. type 3. (opt.) is slice 4. (opt.) size - freg, err := regexp.Compile("([a-zA-Z0-9]+)(\\[([0-9]*)?\\])?") - if err != nil { - return Type{}, err - } - res := freg.FindAllStringSubmatch(t, -1)[0] - var ( - isslice bool - size int - ) + // parse the full representation of the abi-type definition; including: + // * full string + // * type + // * is slice + // * slice size + res := fullTypeRegex.FindAllStringSubmatch(t, -1)[0] + + // check if type is slice and parse type. switch { case res[3] != "": // err is ignored. Already checked for number through the regexp - size, _ = strconv.Atoi(res[3]) - isslice = true + typ.SliceSize, _ = strconv.Atoi(res[3]) + typ.IsSlice = true case res[2] != "": - isslice = true - size = -1 + typ.IsSlice, typ.SliceSize = true, -1 case res[0] == "": - return Type{}, fmt.Errorf("type parse error for `%s`", t) + return Type{}, fmt.Errorf("abi: type parse error: %s", t) } - treg, err := regexp.Compile("([a-zA-Z]+)([0-9]*)?") - if err != nil { - return Type{}, err + // parse the type and size of the abi-type. + parsedType := typeRegex.FindAllStringSubmatch(res[1], -1)[0] + // varSize is the size of the variable + var varSize int + if len(parsedType[2]) > 0 { + var err error + varSize, err = strconv.Atoi(parsedType[2]) + if err != nil { + return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err) + } } - - parsedType := treg.FindAllStringSubmatch(res[1], -1)[0] - vsize, _ := strconv.Atoi(parsedType[2]) - vtype := parsedType[1] - // substitute canonical representation - if vsize == 0 && (vtype == "int" || vtype == "uint") { - vsize = 256 + // varType is the parsed abi type + varType := parsedType[1] + // substitute canonical integer + if varSize == 0 && (varType == "int" || varType == "uint") { + varSize = 256 t += "256" } - switch vtype { + switch varType { case "int": - typ.Kind = reflect.Ptr + typ.Kind = reflect.Int typ.Type = big_t - typ.Size = 256 + typ.Size = varSize typ.T = IntTy case "uint": - typ.Kind = reflect.Ptr + typ.Kind = reflect.Uint typ.Type = ubig_t - typ.Size = 256 + typ.Size = varSize typ.T = UintTy case "bool": typ.Kind = reflect.Bool @@ -120,7 +131,7 @@ func NewType(t string) (typ Type, err error) { typ.Kind = reflect.String typ.Size = -1 typ.T = StringTy - if vsize > 0 { + if varSize > 0 { typ.Size = 32 } case "hash": @@ -131,8 +142,8 @@ func NewType(t string) (typ Type, err error) { case "bytes": typ.Kind = reflect.Array typ.Type = byte_ts - typ.Size = vsize - if vsize == 0 { + typ.Size = varSize + if varSize == 0 { typ.T = BytesTy } else { typ.T = FixedBytesTy @@ -140,13 +151,6 @@ func NewType(t string) (typ Type, err error) { default: return Type{}, fmt.Errorf("unsupported arg type: %s", t) } - - // if the type is a slice we must set Kind to a reflect.Slice - // so that serialisation can be determined based on this kind. - if isslice { - typ.Kind = reflect.Slice - typ.Size = size - } typ.stringKind = t return @@ -173,14 +177,26 @@ func (t Type) pack(v interface{}) ([]byte, error) { value := reflect.ValueOf(v) switch kind := value.Kind(); kind { case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + // check input is unsigned if t.Type != ubig_t { - return nil, fmt.Errorf("type mismatch: %s for %T", t.Type, v) + return nil, fmt.Errorf("abi: type mismatch: %s for %T", t.Type, v) } + + // no implicit type casting + if int(value.Type().Size()*8) != t.Size { + return nil, fmt.Errorf("abi: cannot use type %T as type uint%d", v, t.Size) + } + return packNum(value, t.T), nil case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: if t.Type != ubig_t { return nil, fmt.Errorf("type mismatch: %s for %T", t.Type, v) } + + // no implicit type casting + if int(value.Type().Size()*8) != t.Size { + return nil, fmt.Errorf("abi: cannot use type %T as type uint%d", v, t.Size) + } return packNum(value, t.T), nil case reflect.Ptr: // If the value is a ptr do a assign check (only used by @@ -201,7 +217,7 @@ func (t Type) pack(v interface{}) ([]byte, error) { return packBytesSlice(value.Bytes(), value.Len()), nil } - if t.Size > -1 && value.Len() > t.Size { + if t.SliceSize > -1 && value.Len() > t.SliceSize { return nil, fmt.Errorf("%v out of bound. %d for %d", value.Kind(), value.Len(), t.Size) }