aboutsummaryrefslogtreecommitdiffstats
path: root/accounts/abi/type.go
diff options
context:
space:
mode:
Diffstat (limited to 'accounts/abi/type.go')
-rw-r--r--accounts/abi/type.go178
1 files changed, 54 insertions, 124 deletions
diff --git a/accounts/abi/type.go b/accounts/abi/type.go
index 5a5a5ac49..cad7b8212 100644
--- a/accounts/abi/type.go
+++ b/accounts/abi/type.go
@@ -21,8 +21,6 @@ import (
"reflect"
"regexp"
"strconv"
-
- "github.com/ethereum/go-ethereum/common"
)
const (
@@ -40,53 +38,59 @@ const (
// Type is the reflection of the supported argument type
type Type struct {
- IsSlice bool
- SliceSize int
+ IsSlice, IsArray bool
+ SliceSize int
+
+ Elem *Type
+
+ Kind reflect.Kind
+ Type reflect.Type
+ Size int
+ T byte // Our own type checking
- Kind reflect.Kind
- Type reflect.Type
- Size int
- T byte // Our own type checking
stringKind string // holds the unparsed string for deriving signatures
}
var (
+ // fullTypeRegex parses the abi types
+ //
+ // Types can be in the format of:
+ //
+ // Input = Type [ "[" [ Number ] "]" ] Name .
+ // Type = [ "u" ] "int" [ Number ] .
+ //
+ // Examples:
+ //
+ // string int uint real
+ // string32 int8 uint8 uint[]
+ // address int256 uint256 real[2]
fullTypeRegex = regexp.MustCompile("([a-zA-Z0-9]+)(\\[([0-9]*)?\\])?")
- typeRegex = regexp.MustCompile("([a-zA-Z]+)([0-9]*)?")
+ // typeRegex parses the abi sub types
+ 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:
-//
-// Input = Type [ "[" [ Number ] "]" ] Name .
-// Type = [ "u" ] "int" [ Number ] .
-//
-// Examples:
-//
-// string int uint real
-// string32 int8 uint8 uint[]
-// address int256 uint256 real[2]
+// NewType creates a new reflection type of abi type given in t.
func NewType(t string) (typ Type, err error) {
- // 1. full string 2. type 3. (opt.) is slice 4. (opt.) size
- // 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
typ.SliceSize, _ = strconv.Atoi(res[3])
- typ.IsSlice = true
+ typ.IsArray = true
case res[2] != "":
typ.IsSlice, typ.SliceSize = true, -1
case res[0] == "":
return Type{}, fmt.Errorf("abi: type parse error: %s", t)
}
+ if typ.IsArray || typ.IsSlice {
+ sliceType, err := NewType(res[1])
+ if err != nil {
+ return Type{}, err
+ }
+ typ.Elem = &sliceType
+ return typ, nil
+ }
// parse the type and size of the abi-type.
parsedType := typeRegex.FindAllStringSubmatch(res[1], -1)[0]
@@ -109,21 +113,20 @@ func NewType(t string) (typ Type, err error) {
switch varType {
case "int":
- typ.Kind = reflect.Int
+ typ.Kind = reflectIntKind(false, varSize)
typ.Type = big_t
typ.Size = varSize
typ.T = IntTy
case "uint":
- typ.Kind = reflect.Uint
+ typ.Kind = reflectIntKind(true, varSize)
typ.Type = ubig_t
typ.Size = varSize
typ.T = UintTy
case "bool":
typ.Kind = reflect.Bool
typ.T = BoolTy
- case "real": // TODO
- typ.Kind = reflect.Invalid
case "address":
+ typ.Kind = reflect.Array
typ.Type = address_t
typ.Size = 20
typ.T = AddressTy
@@ -131,22 +134,17 @@ func NewType(t string) (typ Type, err error) {
typ.Kind = reflect.String
typ.Size = -1
typ.T = StringTy
- if varSize > 0 {
- typ.Size = 32
- }
- case "hash":
- typ.Kind = reflect.Array
- typ.Size = 32
- typ.Type = hash_t
- typ.T = HashTy
case "bytes":
- typ.Kind = reflect.Array
- typ.Type = byte_ts
- typ.Size = varSize
+ sliceType, _ := NewType("uint8")
+ typ.Elem = &sliceType
if varSize == 0 {
+ typ.IsSlice = true
typ.T = BytesTy
+ typ.SliceSize = -1
} else {
+ typ.IsArray = true
typ.T = FixedBytesTy
+ typ.SliceSize = varSize
}
default:
return Type{}, fmt.Errorf("unsupported arg type: %s", t)
@@ -156,98 +154,30 @@ func NewType(t string) (typ Type, err error) {
return
}
+// String implements Stringer
func (t Type) String() (out string) {
return t.stringKind
}
-// packBytesSlice packs the given bytes as [L, V] as the canonical representation
-// bytes slice
-func packBytesSlice(bytes []byte, l int) []byte {
- len := packNum(reflect.ValueOf(l), UintTy)
- return append(len, common.RightPadBytes(bytes, (l+31)/32*32)...)
-}
-
-// Test the given input parameter `v` and checks if it matches certain
-// criteria
-// * Big integers are checks for ptr types and if the given value is
-// assignable
-// * Integer are checked for size
-// * Strings, addresses and bytes are checks for type and size
-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("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
- // big.Int for now)
- if t.Type == ubig_t && value.Type() != ubig_t {
- return nil, fmt.Errorf("type mismatch: %s for %T", t.Type, v)
- }
- return packNum(value, t.T), nil
- case reflect.String:
- if t.Size > -1 && value.Len() > t.Size {
- return nil, fmt.Errorf("%v out of bound. %d for %d", value.Kind(), value.Len(), t.Size)
- }
+func (t Type) pack(v reflect.Value) ([]byte, error) {
+ // dereference pointer first if it's a pointer
+ v = indirect(v)
- return packBytesSlice([]byte(value.String()), value.Len()), nil
- case reflect.Slice:
- // Byte slice is a special case, it gets treated as a single value
- if t.T == BytesTy {
- return packBytesSlice(value.Bytes(), value.Len()), nil
- }
-
- 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)
- }
-
- // Signed / Unsigned check
- if value.Type() == big_t && (t.T != IntTy && isSigned(value)) || (t.T == UintTy && isSigned(value)) {
- return nil, fmt.Errorf("slice of incompatible types.")
- }
+ if err := typeCheck(t, v); err != nil {
+ return nil, err
+ }
+ if (t.IsSlice || t.IsArray) && t.T != BytesTy && t.T != FixedBytesTy {
var packed []byte
- for i := 0; i < value.Len(); i++ {
- val, err := t.pack(value.Index(i).Interface())
+ for i := 0; i < v.Len(); i++ {
+ val, err := t.Elem.pack(v.Index(i))
if err != nil {
return nil, err
}
packed = append(packed, val...)
}
- return packBytesSlice(packed, value.Len()), nil
- case reflect.Bool:
- if value.Bool() {
- return common.LeftPadBytes(common.Big1.Bytes(), 32), nil
- } else {
- return common.LeftPadBytes(common.Big0.Bytes(), 32), nil
- }
- case reflect.Array:
- if v, ok := value.Interface().(common.Address); ok {
- return common.LeftPadBytes(v[:], 32), nil
- } else if v, ok := value.Interface().(common.Hash); ok {
- return v[:], nil
- }
+ return packBytesSlice(packed, v.Len()), nil
}
- return nil, fmt.Errorf("ABI: bad input given %v", value.Kind())
+ return packElement(t, v), nil
}