mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-17 01:13:45 +00:00
accounts/abi: redo event and method specific unpacking
Signed-off-by: RJ Catalano <rj@monax.io>
This commit is contained in:
parent
2609e0a827
commit
7c26a13cb7
3 changed files with 146 additions and 85 deletions
|
|
@ -21,7 +21,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
)
|
)
|
||||||
|
|
@ -86,96 +85,26 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Unpack output in v according to the abi specification
|
// Unpack output in v according to the abi specification
|
||||||
func (abi ABI) Unpack(v interface{}, name string, output []byte) error {
|
func (abi ABI) Unpack(v interface{}, name string, output []byte) (err error) {
|
||||||
var method = abi.Methods[name]
|
|
||||||
|
|
||||||
if len(output) == 0 {
|
if err = bytesAreProper(output); err != nil {
|
||||||
return fmt.Errorf("abi: unmarshalling empty output")
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure the passed value is a pointer
|
var unpack unpacker
|
||||||
valueOf := reflect.ValueOf(v)
|
if method, ok := abi.Methods[name]; ok {
|
||||||
if reflect.Ptr != valueOf.Kind() {
|
unpack = method
|
||||||
return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
|
} else if event, ok := abi.Events[name]; ok {
|
||||||
}
|
unpack = event
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
marshalledValue, err := toGoType(0, method.Outputs[0], output)
|
return fmt.Errorf("abi: could not locate named method or event.")
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := set(value, reflect.ValueOf(marshalledValue), method.Outputs[0]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
if unpack.tupleReturn() {
|
||||||
|
return unpack.tupleUnpack(v, output)
|
||||||
|
}
|
||||||
|
|
||||||
|
return unpack.singleUnpack(v, output)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (abi *ABI) UnmarshalJSON(data []byte) error {
|
func (abi *ABI) UnmarshalJSON(data []byte) error {
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package abi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -44,3 +45,72 @@ 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, ",")))))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,68 @@ func (method Method) pack(args ...interface{}) ([]byte, error) {
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
)
|
||||||
|
|
||||||
|
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]
|
||||||
|
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)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (method Method) tupleReturn() bool { return len(method.Outputs) > 1 }
|
||||||
|
|
||||||
|
func (method Method) singleUnpack(v interface{}, output []byte) error {
|
||||||
|
// make sure the passed value is a pointer
|
||||||
|
valueOf := reflect.ValueOf(v)
|
||||||
|
if reflect.Ptr != valueOf.Kind() {
|
||||||
|
return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
value := valueOf.Elem()
|
||||||
|
|
||||||
|
marshalledValue, err := toGoType(0, method.Outputs[0].Type, output)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := set(value, reflect.ValueOf(marshalledValue), method.Outputs[0]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Sig returns the methods string signature according to the ABI spec.
|
// Sig returns the methods string signature according to the ABI spec.
|
||||||
//
|
//
|
||||||
// Example
|
// Example
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue