diff options
Diffstat (limited to 'accounts/abi/type.go')
-rw-r--r-- | accounts/abi/type.go | 178 |
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 } |