Commit dec8bba9 authored by RJ Catalano's avatar RJ Catalano Committed by Felix Lange

accounts/abi: improve type handling, add event support (#14743)

parent e9295163
...@@ -20,10 +20,6 @@ import ( ...@@ -20,10 +20,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"reflect"
"strings"
"github.com/ethereum/go-ethereum/common"
) )
// The ABI holds information about a contract's context and available // The ABI holds information about a contract's context and available
...@@ -76,106 +72,27 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) { ...@@ -76,106 +72,27 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
return append(method.Id(), arguments...), nil return append(method.Id(), arguments...), nil
} }
// these variable are used to determine certain types during type assertion for
// assignment.
var (
r_interSlice = reflect.TypeOf([]interface{}{})
r_hash = reflect.TypeOf(common.Hash{})
r_bytes = reflect.TypeOf([]byte{})
r_byte = reflect.TypeOf(byte(0))
)
// Unpack output in v according to the abi specification // Unpack output in v according to the abi specification
func (abi ABI) Unpack(v interface{}, name string, output []byte) error { func (abi ABI) Unpack(v interface{}, name string, output []byte) (err error) {
var method = abi.Methods[name] if err = bytesAreProper(output); err != nil {
return err
if len(output) == 0 {
return fmt.Errorf("abi: unmarshalling empty output")
}
// make sure the passed value is a pointer
valueOf := reflect.ValueOf(v)
if reflect.Ptr != valueOf.Kind() {
return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
} }
// since there can't be naming collisions with contracts and events,
var ( // we need to decide whether we're calling a method or an event
value = valueOf.Elem() var unpack unpacker
typ = value.Type() if method, ok := abi.Methods[name]; ok {
) unpack = method
} else if event, ok := abi.Events[name]; ok {
if len(method.Outputs) > 1 { unpack = event
switch value.Kind() {
// struct will match named return values to the struct's field
// names
case reflect.Struct:
for i := 0; i < len(method.Outputs); i++ {
marshalledValue, err := toGoType(i, method.Outputs[i], output)
if err != nil {
return err
}
reflectValue := reflect.ValueOf(marshalledValue)
for j := 0; j < typ.NumField(); j++ {
field := typ.Field(j)
// TODO read tags: `abi:"fieldName"`
if field.Name == strings.ToUpper(method.Outputs[i].Name[:1])+method.Outputs[i].Name[1:] {
if err := set(value.Field(j), reflectValue, method.Outputs[i]); err != nil {
return err
}
}
}
}
case reflect.Slice:
if !value.Type().AssignableTo(r_interSlice) {
return fmt.Errorf("abi: cannot marshal tuple in to slice %T (only []interface{} is supported)", v)
}
// if the slice already contains values, set those instead of the interface slice itself.
if value.Len() > 0 {
if len(method.Outputs) > value.Len() {
return fmt.Errorf("abi: cannot marshal in to slices of unequal size (require: %v, got: %v)", len(method.Outputs), value.Len())
}
for i := 0; i < len(method.Outputs); i++ {
marshalledValue, err := toGoType(i, method.Outputs[i], output)
if err != nil {
return err
}
reflectValue := reflect.ValueOf(marshalledValue)
if err := set(value.Index(i).Elem(), reflectValue, method.Outputs[i]); err != nil {
return err
}
}
return nil
}
// create a new slice and start appending the unmarshalled
// values to the new interface slice.
z := reflect.MakeSlice(typ, 0, len(method.Outputs))
for i := 0; i < len(method.Outputs); i++ {
marshalledValue, err := toGoType(i, method.Outputs[i], output)
if err != nil {
return err
}
z = reflect.Append(z, reflect.ValueOf(marshalledValue))
}
value.Set(z)
default:
return fmt.Errorf("abi: cannot unmarshal tuple in to %v", typ)
}
} else { } else {
marshalledValue, err := toGoType(0, method.Outputs[0], output) return fmt.Errorf("abi: could not locate named method or event.")
if err != nil {
return err
}
if err := set(value, reflect.ValueOf(marshalledValue), method.Outputs[0]); err != nil {
return err
}
} }
return nil // requires a struct to unpack into for a tuple return...
if unpack.isTupleReturn() {
return unpack.tupleUnpack(v, output)
}
return unpack.singleUnpack(v, output)
} }
func (abi *ABI) UnmarshalJSON(data []byte) error { func (abi *ABI) UnmarshalJSON(data []byte) error {
......
...@@ -29,25 +29,6 @@ import ( ...@@ -29,25 +29,6 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
) )
// formatSilceOutput add padding to the value and adds a size
func formatSliceOutput(v ...[]byte) []byte {
off := common.LeftPadBytes(big.NewInt(int64(len(v))).Bytes(), 32)
output := append(off, make([]byte, 0, len(v)*32)...)
for _, value := range v {
output = append(output, common.LeftPadBytes(value, 32)...)
}
return output
}
// quick helper padding
func pad(input []byte, size int, left bool) []byte {
if left {
return common.LeftPadBytes(input, size)
}
return common.RightPadBytes(input, size)
}
const jsondata = ` const jsondata = `
[ [
{ "type" : "function", "name" : "balance", "constant" : true }, { "type" : "function", "name" : "balance", "constant" : true },
...@@ -191,7 +172,7 @@ func TestMethodSignature(t *testing.T) { ...@@ -191,7 +172,7 @@ func TestMethodSignature(t *testing.T) {
t.Errorf("expected ids to match %x != %x", m.Id(), idexp) t.Errorf("expected ids to match %x != %x", m.Id(), idexp)
} }
uintt, _ := NewType("uint") uintt, _ := NewType("uint256")
m = Method{"foo", false, []Argument{{"bar", uintt, false}}, nil} m = Method{"foo", false, []Argument{{"bar", uintt, false}}, nil}
exp = "foo(uint256)" exp = "foo(uint256)"
if m.Sig() != exp { if m.Sig() != exp {
......
...@@ -472,7 +472,7 @@ func TestBindings(t *testing.T) { ...@@ -472,7 +472,7 @@ func TestBindings(t *testing.T) {
t.Fatalf("failed to create temporary workspace: %v", err) t.Fatalf("failed to create temporary workspace: %v", err)
} }
defer os.RemoveAll(ws) defer os.RemoveAll(ws)
pkg := filepath.Join(ws, "bindtest") pkg := filepath.Join(ws, "bindtest")
if err = os.MkdirAll(pkg, 0700); err != nil { if err = os.MkdirAll(pkg, 0700); err != nil {
t.Fatalf("failed to create package: %v", err) t.Fatalf("failed to create package: %v", err)
......
...@@ -39,22 +39,23 @@ func formatSliceString(kind reflect.Kind, sliceSize int) string { ...@@ -39,22 +39,23 @@ func formatSliceString(kind reflect.Kind, sliceSize int) string {
// type in t. // type in t.
func sliceTypeCheck(t Type, val reflect.Value) error { func sliceTypeCheck(t Type, val reflect.Value) error {
if val.Kind() != reflect.Slice && val.Kind() != reflect.Array { if val.Kind() != reflect.Slice && val.Kind() != reflect.Array {
return typeErr(formatSliceString(t.Kind, t.SliceSize), val.Type()) return typeErr(formatSliceString(t.Kind, t.Size), val.Type())
} }
if t.IsArray && val.Len() != t.SliceSize {
return typeErr(formatSliceString(t.Elem.Kind, t.SliceSize), formatSliceString(val.Type().Elem().Kind(), val.Len())) if t.T == ArrayTy && val.Len() != t.Size {
return typeErr(formatSliceString(t.Elem.Kind, t.Size), formatSliceString(val.Type().Elem().Kind(), val.Len()))
} }
if t.Elem.IsSlice { if t.Elem.T == SliceTy {
if val.Len() > 0 { if val.Len() > 0 {
return sliceTypeCheck(*t.Elem, val.Index(0)) return sliceTypeCheck(*t.Elem, val.Index(0))
} }
} else if t.Elem.IsArray { } else if t.Elem.T == ArrayTy {
return sliceTypeCheck(*t.Elem, val.Index(0)) return sliceTypeCheck(*t.Elem, val.Index(0))
} }
if elemKind := val.Type().Elem().Kind(); elemKind != t.Elem.Kind { if elemKind := val.Type().Elem().Kind(); elemKind != t.Elem.Kind {
return typeErr(formatSliceString(t.Elem.Kind, t.SliceSize), val.Type()) return typeErr(formatSliceString(t.Elem.Kind, t.Size), val.Type())
} }
return nil return nil
} }
...@@ -62,20 +63,19 @@ func sliceTypeCheck(t Type, val reflect.Value) error { ...@@ -62,20 +63,19 @@ func sliceTypeCheck(t Type, val reflect.Value) error {
// typeCheck checks that the given reflection value can be assigned to the reflection // typeCheck checks that the given reflection value can be assigned to the reflection
// type in t. // type in t.
func typeCheck(t Type, value reflect.Value) error { func typeCheck(t Type, value reflect.Value) error {
if t.IsSlice || t.IsArray { if t.T == SliceTy || t.T == ArrayTy {
return sliceTypeCheck(t, value) return sliceTypeCheck(t, value)
} }
// Check base type validity. Element types will be checked later on. // Check base type validity. Element types will be checked later on.
if t.Kind != value.Kind() { if t.Kind != value.Kind() {
return typeErr(t.Kind, value.Kind()) return typeErr(t.Kind, value.Kind())
} else if t.T == FixedBytesTy && t.Size != value.Len() {
return typeErr(t.Type, value.Type())
} else {
return nil
} }
return nil
}
// varErr returns a formatted error.
func varErr(expected, got reflect.Kind) error {
return typeErr(expected, got)
} }
// typeErr returns a formatted type casting error. // typeErr returns a formatted type casting error.
......
...@@ -18,6 +18,7 @@ package abi ...@@ -18,6 +18,7 @@ package abi
import ( import (
"fmt" "fmt"
"reflect"
"strings" "strings"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -44,3 +45,93 @@ func (e Event) Id() common.Hash { ...@@ -44,3 +45,93 @@ func (e Event) Id() common.Hash {
} }
return common.BytesToHash(crypto.Keccak256([]byte(fmt.Sprintf("%v(%v)", e.Name, strings.Join(types, ","))))) return common.BytesToHash(crypto.Keccak256([]byte(fmt.Sprintf("%v(%v)", e.Name, strings.Join(types, ",")))))
} }
// unpacks an event return tuple into a struct of corresponding go types
//
// Unpacking can be done into a struct or a slice/array.
func (e Event) tupleUnpack(v interface{}, output []byte) error {
// make sure the passed value is a pointer
valueOf := reflect.ValueOf(v)
if reflect.Ptr != valueOf.Kind() {
return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
}
var (
value = valueOf.Elem()
typ = value.Type()
)
if value.Kind() != reflect.Struct {
return fmt.Errorf("abi: cannot unmarshal tuple in to %v", typ)
}
j := 0
for i := 0; i < len(e.Inputs); i++ {
input := e.Inputs[i]
if input.Indexed {
// can't read, continue
continue
} else if input.Type.T == ArrayTy {
// need to move this up because they read sequentially
j += input.Type.Size
}
marshalledValue, err := toGoType((i+j)*32, input.Type, output)
if err != nil {
return err
}
reflectValue := reflect.ValueOf(marshalledValue)
switch value.Kind() {
case reflect.Struct:
for j := 0; j < typ.NumField(); j++ {
field := typ.Field(j)
// TODO read tags: `abi:"fieldName"`
if field.Name == strings.ToUpper(e.Inputs[i].Name[:1])+e.Inputs[i].Name[1:] {
if err := set(value.Field(j), reflectValue, e.Inputs[i]); err != nil {
return err
}
}
}
case reflect.Slice, reflect.Array:
if value.Len() < i {
return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(e.Inputs), value.Len())
}
v := value.Index(i)
if v.Kind() != reflect.Ptr && v.Kind() != reflect.Interface {
return fmt.Errorf("abi: cannot unmarshal %v in to %v", v.Type(), reflectValue.Type())
}
reflectValue := reflect.ValueOf(marshalledValue)
if err := set(v.Elem(), reflectValue, e.Inputs[i]); err != nil {
return err
}
default:
return fmt.Errorf("abi: cannot unmarshal tuple in to %v", typ)
}
}
return nil
}
func (e Event) isTupleReturn() bool { return len(e.Inputs) > 1 }
func (e Event) singleUnpack(v interface{}, output []byte) error {
// make sure the passed value is a pointer
valueOf := reflect.ValueOf(v)
if reflect.Ptr != valueOf.Kind() {
return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
}
if e.Inputs[0].Indexed {
return fmt.Errorf("abi: attempting to unpack indexed variable into element.")
}
value := valueOf.Elem()
marshalledValue, err := toGoType(0, e.Inputs[0].Type, output)
if err != nil {
return err
}
if err := set(value, reflect.ValueOf(marshalledValue), e.Inputs[0]); err != nil {
return err
}
return nil
}
...@@ -31,7 +31,7 @@ func TestEventId(t *testing.T) { ...@@ -31,7 +31,7 @@ func TestEventId(t *testing.T) {
}{ }{
{ {
definition: `[ definition: `[
{ "type" : "event", "name" : "balance", "inputs": [{ "name" : "in", "type": "uint" }] }, { "type" : "event", "name" : "balance", "inputs": [{ "name" : "in", "type": "uint256" }] },
{ "type" : "event", "name" : "check", "inputs": [{ "name" : "t", "type": "address" }, { "name": "b", "type": "uint256" }] } { "type" : "event", "name" : "check", "inputs": [{ "name" : "t", "type": "address" }, { "name": "b", "type": "uint256" }] }
]`, ]`,
expectations: map[string]common.Hash{ expectations: map[string]common.Hash{
......
...@@ -77,6 +77,85 @@ func (method Method) pack(args ...interface{}) ([]byte, error) { ...@@ -77,6 +77,85 @@ func (method Method) pack(args ...interface{}) ([]byte, error) {
return ret, nil return ret, nil
} }
// unpacks a method return tuple into a struct of corresponding go types
//
// Unpacking can be done into a struct or a slice/array.
func (method Method) tupleUnpack(v interface{}, output []byte) error {
// make sure the passed value is a pointer
valueOf := reflect.ValueOf(v)
if reflect.Ptr != valueOf.Kind() {
return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
}
var (
value = valueOf.Elem()
typ = value.Type()
)
j := 0
for i := 0; i < len(method.Outputs); i++ {
toUnpack := method.Outputs[i]
if toUnpack.Type.T == ArrayTy {
// need to move this up because they read sequentially
j += toUnpack.Type.Size
}
marshalledValue, err := toGoType((i+j)*32, toUnpack.Type, output)
if err != nil {
return err
}
reflectValue := reflect.ValueOf(marshalledValue)
switch value.Kind() {
case reflect.Struct:
for j := 0; j < typ.NumField(); j++ {
field := typ.Field(j)
// TODO read tags: `abi:"fieldName"`
if field.Name == strings.ToUpper(method.Outputs[i].Name[:1])+method.Outputs[i].Name[1:] {
if err := set(value.Field(j), reflectValue, method.Outputs[i]); err != nil {
return err
}
}
}
case reflect.Slice, reflect.Array:
if value.Len() < i {
return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(method.Outputs), value.Len())
}
v := value.Index(i)
if v.Kind() != reflect.Ptr && v.Kind() != reflect.Interface {
return fmt.Errorf("abi: cannot unmarshal %v in to %v", v.Type(), reflectValue.Type())
}
reflectValue := reflect.ValueOf(marshalledValue)
if err := set(v.Elem(), reflectValue, method.Outputs[i]); err != nil {
return err
}
default:
return fmt.Errorf("abi: cannot unmarshal tuple in to %v", typ)
}
}
return nil
}
func (method Method) isTupleReturn() bool { return len(method.Outputs) > 1 }
func (method Method) singleUnpack(v interface{}, output []byte) error {
// make sure the passed value is a pointer
valueOf := reflect.ValueOf(v)
if reflect.Ptr != valueOf.Kind() {
return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
}
value := valueOf.Elem()
marshalledValue, err := toGoType(0, method.Outputs[0].Type, output)
if err != nil {
return err
}
if err := set(value, reflect.ValueOf(marshalledValue), method.Outputs[0]); err != nil {
return err
}
return nil
}
// Sig returns the methods string signature according to the ABI spec. // Sig returns the methods string signature according to the ABI spec.
// //
// Example // Example
......
...@@ -25,36 +25,23 @@ import ( ...@@ -25,36 +25,23 @@ import (
) )
var ( var (
big_t = reflect.TypeOf(big.Int{}) big_t = reflect.TypeOf(&big.Int{})
ubig_t = reflect.TypeOf(big.Int{}) derefbig_t = reflect.TypeOf(big.Int{})
byte_t = reflect.TypeOf(byte(0)) uint8_t = reflect.TypeOf(uint8(0))
byte_ts = reflect.TypeOf([]byte(nil)) uint16_t = reflect.TypeOf(uint16(0))
uint_t = reflect.TypeOf(uint(0)) uint32_t = reflect.TypeOf(uint32(0))
uint8_t = reflect.TypeOf(uint8(0)) uint64_t = reflect.TypeOf(uint64(0))
uint16_t = reflect.TypeOf(uint16(0)) int_t = reflect.TypeOf(int(0))
uint32_t = reflect.TypeOf(uint32(0)) int8_t = reflect.TypeOf(int8(0))
uint64_t = reflect.TypeOf(uint64(0)) int16_t = reflect.TypeOf(int16(0))
int_t = reflect.TypeOf(int(0)) int32_t = reflect.TypeOf(int32(0))
int8_t = reflect.TypeOf(int8(0)) int64_t = reflect.TypeOf(int64(0))
int16_t = reflect.TypeOf(int16(0)) address_t = reflect.TypeOf(common.Address{})
int32_t = reflect.TypeOf(int32(0)) int_ts = reflect.TypeOf([]int(nil))
int64_t = reflect.TypeOf(int64(0)) int8_ts = reflect.TypeOf([]int8(nil))
hash_t = reflect.TypeOf(common.Hash{}) int16_ts = reflect.TypeOf([]int16(nil))
address_t = reflect.TypeOf(common.Address{}) int32_ts = reflect.TypeOf([]int32(nil))
int64_ts = reflect.TypeOf([]int64(nil))
uint_ts = reflect.TypeOf([]uint(nil))
uint8_ts = reflect.TypeOf([]uint8(nil))
uint16_ts = reflect.TypeOf([]uint16(nil))
uint32_ts = reflect.TypeOf([]uint32(nil))
uint64_ts = reflect.TypeOf([]uint64(nil))
ubig_ts = reflect.TypeOf([]*big.Int(nil))
int_ts = reflect.TypeOf([]int(nil))
int8_ts = reflect.TypeOf([]int8(nil))
int16_ts = reflect.TypeOf([]int16(nil))
int32_ts = reflect.TypeOf([]int32(nil))
int64_ts = reflect.TypeOf([]int64(nil))
big_ts = reflect.TypeOf([]*big.Int(nil))
) )
// U256 converts a big Int into a 256bit EVM number. // U256 converts a big Int into a 256bit EVM number.
......
...@@ -61,8 +61,9 @@ func packElement(t Type, reflectValue reflect.Value) []byte { ...@@ -61,8 +61,9 @@ func packElement(t Type, reflectValue reflect.Value) []byte {
reflectValue = mustArrayToByteSlice(reflectValue) reflectValue = mustArrayToByteSlice(reflectValue)
} }
return common.RightPadBytes(reflectValue.Bytes(), 32) return common.RightPadBytes(reflectValue.Bytes(), 32)
default:
panic("abi: fatal error")
} }
panic("abi: fatal error")
} }
// packNum packs the given number (using the reflect value) and will cast it to appropriate number representation // packNum packs the given number (using the reflect value) and will cast it to appropriate number representation
...@@ -74,6 +75,8 @@ func packNum(value reflect.Value) []byte { ...@@ -74,6 +75,8 @@ func packNum(value reflect.Value) []byte {
return U256(big.NewInt(value.Int())) return U256(big.NewInt(value.Int()))
case reflect.Ptr: case reflect.Ptr:
return U256(value.Interface().(*big.Int)) return U256(value.Interface().(*big.Int))
default:
panic("abi: fatal error")
} }
return nil
} }
...@@ -322,12 +322,12 @@ func TestPack(t *testing.T) { ...@@ -322,12 +322,12 @@ func TestPack(t *testing.T) {
} { } {
typ, err := NewType(test.typ) typ, err := NewType(test.typ)
if err != nil { if err != nil {
t.Fatal("unexpected parse error:", err) t.Fatalf("%v failed. Unexpected parse error: %v", i, err)
} }
output, err := typ.pack(reflect.ValueOf(test.input)) output, err := typ.pack(reflect.ValueOf(test.input))
if err != nil { if err != nil {
t.Fatal("unexpected pack error:", err) t.Fatalf("%v failed. Unexpected pack error: %v", i, err)
} }
if !bytes.Equal(output, test.output) { if !bytes.Equal(output, test.output) {
...@@ -435,7 +435,4 @@ func TestPackNumber(t *testing.T) { ...@@ -435,7 +435,4 @@ func TestPackNumber(t *testing.T) {
t.Errorf("test %d: pack mismatch: have %x, want %x", i, packed, tt.packed) t.Errorf("test %d: pack mismatch: have %x, want %x", i, packed, tt.packed)
} }
} }
if packed := packNum(reflect.ValueOf("string")); packed != nil {
t.Errorf("expected 'string' to pack to nil. got %x instead", packed)
}
} }
...@@ -24,7 +24,7 @@ import ( ...@@ -24,7 +24,7 @@ import (
// indirect recursively dereferences the value until it either gets the value // indirect recursively dereferences the value until it either gets the value
// or finds a big.Int // or finds a big.Int
func indirect(v reflect.Value) reflect.Value { func indirect(v reflect.Value) reflect.Value {
if v.Kind() == reflect.Ptr && v.Elem().Type() != big_t { if v.Kind() == reflect.Ptr && v.Elem().Type() != derefbig_t {
return indirect(v.Elem()) return indirect(v.Elem())
} }
return v return v
...@@ -73,15 +73,9 @@ func mustArrayToByteSlice(value reflect.Value) reflect.Value { ...@@ -73,15 +73,9 @@ func mustArrayToByteSlice(value reflect.Value) reflect.Value {
func set(dst, src reflect.Value, output Argument) error { func set(dst, src reflect.Value, output Argument) error {
dstType := dst.Type() dstType := dst.Type()
srcType := src.Type() srcType := src.Type()
switch { switch {
case dstType.AssignableTo(src.Type()): case dstType.AssignableTo(srcType):
dst.Set(src) dst.Set(src)
case dstType.Kind() == reflect.Array && srcType.Kind() == reflect.Slice:
if dst.Len() < output.Type.SliceSize {
return fmt.Errorf("abi: cannot unmarshal src (len=%d) in to dst (len=%d)", output.Type.SliceSize, dst.Len())
}
reflect.Copy(dst, src)
case dstType.Kind() == reflect.Interface: case dstType.Kind() == reflect.Interface:
dst.Set(src) dst.Set(src)
case dstType.Kind() == reflect.Ptr: case dstType.Kind() == reflect.Ptr:
......
...@@ -21,6 +21,7 @@ import ( ...@@ -21,6 +21,7 @@ import (
"reflect" "reflect"
"regexp" "regexp"
"strconv" "strconv"
"strings"
) )
const ( const (
...@@ -29,6 +30,7 @@ const ( ...@@ -29,6 +30,7 @@ const (
BoolTy BoolTy
StringTy StringTy
SliceTy SliceTy
ArrayTy
AddressTy AddressTy
FixedBytesTy FixedBytesTy
BytesTy BytesTy
...@@ -39,9 +41,6 @@ const ( ...@@ -39,9 +41,6 @@ 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, IsArray bool
SliceSize int
Elem *Type Elem *Type
Kind reflect.Kind Kind reflect.Kind
...@@ -53,118 +52,116 @@ type Type struct { ...@@ -53,118 +52,116 @@ type Type struct {
} }
var ( var (
// fullTypeRegex parses the abi types
//
// Types can be in the format of:
//
// Input = Type [ "[" [ Number ] "]" ] Name .
// Type = [ "u" ] "int" [ Number ] [ x ] [ Number ].
//
// Examples:
//
// string int uint fixed
// string32 int8 uint8 uint[]
// address int256 uint256 fixed128x128[2]
fullTypeRegex = regexp.MustCompile(`([a-zA-Z0-9]+)(\[([0-9]*)\])?`)
// typeRegex parses the abi sub types // typeRegex parses the abi sub types
typeRegex = regexp.MustCompile("([a-zA-Z]+)(([0-9]+)(x([0-9]+))?)?") typeRegex = regexp.MustCompile("([a-zA-Z]+)(([0-9]+)(x([0-9]+))?)?")
) )
// NewType creates a new reflection type of abi type given in t. // NewType creates a new reflection type of abi type given in t.
func NewType(t string) (typ Type, err error) { func NewType(t string) (typ Type, err error) {
res := fullTypeRegex.FindAllStringSubmatch(t, -1)[0] // check that array brackets are equal if they exist
// check if type is slice and parse type. if strings.Count(t, "[") != strings.Count(t, "]") {
switch { return Type{}, fmt.Errorf("invalid arg type in abi")
case res[3] != "":
// err is ignored. Already checked for number through the regexp
typ.SliceSize, _ = strconv.Atoi(res[3])
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]) 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 {
i := strings.LastIndex(t, "[")
// recursively embed the type
embeddedType, err := NewType(t[:i])
if err != nil { if err != nil {
return Type{}, err return Type{}, err
} }
typ.Elem = &sliceType // grab the last cell and create a type from there
typ.stringKind = sliceType.stringKind + t[len(res[1]):] sliced := t[i:]
// Although we know that this is an array, we cannot return // grab the slice size with regexp
// as we don't know the type of the element, however, if it re := regexp.MustCompile("[0-9]+")
// is still an array, then don't determine the type. intz := re.FindAllString(sliced, -1)
if typ.Elem.IsArray || typ.Elem.IsSlice {
return typ, nil if len(intz) == 0 {
} // is a slice
} typ.T = SliceTy
typ.Kind = reflect.Slice
// parse the type and size of the abi-type. typ.Elem = &embeddedType
parsedType := typeRegex.FindAllStringSubmatch(res[1], -1)[0] typ.Type = reflect.SliceOf(embeddedType.Type)
// varSize is the size of the variable } else if len(intz) == 1 {
var varSize int // is a array
if len(parsedType[3]) > 0 { typ.T = ArrayTy
var err error typ.Kind = reflect.Array
varSize, err = strconv.Atoi(parsedType[2]) typ.Elem = &embeddedType
if err != nil { typ.Size, err = strconv.Atoi(intz[0])
return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err) if err != nil {
return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
}
typ.Type = reflect.ArrayOf(typ.Size, embeddedType.Type)
} else {
return Type{}, fmt.Errorf("invalid formatting of array type")
} }
} return typ, err
// varType is the parsed abi type } else {
varType := parsedType[1] // parse the type and size of the abi-type.
// substitute canonical integer parsedType := typeRegex.FindAllStringSubmatch(t, -1)[0]
if varSize == 0 && (varType == "int" || varType == "uint") { // varSize is the size of the variable
varSize = 256 var varSize int
t += "256" if len(parsedType[3]) > 0 {
} var err error
varSize, err = strconv.Atoi(parsedType[2])
// only set stringKind if not array or slice, as for those, if err != nil {
// the correct string type has been set return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
if !(typ.IsArray || typ.IsSlice) { }
typ.stringKind = t
}
switch varType {
case "int":
typ.Kind, typ.Type = reflectIntKindAndType(false, varSize)
typ.Size = varSize
typ.T = IntTy
case "uint":
typ.Kind, typ.Type = reflectIntKindAndType(true, varSize)
typ.Size = varSize
typ.T = UintTy
case "bool":
typ.Kind = reflect.Bool
typ.T = BoolTy
case "address":
typ.Kind = reflect.Array
typ.Type = address_t
typ.Size = 20
typ.T = AddressTy
case "string":
typ.Kind = reflect.String
typ.Size = -1
typ.T = StringTy
case "bytes":
sliceType, _ := NewType("uint8")
typ.Elem = &sliceType
if varSize == 0 {
typ.IsSlice = true
typ.T = BytesTy
typ.SliceSize = -1
} else { } else {
typ.IsArray = true if parsedType[0] == "uint" || parsedType[0] == "int" {
typ.T = FixedBytesTy // this should fail because it means that there's something wrong with
typ.SliceSize = varSize // 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
varType := parsedType[1]
switch varType {
case "int":
typ.Kind, typ.Type = reflectIntKindAndType(false, varSize)
typ.Size = varSize
typ.T = IntTy
case "uint":
typ.Kind, typ.Type = reflectIntKindAndType(true, varSize)
typ.Size = varSize
typ.T = UintTy
case "bool":
typ.Kind = reflect.Bool
typ.T = BoolTy
typ.Type = reflect.TypeOf(bool(false))
case "address":
typ.Kind = reflect.Array
typ.Type = address_t
typ.Size = 20
typ.T = AddressTy
case "string":
typ.Kind = reflect.String
typ.Type = reflect.TypeOf("")
typ.T = StringTy
case "bytes":
if varSize == 0 {
typ.T = BytesTy
typ.Kind = reflect.Slice
typ.Type = reflect.SliceOf(reflect.TypeOf(byte(0)))
} else {
typ.T = FixedBytesTy
typ.Kind = reflect.Array
typ.Size = varSize
typ.Type = reflect.ArrayOf(varSize, reflect.TypeOf(byte(0)))
}
case "function":
typ.Kind = reflect.Array
typ.T = FunctionTy
typ.Size = 24
typ.Type = reflect.ArrayOf(24, reflect.TypeOf(byte(0)))
default:
return Type{}, fmt.Errorf("unsupported arg type: %s", t)
} }
case "function":
sliceType, _ := NewType("uint8")
typ.Elem = &sliceType
typ.IsArray = true
typ.T = FunctionTy
typ.SliceSize = 24
default:
return Type{}, fmt.Errorf("unsupported arg type: %s", t)
} }
return return
...@@ -183,7 +180,7 @@ func (t Type) pack(v reflect.Value) ([]byte, error) { ...@@ -183,7 +180,7 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
return nil, err return nil, err
} }
if (t.IsSlice || t.IsArray) && t.T != BytesTy && t.T != FixedBytesTy && t.T != FunctionTy { if t.T == SliceTy || t.T == ArrayTy {
var packed []byte var packed []byte
for i := 0; i < v.Len(); i++ { for i := 0; i < v.Len(); i++ {
...@@ -193,18 +190,17 @@ func (t Type) pack(v reflect.Value) ([]byte, error) { ...@@ -193,18 +190,17 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
} }
packed = append(packed, val...) packed = append(packed, val...)
} }
if t.IsSlice { if t.T == SliceTy {
return packBytesSlice(packed, v.Len()), nil return packBytesSlice(packed, v.Len()), nil
} else if t.IsArray { } else if t.T == ArrayTy {
return packed, nil return packed, nil
} }
} }
return packElement(t, v), nil return packElement(t, v), nil
} }
// requireLengthPrefix returns whether the type requires any sort of length // requireLengthPrefix returns whether the type requires any sort of length
// prefixing. // prefixing.
func (t Type) requiresLengthPrefix() bool { func (t Type) requiresLengthPrefix() bool {
return t.T != FixedBytesTy && (t.T == StringTy || t.T == BytesTy || t.IsSlice) return t.T == StringTy || t.T == BytesTy || t.T == SliceTy
} }
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment