diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/bind/bind_test.go index 43ed53b922..17b2216426 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/event.go b/accounts/abi/event.go index 96374ba3c1..44ed7b8df2 100644 --- a/accounts/abi/event.go +++ b/accounts/abi/event.go @@ -46,6 +46,9 @@ 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) @@ -77,14 +80,32 @@ func (e Event) tupleUnpack(v interface{}, output []byte) error { 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(e.Inputs[i].Name[:1])+e.Inputs[i].Name[1:] { - if err := set(value.Field(j), reflectValue, e.Inputs[i]); err != nil { - return err + + 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 diff --git a/accounts/abi/method.go b/accounts/abi/method.go index a15a989170..d8838e9ed6 100644 --- a/accounts/abi/method.go +++ b/accounts/abi/method.go @@ -78,6 +78,8 @@ func (method Method) pack(args ...interface{}) ([]byte, error) { } // 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) @@ -90,10 +92,6 @@ func (method Method) tupleUnpack(v interface{}, output []byte) error { 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(method.Outputs); i++ { toUnpack := method.Outputs[i] @@ -106,14 +104,32 @@ func (method Method) tupleUnpack(v interface{}, output []byte) error { 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 + + 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 diff --git a/accounts/abi/unpack_test.go b/accounts/abi/unpack_test.go index b8236edf39..9c7c339f3f 100644 --- a/accounts/abi/unpack_test.go +++ b/accounts/abi/unpack_test.go @@ -354,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")) @@ -377,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)