aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--accounts/abi/abi.go115
-rw-r--r--accounts/abi/abi_test.go21
-rw-r--r--accounts/abi/bind/bind_test.go2
-rw-r--r--accounts/abi/error.go24
-rw-r--r--accounts/abi/event.go91
-rw-r--r--accounts/abi/event_test.go2
-rw-r--r--accounts/abi/method.go79
-rw-r--r--accounts/abi/numbers.go47
-rw-r--r--accounts/abi/pack.go7
-rw-r--r--accounts/abi/pack_test.go7
-rw-r--r--accounts/abi/reflect.go10
-rw-r--r--accounts/abi/type.go210
-rw-r--r--accounts/abi/type_test.go215
-rw-r--r--accounts/abi/unpack.go279
-rw-r--r--accounts/abi/unpack_test.go641
16 files changed, 904 insertions, 848 deletions
diff --git a/README.md b/README.md
index ab741fcc9..a13476517 100644
--- a/README.md
+++ b/README.md
@@ -35,7 +35,7 @@ The go-ethereum project comes with several wrappers/executables found in the `cm
| **`geth`** | Our main Ethereum CLI client. It is the entry point into the Ethereum network (main-, test- or private net), capable of running as a full node (default) archive node (retaining all historical state) or a light node (retrieving data live). It can be used by other processes as a gateway into the Ethereum network via JSON RPC endpoints exposed on top of HTTP, WebSocket and/or IPC transports. `geth --help` and the [CLI Wiki page](https://github.com/ethereum/go-ethereum/wiki/Command-Line-Options) for command line options. |
| `abigen` | Source code generator to convert Ethereum contract definitions into easy to use, compile-time type-safe Go packages. It operates on plain [Ethereum contract ABIs](https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI) with expanded functionality if the contract bytecode is also available. However it also accepts Solidity source files, making development much more streamlined. Please see our [Native DApps](https://github.com/ethereum/go-ethereum/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts) wiki page for details. |
| `bootnode` | Stripped down version of our Ethereum client implementation that only takes part in the network node discovery protocol, but does not run any of the higher level application protocols. It can be used as a lightweight bootstrap node to aid in finding peers in private networks. |
-| `evm` | Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode. Its purpose is to allow insolated, fine-grained debugging of EVM opcodes (e.g. `evm --code 60ff60ff --debug`). |
+| `evm` | Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode. Its purpose is to allow isolated, fine-grained debugging of EVM opcodes (e.g. `evm --code 60ff60ff --debug`). |
| `gethrpctest` | Developer utility tool to support our [ethereum/rpc-test](https://github.com/ethereum/rpc-tests) test suite which validates baseline conformity to the [Ethereum JSON RPC](https://github.com/ethereum/wiki/wiki/JSON-RPC) specs. Please see the [test suite's readme](https://github.com/ethereum/rpc-tests/blob/master/README.md) for details. |
| `rlpdump` | Developer utility tool to convert binary RLP ([Recursive Length Prefix](https://github.com/ethereum/wiki/wiki/RLP)) dumps (data encoding used by the Ethereum protocol both network as well as consensus wise) to user friendlier hierarchical representation (e.g. `rlpdump --hex CE0183FFFFFFC4C304050583616263`). |
| `swarm` | swarm daemon and tools. This is the entrypoint for the swarm network. `swarm --help` for command line options and subcommands. See https://swarm-guide.readthedocs.io for swarm documentation. |
diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go
index 2a06d474b..205dc300b 100644
--- a/accounts/abi/abi.go
+++ b/accounts/abi/abi.go
@@ -20,10 +20,6 @@ import (
"encoding/json"
"fmt"
"io"
- "reflect"
- "strings"
-
- "github.com/ethereum/go-ethereum/common"
)
// 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) {
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
-func (abi ABI) Unpack(v interface{}, name string, output []byte) error {
- var method = abi.Methods[name]
-
- 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)
+func (abi ABI) Unpack(v interface{}, name string, output []byte) (err error) {
+ if err = bytesAreProper(output); err != nil {
+ return err
}
-
- var (
- value = valueOf.Elem()
- typ = value.Type()
- )
-
- if len(method.Outputs) > 1 {
- 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)
- }
-
+ // since there can't be naming collisions with contracts and events,
+ // we need to decide whether we're calling a method or an event
+ var unpack unpacker
+ if method, ok := abi.Methods[name]; ok {
+ unpack = method
+ } else if event, ok := abi.Events[name]; ok {
+ unpack = event
} else {
- marshalledValue, err := toGoType(0, method.Outputs[0], output)
- if err != nil {
- return err
- }
- if err := set(value, reflect.ValueOf(marshalledValue), method.Outputs[0]); err != nil {
- return err
- }
+ return fmt.Errorf("abi: could not locate named method or event.")
}
- 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 {
diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go
index a3aa9446e..79c4d4a16 100644
--- a/accounts/abi/abi_test.go
+++ b/accounts/abi/abi_test.go
@@ -29,25 +29,6 @@ import (
"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 = `
[
{ "type" : "function", "name" : "balance", "constant" : true },
@@ -191,7 +172,7 @@ func TestMethodSignature(t *testing.T) {
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}
exp = "foo(uint256)"
if m.Sig() != exp {
diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/bind/bind_test.go
index 43ed53b92..17b221642 100644
--- a/accounts/abi/bind/bind_test.go
+++ b/accounts/abi/bind/bind_test.go
@@ -472,7 +472,7 @@ func TestBindings(t *testing.T) {
t.Fatalf("failed to create temporary workspace: %v", err)
}
defer os.RemoveAll(ws)
-
+
pkg := filepath.Join(ws, "bindtest")
if err = os.MkdirAll(pkg, 0700); err != nil {
t.Fatalf("failed to create package: %v", err)
diff --git a/accounts/abi/error.go b/accounts/abi/error.go
index 420acf418..9d8674ad0 100644
--- a/accounts/abi/error.go
+++ b/accounts/abi/error.go
@@ -39,22 +39,23 @@ func formatSliceString(kind reflect.Kind, sliceSize int) string {
// type in t.
func sliceTypeCheck(t Type, val reflect.Value) error {
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 {
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))
}
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
}
@@ -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
// type in t.
func typeCheck(t Type, value reflect.Value) error {
- if t.IsSlice || t.IsArray {
+ if t.T == SliceTy || t.T == ArrayTy {
return sliceTypeCheck(t, value)
}
// Check base type validity. Element types will be checked later on.
if 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.
diff --git a/accounts/abi/event.go b/accounts/abi/event.go
index 51ab84241..44ed7b8df 100644
--- a/accounts/abi/event.go
+++ b/accounts/abi/event.go
@@ -18,6 +18,7 @@ package abi
import (
"fmt"
+ "reflect"
"strings"
"github.com/ethereum/go-ethereum/common"
@@ -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, ",")))))
}
+
+// 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
+}
diff --git a/accounts/abi/event_test.go b/accounts/abi/event_test.go
index b5054a032..7e2f13f76 100644
--- a/accounts/abi/event_test.go
+++ b/accounts/abi/event_test.go
@@ -31,7 +31,7 @@ func TestEventId(t *testing.T) {
}{
{
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" }] }
]`,
expectations: map[string]common.Hash{
diff --git a/accounts/abi/method.go b/accounts/abi/method.go
index 62b3d2957..d8838e9ed 100644
--- a/accounts/abi/method.go
+++ b/accounts/abi/method.go
@@ -77,6 +77,85 @@ func (method Method) pack(args ...interface{}) ([]byte, error) {
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.
//
// Example
diff --git a/accounts/abi/numbers.go b/accounts/abi/numbers.go
index 5d3efff52..9ad99f90d 100644
--- a/accounts/abi/numbers.go
+++ b/accounts/abi/numbers.go
@@ -25,36 +25,23 @@ import (
)
var (
- big_t = reflect.TypeOf(big.Int{})
- ubig_t = reflect.TypeOf(big.Int{})
- byte_t = reflect.TypeOf(byte(0))
- byte_ts = reflect.TypeOf([]byte(nil))
- uint_t = reflect.TypeOf(uint(0))
- uint8_t = reflect.TypeOf(uint8(0))
- uint16_t = reflect.TypeOf(uint16(0))
- uint32_t = reflect.TypeOf(uint32(0))
- uint64_t = reflect.TypeOf(uint64(0))
- int_t = reflect.TypeOf(int(0))
- int8_t = reflect.TypeOf(int8(0))
- int16_t = reflect.TypeOf(int16(0))
- int32_t = reflect.TypeOf(int32(0))
- int64_t = reflect.TypeOf(int64(0))
- hash_t = reflect.TypeOf(common.Hash{})
- address_t = reflect.TypeOf(common.Address{})
-
- 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))
+ big_t = reflect.TypeOf(&big.Int{})
+ derefbig_t = reflect.TypeOf(big.Int{})
+ uint8_t = reflect.TypeOf(uint8(0))
+ uint16_t = reflect.TypeOf(uint16(0))
+ uint32_t = reflect.TypeOf(uint32(0))
+ uint64_t = reflect.TypeOf(uint64(0))
+ int_t = reflect.TypeOf(int(0))
+ int8_t = reflect.TypeOf(int8(0))
+ int16_t = reflect.TypeOf(int16(0))
+ int32_t = reflect.TypeOf(int32(0))
+ int64_t = reflect.TypeOf(int64(0))
+ address_t = reflect.TypeOf(common.Address{})
+ 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))
)
// U256 converts a big Int into a 256bit EVM number.
diff --git a/accounts/abi/pack.go b/accounts/abi/pack.go
index 4d8a3f031..072e80536 100644
--- a/accounts/abi/pack.go
+++ b/accounts/abi/pack.go
@@ -61,8 +61,9 @@ func packElement(t Type, reflectValue reflect.Value) []byte {
reflectValue = mustArrayToByteSlice(reflectValue)
}
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
@@ -74,6 +75,8 @@ func packNum(value reflect.Value) []byte {
return U256(big.NewInt(value.Int()))
case reflect.Ptr:
return U256(value.Interface().(*big.Int))
+ default:
+ panic("abi: fatal error")
}
- return nil
+
}
diff --git a/accounts/abi/pack_test.go b/accounts/abi/pack_test.go
index c6cfb56ea..36401ee67 100644
--- a/accounts/abi/pack_test.go
+++ b/accounts/abi/pack_test.go
@@ -322,12 +322,12 @@ func TestPack(t *testing.T) {
} {
typ, err := NewType(test.typ)
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))
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) {
@@ -435,7 +435,4 @@ func TestPackNumber(t *testing.T) {
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)
- }
}
diff --git a/accounts/abi/reflect.go b/accounts/abi/reflect.go
index 8fa75df07..e953b77c1 100644
--- a/accounts/abi/reflect.go
+++ b/accounts/abi/reflect.go
@@ -24,7 +24,7 @@ import (
// indirect recursively dereferences the value until it either gets the value
// or finds a big.Int
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 v
@@ -73,15 +73,9 @@ func mustArrayToByteSlice(value reflect.Value) reflect.Value {
func set(dst, src reflect.Value, output Argument) error {
dstType := dst.Type()
srcType := src.Type()
-
switch {
- case dstType.AssignableTo(src.Type()):
+ case dstType.AssignableTo(srcType):
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:
dst.Set(src)
case dstType.Kind() == reflect.Ptr:
diff --git a/accounts/abi/type.go b/accounts/abi/type.go
index 5f20babb3..fba10b96d 100644
--- a/accounts/abi/type.go
+++ b/accounts/abi/type.go
@@ -21,6 +21,7 @@ import (
"reflect"
"regexp"
"strconv"
+ "strings"
)
const (
@@ -29,6 +30,7 @@ const (
BoolTy
StringTy
SliceTy
+ ArrayTy
AddressTy
FixedBytesTy
BytesTy
@@ -39,9 +41,6 @@ const (
// Type is the reflection of the supported argument type
type Type struct {
- IsSlice, IsArray bool
- SliceSize int
-
Elem *Type
Kind reflect.Kind
@@ -53,118 +52,116 @@ type Type struct {
}
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 = regexp.MustCompile("([a-zA-Z]+)(([0-9]+)(x([0-9]+))?)?")
)
// NewType creates a new reflection type of abi type given in t.
func NewType(t string) (typ Type, err error) {
- 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.IsArray = true
- case res[2] != "":
- typ.IsSlice, typ.SliceSize = true, -1
- case res[0] == "":
- return Type{}, fmt.Errorf("abi: type parse error: %s", t)
+ // check that array brackets are equal if they exist
+ if strings.Count(t, "[") != strings.Count(t, "]") {
+ return Type{}, fmt.Errorf("invalid arg type in abi")
}
- 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 {
return Type{}, err
}
- typ.Elem = &sliceType
- typ.stringKind = sliceType.stringKind + t[len(res[1]):]
- // Although we know that this is an array, we cannot return
- // as we don't know the type of the element, however, if it
- // is still an array, then don't determine the type.
- if typ.Elem.IsArray || typ.Elem.IsSlice {
- return typ, nil
- }
- }
-
- // 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[3]) > 0 {
- var err error
- varSize, err = strconv.Atoi(parsedType[2])
- if err != nil {
- return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
+ // grab the last cell and create a type from there
+ sliced := t[i:]
+ // grab the slice size with regexp
+ re := regexp.MustCompile("[0-9]+")
+ intz := re.FindAllString(sliced, -1)
+
+ if len(intz) == 0 {
+ // is a slice
+ typ.T = SliceTy
+ typ.Kind = reflect.Slice
+ typ.Elem = &embeddedType
+ typ.Type = reflect.SliceOf(embeddedType.Type)
+ } else if len(intz) == 1 {
+ // is a array
+ typ.T = ArrayTy
+ typ.Kind = reflect.Array
+ typ.Elem = &embeddedType
+ typ.Size, err = strconv.Atoi(intz[0])
+ 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")
}
- }
- // varType is the parsed abi type
- varType := parsedType[1]
- // substitute canonical integer
- if varSize == 0 && (varType == "int" || varType == "uint") {
- varSize = 256
- t += "256"
- }
-
- // only set stringKind if not array or slice, as for those,
- // the correct string type has been set
- 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
+ return typ, err
+ } else {
+ // parse the type and size of the abi-type.
+ parsedType := typeRegex.FindAllStringSubmatch(t, -1)[0]
+ // varSize is the size of the variable
+ var varSize int
+ if len(parsedType[3]) > 0 {
+ var err error
+ varSize, err = strconv.Atoi(parsedType[2])
+ if err != nil {
+ return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
+ }
} else {
- typ.IsArray = true
- typ.T = FixedBytesTy
- typ.SliceSize = varSize
+ if parsedType[0] == "uint" || parsedType[0] == "int" {
+ // this should fail because it means that there's something wrong with
+ // 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
@@ -183,7 +180,7 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
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
for i := 0; i < v.Len(); i++ {
@@ -193,18 +190,17 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
}
packed = append(packed, val...)
}
- if t.IsSlice {
+ if t.T == SliceTy {
return packBytesSlice(packed, v.Len()), nil
- } else if t.IsArray {
+ } else if t.T == ArrayTy {
return packed, nil
}
}
-
return packElement(t, v), nil
}
// requireLengthPrefix returns whether the type requires any sort of length
// prefixing.
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
}
diff --git a/accounts/abi/type_test.go b/accounts/abi/type_test.go
index 984a5bb4c..e55af1293 100644
--- a/accounts/abi/type_test.go
+++ b/accounts/abi/type_test.go
@@ -21,6 +21,7 @@ import (
"reflect"
"testing"
+ "github.com/davecgh/go-spew/spew"
"github.com/ethereum/go-ethereum/common"
)
@@ -34,51 +35,58 @@ func TestTypeRegexp(t *testing.T) {
blob string
kind Type
}{
- {"bool", Type{Kind: reflect.Bool, T: BoolTy, stringKind: "bool"}},
- {"bool[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Bool, T: BoolTy, Elem: &Type{Kind: reflect.Bool, T: BoolTy, stringKind: "bool"}, stringKind: "bool[]"}},
- {"bool[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Bool, T: BoolTy, Elem: &Type{Kind: reflect.Bool, T: BoolTy, stringKind: "bool"}, stringKind: "bool[2]"}},
+ {"bool", Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}},
+ {"bool[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]bool(nil)), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}},
+ {"bool[2]", Type{Size: 2, Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}},
+ {"bool[2][]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][]"}},
+ {"bool[][]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([][]bool{}), Elem: &Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][]"}},
+ {"bool[][2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]bool{}), Elem: &Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][2]"}},
+ {"bool[2][2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][2]"}},
+ {"bool[2][][2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][][2]bool{}), Elem: &Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][]"}, stringKind: "bool[2][][2]"}},
+ {"bool[2][2][2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][2]"}, stringKind: "bool[2][2][2]"}},
+ {"bool[][][]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][]"}, stringKind: "bool[][][]"}},
+ {"bool[][2][]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][2][]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][2]"}, stringKind: "bool[][2][]"}},
{"int8", Type{Kind: reflect.Int8, Type: int8_t, Size: 8, T: IntTy, stringKind: "int8"}},
{"int16", Type{Kind: reflect.Int16, Type: int16_t, Size: 16, T: IntTy, stringKind: "int16"}},
{"int32", Type{Kind: reflect.Int32, Type: int32_t, Size: 32, T: IntTy, stringKind: "int32"}},
{"int64", Type{Kind: reflect.Int64, Type: int64_t, Size: 64, T: IntTy, stringKind: "int64"}},
{"int256", Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}},
- {"int8[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Int8, Type: int8_t, Size: 8, T: IntTy, Elem: &Type{Kind: reflect.Int8, Type: int8_t, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[]"}},
- {"int8[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Int8, Type: int8_t, Size: 8, T: IntTy, Elem: &Type{Kind: reflect.Int8, Type: int8_t, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[2]"}},
- {"int16[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Int16, Type: int16_t, Size: 16, T: IntTy, Elem: &Type{Kind: reflect.Int16, Type: int16_t, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[]"}},
- {"int16[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Int16, Type: int16_t, Size: 16, T: IntTy, Elem: &Type{Kind: reflect.Int16, Type: int16_t, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[2]"}},
- {"int32[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Int32, Type: int32_t, Size: 32, T: IntTy, Elem: &Type{Kind: reflect.Int32, Type: int32_t, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[]"}},
- {"int32[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Int32, Type: int32_t, Size: 32, T: IntTy, Elem: &Type{Kind: reflect.Int32, Type: int32_t, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[2]"}},
- {"int64[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Int64, Type: int64_t, Size: 64, T: IntTy, Elem: &Type{Kind: reflect.Int64, Type: int64_t, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[]"}},
- {"int64[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Int64, Type: int64_t, Size: 64, T: IntTy, Elem: &Type{Kind: reflect.Int64, Type: int64_t, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[2]"}},
- {"int256[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[]"}},
- {"int256[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[2]"}},
+ {"int8[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int8{}), Elem: &Type{Kind: reflect.Int8, Type: int8_t, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[]"}},
+ {"int8[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int8{}), Elem: &Type{Kind: reflect.Int8, Type: int8_t, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[2]"}},
+ {"int16[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int16{}), Elem: &Type{Kind: reflect.Int16, Type: int16_t, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[]"}},
+ {"int16[2]", Type{Size: 2, Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]int16{}), Elem: &Type{Kind: reflect.Int16, Type: int16_t, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[2]"}},
+ {"int32[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int32{}), Elem: &Type{Kind: reflect.Int32, Type: int32_t, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[]"}},
+ {"int32[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int32{}), Elem: &Type{Kind: reflect.Int32, Type: int32_t, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[2]"}},
+ {"int64[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int64{}), Elem: &Type{Kind: reflect.Int64, Type: int64_t, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[]"}},
+ {"int64[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int64{}), Elem: &Type{Kind: reflect.Int64, Type: int64_t, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[2]"}},
+ {"int256[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[]"}},
+ {"int256[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[2]"}},
{"uint8", Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}},
{"uint16", Type{Kind: reflect.Uint16, Type: uint16_t, Size: 16, T: UintTy, stringKind: "uint16"}},
{"uint32", Type{Kind: reflect.Uint32, Type: uint32_t, Size: 32, T: UintTy, stringKind: "uint32"}},
{"uint64", Type{Kind: reflect.Uint64, Type: uint64_t, Size: 64, T: UintTy, stringKind: "uint64"}},
{"uint256", Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: UintTy, stringKind: "uint256"}},
- {"uint8[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, Elem: &Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[]"}},
- {"uint8[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, Elem: &Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[2]"}},
- {"uint16[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Uint16, Type: uint16_t, Size: 16, T: UintTy, Elem: &Type{Kind: reflect.Uint16, Type: uint16_t, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[]"}},
- {"uint16[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Uint16, Type: uint16_t, Size: 16, T: UintTy, Elem: &Type{Kind: reflect.Uint16, Type: uint16_t, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[2]"}},
- {"uint32[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Uint32, Type: uint32_t, Size: 32, T: UintTy, Elem: &Type{Kind: reflect.Uint32, Type: uint32_t, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[]"}},
- {"uint32[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Uint32, Type: uint32_t, Size: 32, T: UintTy, Elem: &Type{Kind: reflect.Uint32, Type: uint32_t, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[2]"}},
- {"uint64[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Uint64, Type: uint64_t, Size: 64, T: UintTy, Elem: &Type{Kind: reflect.Uint64, Type: uint64_t, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[]"}},
- {"uint64[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Uint64, Type: uint64_t, Size: 64, T: UintTy, Elem: &Type{Kind: reflect.Uint64, Type: uint64_t, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[2]"}},
- {"uint256[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Ptr, Type: big_t, Size: 256, T: UintTy, Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[]"}},
- {"uint256[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Ptr, Type: big_t, Size: 256, T: UintTy, Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[2]"}},
- {"bytes32", Type{IsArray: true, SliceSize: 32, Elem: &Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: FixedBytesTy, stringKind: "bytes32"}},
- {"bytes[]", Type{IsSlice: true, SliceSize: -1, Elem: &Type{IsSlice: true, SliceSize: -1, Elem: &Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: BytesTy, stringKind: "bytes"}, stringKind: "bytes[]"}},
- {"bytes[2]", Type{IsArray: true, SliceSize: 2, Elem: &Type{IsSlice: true, SliceSize: -1, Elem: &Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: BytesTy, stringKind: "bytes"}, stringKind: "bytes[2]"}},
- {"bytes32[]", Type{IsSlice: true, SliceSize: -1, Elem: &Type{IsArray: true, SliceSize: 32, Elem: &Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: FixedBytesTy, stringKind: "bytes32"}, stringKind: "bytes32[]"}},
- {"bytes32[2]", Type{IsArray: true, SliceSize: 2, Elem: &Type{IsArray: true, SliceSize: 32, Elem: &Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: FixedBytesTy, stringKind: "bytes32"}, stringKind: "bytes32[2]"}},
- {"string", Type{Kind: reflect.String, Size: -1, T: StringTy, stringKind: "string"}},
- {"string[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.String, T: StringTy, Size: -1, Elem: &Type{Kind: reflect.String, T: StringTy, Size: -1, stringKind: "string"}, stringKind: "string[]"}},
- {"string[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.String, T: StringTy, Size: -1, Elem: &Type{Kind: reflect.String, T: StringTy, Size: -1, stringKind: "string"}, stringKind: "string[2]"}},
+ {"uint8[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]uint8{}), Elem: &Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[]"}},
+ {"uint8[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint8{}), Elem: &Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[2]"}},
+ {"uint16[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint16{}), Elem: &Type{Kind: reflect.Uint16, Type: uint16_t, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[]"}},
+ {"uint16[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint16{}), Elem: &Type{Kind: reflect.Uint16, Type: uint16_t, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[2]"}},
+ {"uint32[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint32{}), Elem: &Type{Kind: reflect.Uint32, Type: uint32_t, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[]"}},
+ {"uint32[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint32{}), Elem: &Type{Kind: reflect.Uint32, Type: uint32_t, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[2]"}},
+ {"uint64[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint64{}), Elem: &Type{Kind: reflect.Uint64, Type: uint64_t, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[]"}},
+ {"uint64[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint64{}), Elem: &Type{Kind: reflect.Uint64, Type: uint64_t, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[2]"}},
+ {"uint256[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[]"}},
+ {"uint256[2]", Type{Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]*big.Int{}), Size: 2, Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[2]"}},
+ {"bytes32", Type{Kind: reflect.Array, T: FixedBytesTy, Size: 32, Type: reflect.TypeOf([32]byte{}), stringKind: "bytes32"}},
+ {"bytes[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][]byte{}), Elem: &Type{Kind: reflect.Slice, Type: reflect.TypeOf([]byte{}), T: BytesTy, stringKind: "bytes"}, stringKind: "bytes[]"}},
+ {"bytes[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]byte{}), Elem: &Type{T: BytesTy, Type: reflect.TypeOf([]byte{}), Kind: reflect.Slice, stringKind: "bytes"}, stringKind: "bytes[2]"}},
+ {"bytes32[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][32]byte{}), Elem: &Type{Kind: reflect.Array, Type: reflect.TypeOf([32]byte{}), T: FixedBytesTy, Size: 32, stringKind: "bytes32"}, stringKind: "bytes32[]"}},
+ {"bytes32[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][32]byte{}), Elem: &Type{Kind: reflect.Array, T: FixedBytesTy, Size: 32, Type: reflect.TypeOf([32]byte{}), stringKind: "bytes32"}, stringKind: "bytes32[2]"}},
+ {"string", Type{Kind: reflect.String, T: StringTy, Type: reflect.TypeOf(""), stringKind: "string"}},
+ {"string[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]string{}), Elem: &Type{Kind: reflect.String, Type: reflect.TypeOf(""), T: StringTy, stringKind: "string"}, stringKind: "string[]"}},
+ {"string[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]string{}), Elem: &Type{Kind: reflect.String, T: StringTy, Type: reflect.TypeOf(""), stringKind: "string"}, stringKind: "string[2]"}},
{"address", Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}},
- {"address[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Array, Type: address_t, T: AddressTy, Size: 20, Elem: &Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[]"}},
- {"address[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Array, Type: address_t, T: AddressTy, Size: 20, Elem: &Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[2]"}},
-
+ {"address[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]common.Address{}), Elem: &Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[]"}},
+ {"address[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]common.Address{}), Elem: &Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[2]"}},
// TODO when fixed types are implemented properly
// {"fixed", Type{}},
// {"fixed128x128", Type{}},
@@ -87,13 +95,14 @@ func TestTypeRegexp(t *testing.T) {
// {"fixed128x128[]", Type{}},
// {"fixed128x128[2]", Type{}},
}
- for i, tt := range tests {
+
+ for _, tt := range tests {
typ, err := NewType(tt.blob)
if err != nil {
- t.Errorf("type %d: failed to parse type string: %v", i, err)
+ t.Errorf("type %q: failed to parse type string: %v", tt.blob, err)
}
if !reflect.DeepEqual(typ, tt.kind) {
- t.Errorf("type %d: parsed type mismatch:\n have %+v\n want %+v", i, typeWithoutStringer(typ), typeWithoutStringer(tt.kind))
+ t.Errorf("type %q: parsed type mismatch:\nGOT %s\nWANT %s ", tt.blob, spew.Sdump(typeWithoutStringer(typ)), spew.Sdump(typeWithoutStringer(tt.kind)))
}
}
}
@@ -104,15 +113,90 @@ func TestTypeCheck(t *testing.T) {
input interface{}
err string
}{
- {"uint", big.NewInt(1), ""},
- {"int", big.NewInt(1), ""},
- {"uint30", big.NewInt(1), ""},
+ {"uint", big.NewInt(1), "unsupported arg type: uint"},
+ {"int", big.NewInt(1), "unsupported arg type: int"},
+ {"uint256", big.NewInt(1), ""},
+ {"uint256[][3][]", [][3][]*big.Int{{{}}}, ""},
+ {"uint256[][][3]", [3][][]*big.Int{{{}}}, ""},
+ {"uint256[3][][]", [][][3]*big.Int{{{}}}, ""},
+ {"uint256[3][3][3]", [3][3][3]*big.Int{{{}}}, ""},
+ {"uint8[][]", [][]uint8{}, ""},
+ {"int256", big.NewInt(1), ""},
+ {"uint8", uint8(1), ""},
+ {"uint16", uint16(1), ""},
+ {"uint32", uint32(1), ""},
+ {"uint64", uint64(1), ""},
+ {"int8", int8(1), ""},
+ {"int16", int16(1), ""},
+ {"int32", int32(1), ""},
+ {"int64", int64(1), ""},
+ {"uint24", big.NewInt(1), ""},
+ {"uint40", big.NewInt(1), ""},
+ {"uint48", big.NewInt(1), ""},
+ {"uint56", big.NewInt(1), ""},
+ {"uint72", big.NewInt(1), ""},
+ {"uint80", big.NewInt(1), ""},
+ {"uint88", big.NewInt(1), ""},
+ {"uint96", big.NewInt(1), ""},
+ {"uint104", big.NewInt(1), ""},
+ {"uint112", big.NewInt(1), ""},
+ {"uint120", big.NewInt(1), ""},
+ {"uint128", big.NewInt(1), ""},
+ {"uint136", big.NewInt(1), ""},
+ {"uint144", big.NewInt(1), ""},
+ {"uint152", big.NewInt(1), ""},
+ {"uint160", big.NewInt(1), ""},
+ {"uint168", big.NewInt(1), ""},
+ {"uint176", big.NewInt(1), ""},
+ {"uint184", big.NewInt(1), ""},
+ {"uint192", big.NewInt(1), ""},
+ {"uint200", big.NewInt(1), ""},
+ {"uint208", big.NewInt(1), ""},
+ {"uint216", big.NewInt(1), ""},
+ {"uint224", big.NewInt(1), ""},
+ {"uint232", big.NewInt(1), ""},
+ {"uint240", big.NewInt(1), ""},
+ {"uint248", big.NewInt(1), ""},
+ {"int24", big.NewInt(1), ""},
+ {"int40", big.NewInt(1), ""},
+ {"int48", big.NewInt(1), ""},
+ {"int56", big.NewInt(1), ""},
+ {"int72", big.NewInt(1), ""},
+ {"int80", big.NewInt(1), ""},
+ {"int88", big.NewInt(1), ""},
+ {"int96", big.NewInt(1), ""},
+ {"int104", big.NewInt(1), ""},
+ {"int112", big.NewInt(1), ""},
+ {"int120", big.NewInt(1), ""},
+ {"int128", big.NewInt(1), ""},
+ {"int136", big.NewInt(1), ""},
+ {"int144", big.NewInt(1), ""},
+ {"int152", big.NewInt(1), ""},
+ {"int160", big.NewInt(1), ""},
+ {"int168", big.NewInt(1), ""},
+ {"int176", big.NewInt(1), ""},
+ {"int184", big.NewInt(1), ""},
+ {"int192", big.NewInt(1), ""},
+ {"int200", big.NewInt(1), ""},
+ {"int208", big.NewInt(1), ""},
+ {"int216", big.NewInt(1), ""},
+ {"int224", big.NewInt(1), ""},
+ {"int232", big.NewInt(1), ""},
+ {"int240", big.NewInt(1), ""},
+ {"int248", big.NewInt(1), ""},
{"uint30", uint8(1), "abi: cannot use uint8 as type ptr as argument"},
+ {"uint8", uint16(1), "abi: cannot use uint16 as type uint8 as argument"},
+ {"uint8", uint32(1), "abi: cannot use uint32 as type uint8 as argument"},
+ {"uint8", uint64(1), "abi: cannot use uint64 as type uint8 as argument"},
+ {"uint8", int8(1), "abi: cannot use int8 as type uint8 as argument"},
+ {"uint8", int16(1), "abi: cannot use int16 as type uint8 as argument"},
+ {"uint8", int32(1), "abi: cannot use int32 as type uint8 as argument"},
+ {"uint8", int64(1), "abi: cannot use int64 as type uint8 as argument"},
{"uint16", uint16(1), ""},
{"uint16", uint8(1), "abi: cannot use uint8 as type uint16 as argument"},
{"uint16[]", []uint16{1, 2, 3}, ""},
{"uint16[]", [3]uint16{1, 2, 3}, ""},
- {"uint16[]", []uint32{1, 2, 3}, "abi: cannot use []uint32 as type []uint16 as argument"},
+ {"uint16[]", []uint32{1, 2, 3}, "abi: cannot use []uint32 as type [0]uint16 as argument"},
{"uint16[3]", [3]uint32{1, 2, 3}, "abi: cannot use [3]uint32 as type [3]uint16 as argument"},
{"uint16[3]", [4]uint16{1, 2, 3}, "abi: cannot use [4]uint16 as type [3]uint16 as argument"},
{"uint16[3]", []uint16{1, 2, 3}, ""},
@@ -122,20 +206,61 @@ func TestTypeCheck(t *testing.T) {
{"address[1]", [1]common.Address{{1}}, ""},
{"address[2]", [1]common.Address{{1}}, "abi: cannot use [1]array as type [2]array as argument"},
{"bytes32", [32]byte{}, ""},
+ {"bytes31", [31]byte{}, ""},
+ {"bytes30", [30]byte{}, ""},
+ {"bytes29", [29]byte{}, ""},
+ {"bytes28", [28]byte{}, ""},
+ {"bytes27", [27]byte{}, ""},
+ {"bytes26", [26]byte{}, ""},
+ {"bytes25", [25]byte{}, ""},
+ {"bytes24", [24]byte{}, ""},
+ {"bytes23", [23]byte{}, ""},
+ {"bytes22", [22]byte{}, ""},
+ {"bytes21", [21]byte{}, ""},
+ {"bytes20", [20]byte{}, ""},
+ {"bytes19", [19]byte{}, ""},
+ {"bytes18", [18]byte{}, ""},
+ {"bytes17", [17]byte{}, ""},
+ {"bytes16", [16]byte{}, ""},
+ {"bytes15", [15]byte{}, ""},
+ {"bytes14", [14]byte{}, ""},
+ {"bytes13", [13]byte{}, ""},
+ {"bytes12", [12]byte{}, ""},
+ {"bytes11", [11]byte{}, ""},
+ {"bytes10", [10]byte{}, ""},
+ {"bytes9", [9]byte{}, ""},
+ {"bytes8", [8]byte{}, ""},
+ {"bytes7", [7]byte{}, ""},
+ {"bytes6", [6]byte{}, ""},
+ {"bytes5", [5]byte{}, ""},
+ {"bytes4", [4]byte{}, ""},
+ {"bytes3", [3]byte{}, ""},
+ {"bytes2", [2]byte{}, ""},
+ {"bytes1", [1]byte{}, ""},
{"bytes32", [33]byte{}, "abi: cannot use [33]uint8 as type [32]uint8 as argument"},
{"bytes32", common.Hash{1}, ""},
- {"bytes31", [31]byte{}, ""},
+ {"bytes31", common.Hash{1}, "abi: cannot use common.Hash as type [31]uint8 as argument"},
{"bytes31", [32]byte{}, "abi: cannot use [32]uint8 as type [31]uint8 as argument"},
{"bytes", []byte{0, 1}, ""},
- {"bytes", [2]byte{0, 1}, ""},
- {"bytes", common.Hash{1}, ""},
+ {"bytes", [2]byte{0, 1}, "abi: cannot use array as type slice as argument"},
+ {"bytes", common.Hash{1}, "abi: cannot use array as type slice as argument"},
{"string", "hello world", ""},
+ {"string", string(""), ""},
+ {"string", []byte{}, "abi: cannot use slice as type string as argument"},
{"bytes32[]", [][32]byte{{}}, ""},
{"function", [24]byte{}, ""},
+ {"bytes20", common.Address{}, ""},
+ {"address", [20]byte{}, ""},
+ {"address", common.Address{}, ""},
} {
typ, err := NewType(test.typ)
- if err != nil {
+ if err != nil && len(test.err) == 0 {
t.Fatal("unexpected parse error:", err)
+ } else if err != nil && len(test.err) != 0 {
+ if err.Error() != test.err {
+ t.Errorf("%d failed. Expected err: '%v' got err: '%v'", i, test.err, err)
+ }
+ continue
}
err = typeCheck(typ, reflect.ValueOf(test.input))
diff --git a/accounts/abi/unpack.go b/accounts/abi/unpack.go
index fc41c88ac..ffa14ccd3 100644
--- a/accounts/abi/unpack.go
+++ b/accounts/abi/unpack.go
@@ -25,118 +25,16 @@ import (
"github.com/ethereum/go-ethereum/common"
)
-// toGoSliceType parses the input and casts it to the proper slice defined by the ABI
-// argument in T.
-func toGoSlice(i int, t Argument, output []byte) (interface{}, error) {
- index := i * 32
- // The slice must, at very least be large enough for the index+32 which is exactly the size required
- // for the [offset in output, size of offset].
- if index+32 > len(output) {
- return nil, fmt.Errorf("abi: cannot marshal in to go slice: insufficient size output %d require %d", len(output), index+32)
- }
- elem := t.Type.Elem
-
- // first we need to create a slice of the type
- var refSlice reflect.Value
- switch elem.T {
- case IntTy, UintTy, BoolTy:
- // create a new reference slice matching the element type
- switch t.Type.Kind {
- case reflect.Bool:
- refSlice = reflect.ValueOf([]bool(nil))
- case reflect.Uint8:
- refSlice = reflect.ValueOf([]uint8(nil))
- case reflect.Uint16:
- refSlice = reflect.ValueOf([]uint16(nil))
- case reflect.Uint32:
- refSlice = reflect.ValueOf([]uint32(nil))
- case reflect.Uint64:
- refSlice = reflect.ValueOf([]uint64(nil))
- case reflect.Int8:
- refSlice = reflect.ValueOf([]int8(nil))
- case reflect.Int16:
- refSlice = reflect.ValueOf([]int16(nil))
- case reflect.Int32:
- refSlice = reflect.ValueOf([]int32(nil))
- case reflect.Int64:
- refSlice = reflect.ValueOf([]int64(nil))
- default:
- refSlice = reflect.ValueOf([]*big.Int(nil))
- }
- case AddressTy: // address must be of slice Address
- refSlice = reflect.ValueOf([]common.Address(nil))
- case HashTy: // hash must be of slice hash
- refSlice = reflect.ValueOf([]common.Hash(nil))
- case FixedBytesTy:
- refSlice = reflect.ValueOf([][]byte(nil))
- default: // no other types are supported
- return nil, fmt.Errorf("abi: unsupported slice type %v", elem.T)
- }
-
- var slice []byte
- var size int
- var offset int
- if t.Type.IsSlice {
- // get the offset which determines the start of this array ...
- offset = int(binary.BigEndian.Uint64(output[index+24 : index+32]))
- if offset+32 > len(output) {
- return nil, fmt.Errorf("abi: cannot marshal in to go slice: offset %d would go over slice boundary (len=%d)", len(output), offset+32)
- }
-
- slice = output[offset:]
- // ... starting with the size of the array in elements ...
- size = int(binary.BigEndian.Uint64(slice[24:32]))
- slice = slice[32:]
- // ... and make sure that we've at the very least the amount of bytes
- // available in the buffer.
- if size*32 > len(slice) {
- return nil, fmt.Errorf("abi: cannot marshal in to go slice: insufficient size output %d require %d", len(output), offset+32+size*32)
- }
-
- // reslice to match the required size
- slice = slice[:size*32]
- } else if t.Type.IsArray {
- //get the number of elements in the array
- size = t.Type.SliceSize
-
- //check to make sure array size matches up
- if index+32*size > len(output) {
- return nil, fmt.Errorf("abi: cannot marshal in to go array: offset %d would go over slice boundary (len=%d)", len(output), index+32*size)
- }
- //slice is there for a fixed amount of times
- slice = output[index : index+size*32]
- }
-
- for i := 0; i < size; i++ {
- var (
- inter interface{} // interface type
- returnOutput = slice[i*32 : i*32+32] // the return output
- err error
- )
- // set inter to the correct type (cast)
- switch elem.T {
- case IntTy, UintTy:
- inter = readInteger(t.Type.Kind, returnOutput)
- case BoolTy:
- inter, err = readBool(returnOutput)
- if err != nil {
- return nil, err
- }
- case AddressTy:
- inter = common.BytesToAddress(returnOutput)
- case HashTy:
- inter = common.BytesToHash(returnOutput)
- case FixedBytesTy:
- inter = returnOutput
- }
- // append the item to our reflect slice
- refSlice = reflect.Append(refSlice, reflect.ValueOf(inter))
- }
-
- // return the interface
- return refSlice.Interface(), nil
+// unpacker is a utility interface that enables us to have
+// abstraction between events and methods and also to properly
+// "unpack" them; e.g. events use Inputs, methods use Outputs.
+type unpacker interface {
+ tupleUnpack(v interface{}, output []byte) error
+ singleUnpack(v interface{}, output []byte) error
+ isTupleReturn() bool
}
+// reads the integer based on its kind
func readInteger(kind reflect.Kind, b []byte) interface{} {
switch kind {
case reflect.Uint8:
@@ -160,13 +58,10 @@ func readInteger(kind reflect.Kind, b []byte) interface{} {
}
}
+// reads a bool
func readBool(word []byte) (bool, error) {
- if len(word) != 32 {
- return false, fmt.Errorf("abi: fatal error: incorrect word length")
- }
-
- for i, b := range word {
- if b != 0 && i != 31 {
+ for _, b := range word[:31] {
+ if b != 0 {
return false, errBadBool
}
}
@@ -178,58 +73,144 @@ func readBool(word []byte) (bool, error) {
default:
return false, errBadBool
}
+}
+// A function type is simply the address with the function selection signature at the end.
+// This enforces that standard by always presenting it as a 24-array (address + sig = 24 bytes)
+func readFunctionType(t Type, word []byte) (funcTy [24]byte, err error) {
+ if t.T != FunctionTy {
+ return [24]byte{}, fmt.Errorf("abi: invalid type in call to make function type byte array.")
+ }
+ if garbage := binary.BigEndian.Uint64(word[24:32]); garbage != 0 {
+ err = fmt.Errorf("abi: got improperly encoded function type, got %v", word)
+ } else {
+ copy(funcTy[:], word[0:24])
+ }
+ return
}
-// toGoType parses the input and casts it to the proper type defined by the ABI
-// argument in T.
-func toGoType(i int, t Argument, output []byte) (interface{}, error) {
- // we need to treat slices differently
- if (t.Type.IsSlice || t.Type.IsArray) && t.Type.T != BytesTy && t.Type.T != StringTy && t.Type.T != FixedBytesTy && t.Type.T != FunctionTy {
- return toGoSlice(i, t, output)
+// through reflection, creates a fixed array to be read from
+func readFixedBytes(t Type, word []byte) (interface{}, error) {
+ if t.T != FixedBytesTy {
+ return nil, fmt.Errorf("abi: invalid type in call to make fixed byte array.")
}
+ // convert
+ array := reflect.New(t.Type).Elem()
- index := i * 32
- if index+32 > len(output) {
- return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), index+32)
+ reflect.Copy(array, reflect.ValueOf(word[0:t.Size]))
+ return array.Interface(), nil
+
+}
+
+// iteratively unpack elements
+func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error) {
+ if start+32*size > len(output) {
+ return nil, fmt.Errorf("abi: cannot marshal in to go array: offset %d would go over slice boundary (len=%d)", len(output), start+32*size)
}
- // Parse the given index output and check whether we need to read
- // a different offset and length based on the type (i.e. string, bytes)
- var returnOutput []byte
- switch t.Type.T {
- case StringTy, BytesTy: // variable arrays are written at the end of the return bytes
- // parse offset from which we should start reading
- offset := int(binary.BigEndian.Uint64(output[index+24 : index+32]))
- if offset+32 > len(output) {
- return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), offset+32)
+ // this value will become our slice or our array, depending on the type
+ var refSlice reflect.Value
+ slice := output[start : start+size*32]
+
+ if t.T == SliceTy {
+ // declare our slice
+ refSlice = reflect.MakeSlice(t.Type, size, size)
+ } else if t.T == ArrayTy {
+ // declare our array
+ refSlice = reflect.New(t.Type).Elem()
+ } else {
+ return nil, fmt.Errorf("abi: invalid type in array/slice unpacking stage")
+ }
+
+ for i, j := start, 0; j*32 < len(slice); i, j = i+32, j+1 {
+ // this corrects the arrangement so that we get all the underlying array values
+ if t.Elem.T == ArrayTy && j != 0 {
+ i = start + t.Elem.Size*32*j
}
- // parse the size up until we should be reading
- size := int(binary.BigEndian.Uint64(output[offset+24 : offset+32]))
- if offset+32+size > len(output) {
- return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), offset+32+size)
+ inter, err := toGoType(i, *t.Elem, output)
+ if err != nil {
+ return nil, err
}
+ // append the item to our reflect slice
+ refSlice.Index(j).Set(reflect.ValueOf(inter))
+ }
- // get the bytes for this return value
- returnOutput = output[offset+32 : offset+32+size]
- default:
+ // return the interface
+ return refSlice.Interface(), nil
+}
+
+// toGoType parses the output bytes and recursively assigns the value of these bytes
+// into a go type with accordance with the ABI spec.
+func toGoType(index int, t Type, output []byte) (interface{}, error) {
+ if index+32 > len(output) {
+ return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), index+32)
+ }
+
+ var (
+ returnOutput []byte
+ begin, end int
+ err error
+ )
+
+ // if we require a length prefix, find the beginning word and size returned.
+ if t.requiresLengthPrefix() {
+ begin, end, err = lengthPrefixPointsTo(index, output)
+ if err != nil {
+ return nil, err
+ }
+ } else {
returnOutput = output[index : index+32]
}
- // convert the bytes to whatever is specified by the ABI.
- switch t.Type.T {
+ switch t.T {
+ case SliceTy:
+ return forEachUnpack(t, output, begin, end)
+ case ArrayTy:
+ return forEachUnpack(t, output, index, t.Size)
+ case StringTy: // variable arrays are written at the end of the return bytes
+ return string(output[begin : begin+end]), nil
case IntTy, UintTy:
- return readInteger(t.Type.Kind, returnOutput), nil
+ return readInteger(t.Kind, returnOutput), nil
case BoolTy:
return readBool(returnOutput)
case AddressTy:
return common.BytesToAddress(returnOutput), nil
case HashTy:
return common.BytesToHash(returnOutput), nil
- case BytesTy, FixedBytesTy, FunctionTy:
- return returnOutput, nil
- case StringTy:
- return string(returnOutput), nil
+ case BytesTy:
+ return output[begin : begin+end], nil
+ case FixedBytesTy:
+ return readFixedBytes(t, returnOutput)
+ case FunctionTy:
+ return readFunctionType(t, returnOutput)
+ default:
+ return nil, fmt.Errorf("abi: unknown type %v", t.T)
+ }
+}
+
+// interprets a 32 byte slice as an offset and then determines which indice to look to decode the type.
+func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err error) {
+ offset := int(binary.BigEndian.Uint64(output[index+24 : index+32]))
+ if offset+32 > len(output) {
+ return 0, 0, fmt.Errorf("abi: cannot marshal in to go slice: offset %d would go over slice boundary (len=%d)", len(output), offset+32)
+ }
+ length = int(binary.BigEndian.Uint64(output[offset+24 : offset+32]))
+ if offset+32+length > len(output) {
+ return 0, 0, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), offset+32+length)
+ }
+ start = offset + 32
+
+ //fmt.Printf("LENGTH PREFIX INFO: \nsize: %v\noffset: %v\nstart: %v\n", length, offset, start)
+ return
+}
+
+// checks for proper formatting of byte output
+func bytesAreProper(output []byte) error {
+ if len(output) == 0 {
+ return fmt.Errorf("abi: unmarshalling empty output")
+ } else if len(output)%32 != 0 {
+ return fmt.Errorf("abi: improperly formatted output")
+ } else {
+ return nil
}
- return nil, fmt.Errorf("abi: unknown type %v", t.Type.T)
}
diff --git a/accounts/abi/unpack_test.go b/accounts/abi/unpack_test.go
index 8e3afee4e..9c7c339f3 100644
--- a/accounts/abi/unpack_test.go
+++ b/accounts/abi/unpack_test.go
@@ -18,6 +18,7 @@ package abi
import (
"bytes"
+ "encoding/hex"
"fmt"
"math/big"
"reflect"
@@ -27,263 +28,261 @@ import (
"github.com/ethereum/go-ethereum/common"
)
-func TestSimpleMethodUnpack(t *testing.T) {
- for i, test := range []struct {
- def string // definition of the **output** ABI params
- marshalledOutput []byte // evm return data
- expectedOut interface{} // the expected output
- outVar string // the output variable (e.g. uint32, *big.Int, etc)
- err string // empty or error if expected
- }{
- {
- `[ { "type": "bool" } ]`,
- common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
- bool(true),
- "bool",
- "",
- },
- {
- `[ { "type": "uint32" } ]`,
- common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
- uint32(1),
- "uint32",
- "",
- },
- {
- `[ { "type": "uint32" } ]`,
- common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
- nil,
- "uint16",
- "abi: cannot unmarshal uint32 in to uint16",
- },
- {
- `[ { "type": "uint17" } ]`,
- common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
- nil,
- "uint16",
- "abi: cannot unmarshal *big.Int in to uint16",
- },
- {
- `[ { "type": "uint17" } ]`,
- common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
- big.NewInt(1),
- "*big.Int",
- "",
- },
-
- {
- `[ { "type": "int32" } ]`,
- common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
- int32(1),
- "int32",
- "",
- },
- {
- `[ { "type": "int32" } ]`,
- common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
- nil,
- "int16",
- "abi: cannot unmarshal int32 in to int16",
- },
- {
- `[ { "type": "int17" } ]`,
- common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
- nil,
- "int16",
- "abi: cannot unmarshal *big.Int in to int16",
- },
- {
- `[ { "type": "int17" } ]`,
- common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
- big.NewInt(1),
- "*big.Int",
- "",
- },
-
- {
- `[ { "type": "address" } ]`,
- common.Hex2Bytes("0000000000000000000000000100000000000000000000000000000000000000"),
- common.Address{1},
- "address",
- "",
- },
- {
- `[ { "type": "bytes32" } ]`,
- common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
- common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
- "bytes",
- "",
- },
- {
- `[ { "type": "bytes32" } ]`,
- common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
- common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
- "hash",
- "",
- },
- {
- `[ { "type": "bytes32" } ]`,
- common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
- common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
- "interface",
- "",
- },
- {
- `[ { "type": "function" } ]`,
- common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
- [24]byte{1},
- "function",
- "",
- },
- } {
- abiDefinition := fmt.Sprintf(`[{ "name" : "method", "outputs": %s}]`, test.def)
- abi, err := JSON(strings.NewReader(abiDefinition))
- if err != nil {
- t.Errorf("%d failed. %v", i, err)
- continue
- }
+type unpackTest struct {
+ def string // ABI definition JSON
+ enc string // evm return data
+ want interface{} // the expected output
+ err string // empty or error if expected
+}
- var outvar interface{}
- switch test.outVar {
- case "bool":
- var v bool
- err = abi.Unpack(&v, "method", test.marshalledOutput)
- outvar = v
- case "uint8":
- var v uint8
- err = abi.Unpack(&v, "method", test.marshalledOutput)
- outvar = v
- case "uint16":
- var v uint16
- err = abi.Unpack(&v, "method", test.marshalledOutput)
- outvar = v
- case "uint32":
- var v uint32
- err = abi.Unpack(&v, "method", test.marshalledOutput)
- outvar = v
- case "uint64":
- var v uint64
- err = abi.Unpack(&v, "method", test.marshalledOutput)
- outvar = v
- case "int8":
- var v int8
- err = abi.Unpack(&v, "method", test.marshalledOutput)
- outvar = v
- case "int16":
- var v int16
- err = abi.Unpack(&v, "method", test.marshalledOutput)
- outvar = v
- case "int32":
- var v int32
- err = abi.Unpack(&v, "method", test.marshalledOutput)
- outvar = v
- case "int64":
- var v int64
- err = abi.Unpack(&v, "method", test.marshalledOutput)
- outvar = v
- case "*big.Int":
- var v *big.Int
- err = abi.Unpack(&v, "method", test.marshalledOutput)
- outvar = v
- case "address":
- var v common.Address
- err = abi.Unpack(&v, "method", test.marshalledOutput)
- outvar = v
- case "bytes":
- var v []byte
- err = abi.Unpack(&v, "method", test.marshalledOutput)
- outvar = v
- case "hash":
- var v common.Hash
- err = abi.Unpack(&v, "method", test.marshalledOutput)
- outvar = v.Bytes()[:]
- case "function":
- var v [24]byte
- err = abi.Unpack(&v, "method", test.marshalledOutput)
- outvar = v
- case "interface":
- err = abi.Unpack(&outvar, "method", test.marshalledOutput)
- default:
- t.Errorf("unsupported type '%v' please add it to the switch statement in this test", test.outVar)
- continue
+func (test unpackTest) checkError(err error) error {
+ if err != nil {
+ if len(test.err) == 0 {
+ return fmt.Errorf("expected no err but got: %v", err)
+ } else if err.Error() != test.err {
+ return fmt.Errorf("expected err: '%v' got err: %q", test.err, err)
}
+ } else if len(test.err) > 0 {
+ return fmt.Errorf("expected err: %v but got none", test.err)
+ }
+ return nil
+}
- if err != nil && len(test.err) == 0 {
- t.Errorf("%d failed. Expected no err but got: %v", i, err)
- continue
+var unpackTests = []unpackTest{
+ {
+ def: `[{ "type": "bool" }]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000001",
+ want: true,
+ },
+ {
+ def: `[{"type": "uint32"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000001",
+ want: uint32(1),
+ },
+ {
+ def: `[{"type": "uint32"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000001",
+ want: uint16(0),
+ err: "abi: cannot unmarshal uint32 in to uint16",
+ },
+ {
+ def: `[{"type": "uint17"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000001",
+ want: uint16(0),
+ err: "abi: cannot unmarshal *big.Int in to uint16",
+ },
+ {
+ def: `[{"type": "uint17"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000001",
+ want: big.NewInt(1),
+ },
+ {
+ def: `[{"type": "int32"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000001",
+ want: int32(1),
+ },
+ {
+ def: `[{"type": "int32"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000001",
+ want: int16(0),
+ err: "abi: cannot unmarshal int32 in to int16",
+ },
+ {
+ def: `[{"type": "int17"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000001",
+ want: int16(0),
+ err: "abi: cannot unmarshal *big.Int in to int16",
+ },
+ {
+ def: `[{"type": "int17"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000001",
+ want: big.NewInt(1),
+ },
+ {
+ def: `[{"type": "address"}]`,
+ enc: "0000000000000000000000000100000000000000000000000000000000000000",
+ want: common.Address{1},
+ },
+ {
+ def: `[{"type": "bytes32"}]`,
+ enc: "0100000000000000000000000000000000000000000000000000000000000000",
+ want: [32]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ {
+ def: `[{"type": "bytes"}]`,
+ enc: "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200100000000000000000000000000000000000000000000000000000000000000",
+ want: common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
+ },
+ {
+ def: `[{"type": "bytes"}]`,
+ enc: "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200100000000000000000000000000000000000000000000000000000000000000",
+ want: [32]byte{},
+ err: "abi: cannot unmarshal []uint8 in to [32]uint8",
+ },
+ {
+ def: `[{"type": "bytes32"}]`,
+ enc: "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200100000000000000000000000000000000000000000000000000000000000000",
+ want: []byte(nil),
+ err: "abi: cannot unmarshal [32]uint8 in to []uint8",
+ },
+ {
+ def: `[{"type": "bytes32"}]`,
+ enc: "0100000000000000000000000000000000000000000000000000000000000000",
+ want: common.HexToHash("0100000000000000000000000000000000000000000000000000000000000000"),
+ },
+ {
+ def: `[{"type": "function"}]`,
+ enc: "0100000000000000000000000000000000000000000000000000000000000000",
+ want: [24]byte{1},
+ },
+ // slices
+ {
+ def: `[{"type": "uint8[]"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: []uint8{1, 2},
+ },
+ {
+ def: `[{"type": "uint8[2]"}]`,
+ enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: [2]uint8{1, 2},
+ },
+ // multi dimensional, if these pass, all types that don't require length prefix should pass
+ {
+ def: `[{"type": "uint8[][]"}]`,
+ enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000E0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: [][]uint8{{1, 2}, {1, 2}},
+ },
+ {
+ def: `[{"type": "uint8[2][2]"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: [2][2]uint8{{1, 2}, {1, 2}},
+ },
+ {
+ def: `[{"type": "uint8[][2]"}]`,
+ enc: "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001",
+ want: [2][]uint8{{1}, {1}},
+ },
+ {
+ def: `[{"type": "uint8[2][]"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: [][2]uint8{{1, 2}},
+ },
+ {
+ def: `[{"type": "uint16[]"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: []uint16{1, 2},
+ },
+ {
+ def: `[{"type": "uint16[2]"}]`,
+ enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: [2]uint16{1, 2},
+ },
+ {
+ def: `[{"type": "uint32[]"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: []uint32{1, 2},
+ },
+ {
+ def: `[{"type": "uint32[2]"}]`,
+ enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: [2]uint32{1, 2},
+ },
+ {
+ def: `[{"type": "uint64[]"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: []uint64{1, 2},
+ },
+ {
+ def: `[{"type": "uint64[2]"}]`,
+ enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: [2]uint64{1, 2},
+ },
+ {
+ def: `[{"type": "uint256[]"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: []*big.Int{big.NewInt(1), big.NewInt(2)},
+ },
+ {
+ def: `[{"type": "uint256[3]"}]`,
+ enc: "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003",
+ want: [3]*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)},
+ },
+ {
+ def: `[{"type": "int8[]"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: []int8{1, 2},
+ },
+ {
+ def: `[{"type": "int8[2]"}]`,
+ enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: [2]int8{1, 2},
+ },
+ {
+ def: `[{"type": "int16[]"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: []int16{1, 2},
+ },
+ {
+ def: `[{"type": "int16[2]"}]`,
+ enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: [2]int16{1, 2},
+ },
+ {
+ def: `[{"type": "int32[]"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: []int32{1, 2},
+ },
+ {
+ def: `[{"type": "int32[2]"}]`,
+ enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: [2]int32{1, 2},
+ },
+ {
+ def: `[{"type": "int64[]"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: []int64{1, 2},
+ },
+ {
+ def: `[{"type": "int64[2]"}]`,
+ enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: [2]int64{1, 2},
+ },
+ {
+ def: `[{"type": "int256[]"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: []*big.Int{big.NewInt(1), big.NewInt(2)},
+ },
+ {
+ def: `[{"type": "int256[3]"}]`,
+ enc: "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003",
+ want: [3]*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)},
+ },
+}
+
+func TestUnpack(t *testing.T) {
+ for i, test := range unpackTests {
+ def := fmt.Sprintf(`[{ "name" : "method", "outputs": %s}]`, test.def)
+ abi, err := JSON(strings.NewReader(def))
+ if err != nil {
+ t.Fatalf("invalid ABI definition %s: %v", def, err)
}
- if err == nil && len(test.err) != 0 {
- t.Errorf("%d failed. Expected err: %v but got none", i, test.err)
- continue
+ encb, err := hex.DecodeString(test.enc)
+ if err != nil {
+ t.Fatalf("invalid hex: %s" + test.enc)
}
- if err != nil && len(test.err) != 0 && err.Error() != test.err {
- t.Errorf("%d failed. Expected err: '%v' got err: '%v'", i, test.err, err)
+ outptr := reflect.New(reflect.TypeOf(test.want))
+ err = abi.Unpack(outptr.Interface(), "method", encb)
+ if err := test.checkError(err); err != nil {
+ t.Errorf("test %d (%v) failed: %v", i, test.def, err)
continue
}
-
- if err == nil {
- if !reflect.DeepEqual(test.expectedOut, outvar) {
- t.Errorf("%d failed. Output error: expected %v, got %v", i, test.expectedOut, outvar)
- }
+ out := outptr.Elem().Interface()
+ if !reflect.DeepEqual(test.want, out) {
+ t.Errorf("test %d (%v) failed: expected %v, got %v", i, test.def, test.want, out)
}
}
}
-func TestUnpackSetInterfaceSlice(t *testing.T) {
- var (
- var1 = new(uint8)
- var2 = new(uint8)
- )
- out := []interface{}{var1, var2}
- abi, err := JSON(strings.NewReader(`[{"type":"function", "name":"ints", "outputs":[{"type":"uint8"}, {"type":"uint8"}]}]`))
- if err != nil {
- t.Fatal(err)
- }
- marshalledReturn := append(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")...)
- err = abi.Unpack(&out, "ints", marshalledReturn)
- if err != nil {
- t.Fatal(err)
- }
- if *var1 != 1 {
- t.Error("expected var1 to be 1, got", *var1)
- }
- if *var2 != 2 {
- t.Error("expected var2 to be 2, got", *var2)
- }
-
- out = []interface{}{var1}
- err = abi.Unpack(&out, "ints", marshalledReturn)
-
- expErr := "abi: cannot marshal in to slices of unequal size (require: 2, got: 1)"
- if err == nil || err.Error() != expErr {
- t.Error("expected err:", expErr, "Got:", err)
- }
-}
-
-func TestUnpackSetInterfaceArrayOutput(t *testing.T) {
- var (
- var1 = new([1]uint32)
- var2 = new([1]uint32)
- )
- out := []interface{}{var1, var2}
- abi, err := JSON(strings.NewReader(`[{"type":"function", "name":"ints", "outputs":[{"type":"uint32[1]"}, {"type":"uint32[1]"}]}]`))
- if err != nil {
- t.Fatal(err)
- }
- marshalledReturn := append(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")...)
- err = abi.Unpack(&out, "ints", marshalledReturn)
- if err != nil {
- t.Fatal(err)
- }
-
- if *var1 != [1]uint32{1} {
- t.Error("expected var1 to be [1], got", *var1)
- }
- if *var2 != [1]uint32{2} {
- t.Error("expected var2 to be [2], got", *var2)
- }
-}
-
func TestMultiReturnWithStruct(t *testing.T) {
const definition = `[
{ "name" : "multi", "constant" : false, "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]`
@@ -337,101 +336,6 @@ func TestMultiReturnWithStruct(t *testing.T) {
}
}
-func TestMultiReturnWithSlice(t *testing.T) {
- const definition = `[
- { "name" : "multi", "constant" : false, "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]`
-
- abi, err := JSON(strings.NewReader(definition))
- if err != nil {
- t.Fatal(err)
- }
-
- // using buff to make the code readable
- buff := new(bytes.Buffer)
- buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
- buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040"))
- buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000005"))
- stringOut := "hello"
- buff.Write(common.RightPadBytes([]byte(stringOut), 32))
-
- var inter []interface{}
- err = abi.Unpack(&inter, "multi", buff.Bytes())
- if err != nil {
- t.Error(err)
- }
-
- if len(inter) != 2 {
- t.Fatal("expected 2 results got", len(inter))
- }
-
- if num, ok := inter[0].(*big.Int); !ok || num.Cmp(big.NewInt(1)) != 0 {
- t.Error("expected index 0 to be 1 got", num)
- }
-
- if str, ok := inter[1].(string); !ok || str != stringOut {
- t.Error("expected index 1 to be", stringOut, "got", str)
- }
-}
-
-func TestMarshalArrays(t *testing.T) {
- const definition = `[
- { "name" : "bytes32", "constant" : false, "outputs": [ { "type": "bytes32" } ] },
- { "name" : "bytes10", "constant" : false, "outputs": [ { "type": "bytes10" } ] }
- ]`
-
- abi, err := JSON(strings.NewReader(definition))
- if err != nil {
- t.Fatal(err)
- }
-
- output := common.LeftPadBytes([]byte{1}, 32)
-
- var bytes10 [10]byte
- err = abi.Unpack(&bytes10, "bytes32", output)
- if err == nil || err.Error() != "abi: cannot unmarshal src (len=32) in to dst (len=10)" {
- t.Error("expected error or bytes32 not be assignable to bytes10:", err)
- }
-
- var bytes32 [32]byte
- err = abi.Unpack(&bytes32, "bytes32", output)
- if err != nil {
- t.Error("didn't expect error:", err)
- }
- if !bytes.Equal(bytes32[:], output) {
- t.Error("expected bytes32[31] to be 1 got", bytes32[31])
- }
-
- type (
- B10 [10]byte
- B32 [32]byte
- )
-
- var b10 B10
- err = abi.Unpack(&b10, "bytes32", output)
- if err == nil || err.Error() != "abi: cannot unmarshal src (len=32) in to dst (len=10)" {
- t.Error("expected error or bytes32 not be assignable to bytes10:", err)
- }
-
- var b32 B32
- err = abi.Unpack(&b32, "bytes32", output)
- if err != nil {
- t.Error("didn't expect error:", err)
- }
- if !bytes.Equal(b32[:], output) {
- t.Error("expected bytes32[31] to be 1 got", bytes32[31])
- }
-
- output[10] = 1
- var shortAssignLong [32]byte
- err = abi.Unpack(&shortAssignLong, "bytes10", output)
- if err != nil {
- t.Error("didn't expect error:", err)
- }
- if !bytes.Equal(output, shortAssignLong[:]) {
- t.Errorf("expected %x to be %x", shortAssignLong, output)
- }
-}
-
func TestUnmarshal(t *testing.T) {
const definition = `[
{ "name" : "int", "constant" : false, "outputs": [ { "type": "uint256" } ] },
@@ -450,6 +354,29 @@ func TestUnmarshal(t *testing.T) {
}
buff := new(bytes.Buffer)
+ // marshall mixed bytes (mixedBytes)
+ p0, p0Exp := []byte{}, common.Hex2Bytes("01020000000000000000")
+ p1, p1Exp := [32]byte{}, common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000ddeeff")
+ mixedBytes := []interface{}{&p0, &p1}
+
+ buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040"))
+ buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000ddeeff"))
+ buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000a"))
+ buff.Write(common.Hex2Bytes("0102000000000000000000000000000000000000000000000000000000000000"))
+
+ err = abi.Unpack(&mixedBytes, "mixedBytes", buff.Bytes())
+ if err !=nil {
+ t.Error(err)
+ } else {
+ if bytes.Compare(p0, p0Exp) != 0 {
+ t.Errorf("unexpected value unpacked: want %x, got %x", p0Exp, p0)
+ }
+
+ if bytes.Compare(p1[:], p1Exp) != 0 {
+ t.Errorf("unexpected value unpacked: want %x, got %x", p1Exp, p1)
+ }
+ }
+
// marshal int
var Int *big.Int
err = abi.Unpack(&Int, "int", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
@@ -473,6 +400,7 @@ func TestUnmarshal(t *testing.T) {
}
// marshal dynamic bytes max length 32
+ buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
bytesOut := common.RightPadBytes([]byte("hello"), 32)
@@ -504,11 +432,11 @@ func TestUnmarshal(t *testing.T) {
t.Errorf("expected %x got %x", bytesOut, Bytes)
}
- // marshall dynamic bytes max length 63
+ // marshall dynamic bytes max length 64
buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000003f"))
- bytesOut = common.RightPadBytes([]byte("hello"), 63)
+ bytesOut = common.RightPadBytes([]byte("hello"), 64)
buff.Write(bytesOut)
err = abi.Unpack(&Bytes, "bytes", buff.Bytes())
@@ -516,8 +444,8 @@ func TestUnmarshal(t *testing.T) {
t.Error(err)
}
- if !bytes.Equal(Bytes, bytesOut) {
- t.Errorf("expected %x got %x", bytesOut, Bytes)
+ if !bytes.Equal(Bytes, bytesOut[:len(bytesOut)-1]) {
+ t.Errorf("expected %x got %x", bytesOut[:len(bytesOut)-1], Bytes)
}
// marshal dynamic bytes output empty
@@ -569,29 +497,6 @@ func TestUnmarshal(t *testing.T) {
t.Error("expected error")
}
- // marshal mixed bytes
- buff.Reset()
- buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040"))
- fixed := common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")
- buff.Write(fixed)
- buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
- bytesOut = common.RightPadBytes([]byte("hello"), 32)
- buff.Write(bytesOut)
-
- var out []interface{}
- err = abi.Unpack(&out, "mixedBytes", buff.Bytes())
- if err != nil {
- t.Fatal("didn't expect error:", err)
- }
-
- if !bytes.Equal(bytesOut, out[0].([]byte)) {
- t.Errorf("expected %x, got %x", bytesOut, out[0])
- }
-
- if !bytes.Equal(fixed, out[1].([]byte)) {
- t.Errorf("expected %x, got %x", fixed, out[1])
- }
-
buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"))