abi: removed implicit type casting & refactored type parsing

This commit is contained in:
Jeffrey Wilcke 2016-03-31 23:54:47 +02:00
parent 968d8ffe94
commit a306e17a26
3 changed files with 80 additions and 53 deletions

View File

@ -186,7 +186,7 @@ func toGoSlice(i int, t Argument, output []byte) (interface{}, error) {
// argument in T. // argument in T.
func toGoType(i int, t Argument, output []byte) (interface{}, error) { func toGoType(i int, t Argument, output []byte) (interface{}, error) {
// we need to treat slices differently // we need to treat slices differently
if t.Type.Kind == reflect.Slice { if t.Type.IsSlice {
return toGoSlice(i, t, output) return toGoSlice(i, t, output)
} }

View File

@ -59,7 +59,7 @@ func TestType(t *testing.T) {
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
if typ.Kind != reflect.Ptr { if typ.Kind != reflect.Uint {
t.Error("expected uint32 to have kind Ptr") t.Error("expected uint32 to have kind Ptr")
} }
@ -67,8 +67,8 @@ func TestType(t *testing.T) {
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
if typ.Kind != reflect.Slice { if !typ.IsSlice {
t.Error("expected uint32[] to have type slice") t.Error("expected uint32[] to be slice")
} }
if typ.Type != ubig_t { if typ.Type != ubig_t {
t.Error("expcted uith32[] to have type uint64") t.Error("expcted uith32[] to have type uint64")
@ -78,13 +78,13 @@ func TestType(t *testing.T) {
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
if typ.Kind != reflect.Slice { if !typ.IsSlice {
t.Error("expected uint32[2] to have kind slice") t.Error("expected uint32[2] to be slice")
} }
if typ.Type != ubig_t { if typ.Type != ubig_t {
t.Error("expcted uith32[2] to have type uint64") 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") 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") 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 { if _, err := abi.Pack("test", uint32(1000)); err != nil {
t.Error(err) t.Error(err)
} }
@ -204,7 +200,7 @@ func TestTestSlice(t *testing.T) {
t.FailNow() t.FailNow()
} }
slice := make([]byte, 2) slice := make([]uint64, 2)
if _, err := abi.Pack("uint64[2]", slice); err != nil { if _, err := abi.Pack("uint64[2]", slice); err != nil {
t.Error(err) 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) { func TestMethodSignature(t *testing.T) {
String, _ := NewType("string") String, _ := NewType("string")
String32, _ := NewType("string32") String32, _ := NewType("string32")

View File

@ -40,6 +40,9 @@ const (
// Type is the reflection of the supported argument type // Type is the reflection of the supported argument type
type Type struct { type Type struct {
IsSlice bool
SliceSize int
Kind reflect.Kind Kind reflect.Kind
Type reflect.Type Type reflect.Type
Size int Size int
@ -47,6 +50,11 @@ type Type struct {
stringKind string // holds the unparsed string for deriving signatures 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. // 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: // Strings can be in the format of:
@ -61,51 +69,54 @@ type Type struct {
// address int256 uint256 real[2] // address int256 uint256 real[2]
func NewType(t string) (typ Type, err error) { func NewType(t string) (typ Type, err error) {
// 1. full string 2. type 3. (opt.) is slice 4. (opt.) size // 1. full string 2. type 3. (opt.) is slice 4. (opt.) size
freg, err := regexp.Compile("([a-zA-Z0-9]+)(\\[([0-9]*)?\\])?") // parse the full representation of the abi-type definition; including:
if err != nil { // * full string
return Type{}, err // * type
} // * is slice
res := freg.FindAllStringSubmatch(t, -1)[0] // * slice size
var ( res := fullTypeRegex.FindAllStringSubmatch(t, -1)[0]
isslice bool
size int // check if type is slice and parse type.
)
switch { switch {
case res[3] != "": case res[3] != "":
// err is ignored. Already checked for number through the regexp // err is ignored. Already checked for number through the regexp
size, _ = strconv.Atoi(res[3]) typ.SliceSize, _ = strconv.Atoi(res[3])
isslice = true typ.IsSlice = true
case res[2] != "": case res[2] != "":
isslice = true typ.IsSlice, typ.SliceSize = true, -1
size = -1
case res[0] == "": 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]*)?") // parse the type and size of the abi-type.
if err != nil { parsedType := typeRegex.FindAllStringSubmatch(res[1], -1)[0]
return Type{}, err // 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)
}
} }
// varType is the parsed abi type
parsedType := treg.FindAllStringSubmatch(res[1], -1)[0] varType := parsedType[1]
vsize, _ := strconv.Atoi(parsedType[2]) // substitute canonical integer
vtype := parsedType[1] if varSize == 0 && (varType == "int" || varType == "uint") {
// substitute canonical representation varSize = 256
if vsize == 0 && (vtype == "int" || vtype == "uint") {
vsize = 256
t += "256" t += "256"
} }
switch vtype { switch varType {
case "int": case "int":
typ.Kind = reflect.Ptr typ.Kind = reflect.Int
typ.Type = big_t typ.Type = big_t
typ.Size = 256 typ.Size = varSize
typ.T = IntTy typ.T = IntTy
case "uint": case "uint":
typ.Kind = reflect.Ptr typ.Kind = reflect.Uint
typ.Type = ubig_t typ.Type = ubig_t
typ.Size = 256 typ.Size = varSize
typ.T = UintTy typ.T = UintTy
case "bool": case "bool":
typ.Kind = reflect.Bool typ.Kind = reflect.Bool
@ -120,7 +131,7 @@ func NewType(t string) (typ Type, err error) {
typ.Kind = reflect.String typ.Kind = reflect.String
typ.Size = -1 typ.Size = -1
typ.T = StringTy typ.T = StringTy
if vsize > 0 { if varSize > 0 {
typ.Size = 32 typ.Size = 32
} }
case "hash": case "hash":
@ -131,8 +142,8 @@ func NewType(t string) (typ Type, err error) {
case "bytes": case "bytes":
typ.Kind = reflect.Array typ.Kind = reflect.Array
typ.Type = byte_ts typ.Type = byte_ts
typ.Size = vsize typ.Size = varSize
if vsize == 0 { if varSize == 0 {
typ.T = BytesTy typ.T = BytesTy
} else { } else {
typ.T = FixedBytesTy typ.T = FixedBytesTy
@ -140,13 +151,6 @@ func NewType(t string) (typ Type, err error) {
default: default:
return Type{}, fmt.Errorf("unsupported arg type: %s", t) 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 typ.stringKind = t
return return
@ -173,14 +177,26 @@ func (t Type) pack(v interface{}) ([]byte, error) {
value := reflect.ValueOf(v) value := reflect.ValueOf(v)
switch kind := value.Kind(); kind { switch kind := value.Kind(); kind {
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
// check input is unsigned
if t.Type != ubig_t { 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 return packNum(value, t.T), nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if t.Type != ubig_t { if t.Type != ubig_t {
return nil, fmt.Errorf("type mismatch: %s for %T", t.Type, v) 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 return packNum(value, t.T), nil
case reflect.Ptr: case reflect.Ptr:
// If the value is a ptr do a assign check (only used by // 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 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) return nil, fmt.Errorf("%v out of bound. %d for %d", value.Kind(), value.Len(), t.Size)
} }