accounts/abi: support unpacking into slice/array

This commit is contained in:
Bas van Kervel 2017-10-11 12:03:50 +02:00
parent f5e3630a79
commit b07ad265e5
No known key found for this signature in database
GPG key ID: BFB23B252EF5812B
4 changed files with 78 additions and 17 deletions

View file

@ -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, ","))))) 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 { func (e Event) tupleUnpack(v interface{}, output []byte) error {
// make sure the passed value is a pointer // make sure the passed value is a pointer
valueOf := reflect.ValueOf(v) valueOf := reflect.ValueOf(v)
@ -77,14 +80,32 @@ func (e Event) tupleUnpack(v interface{}, output []byte) error {
return err return err
} }
reflectValue := reflect.ValueOf(marshalledValue) reflectValue := reflect.ValueOf(marshalledValue)
for j := 0; j < typ.NumField(); j++ {
field := typ.Field(j) switch value.Kind() {
// TODO read tags: `abi:"fieldName"` case reflect.Struct:
if field.Name == strings.ToUpper(e.Inputs[i].Name[:1])+e.Inputs[i].Name[1:] { for j := 0; j < typ.NumField(); j++ {
if err := set(value.Field(j), reflectValue, e.Inputs[i]); err != nil { field := typ.Field(j)
return err // 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 return nil

View file

@ -78,6 +78,8 @@ func (method Method) pack(args ...interface{}) ([]byte, error) {
} }
// unpacks a method return tuple into a struct of corresponding go types // 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 { func (method Method) tupleUnpack(v interface{}, output []byte) error {
// make sure the passed value is a pointer // make sure the passed value is a pointer
valueOf := reflect.ValueOf(v) valueOf := reflect.ValueOf(v)
@ -90,10 +92,6 @@ func (method Method) tupleUnpack(v interface{}, output []byte) error {
typ = value.Type() typ = value.Type()
) )
if value.Kind() != reflect.Struct {
return fmt.Errorf("abi: cannot unmarshal tuple in to %v", typ)
}
j := 0 j := 0
for i := 0; i < len(method.Outputs); i++ { for i := 0; i < len(method.Outputs); i++ {
toUnpack := method.Outputs[i] toUnpack := method.Outputs[i]
@ -106,14 +104,32 @@ func (method Method) tupleUnpack(v interface{}, output []byte) error {
return err return err
} }
reflectValue := reflect.ValueOf(marshalledValue) reflectValue := reflect.ValueOf(marshalledValue)
for j := 0; j < typ.NumField(); j++ {
field := typ.Field(j) switch value.Kind() {
// TODO read tags: `abi:"fieldName"` case reflect.Struct:
if field.Name == strings.ToUpper(method.Outputs[i].Name[:1])+method.Outputs[i].Name[1:] { for j := 0; j < typ.NumField(); j++ {
if err := set(value.Field(j), reflectValue, method.Outputs[i]); err != nil { field := typ.Field(j)
return err // 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 return nil

View file

@ -354,6 +354,29 @@ func TestUnmarshal(t *testing.T) {
} }
buff := new(bytes.Buffer) 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 // marshal int
var Int *big.Int var Int *big.Int
err = abi.Unpack(&Int, "int", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) err = abi.Unpack(&Int, "int", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
@ -377,6 +400,7 @@ func TestUnmarshal(t *testing.T) {
} }
// marshal dynamic bytes max length 32 // marshal dynamic bytes max length 32
buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")) buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")) buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
bytesOut := common.RightPadBytes([]byte("hello"), 32) bytesOut := common.RightPadBytes([]byte("hello"), 32)