From 1e72271f571f916691c5c18b8f0c4c5f7e0445c3 Mon Sep 17 00:00:00 2001 From: Fynn Date: Fri, 16 Feb 2018 11:46:25 +0100 Subject: [PATCH 1/2] accounts/abi: use unpackTuple to unpack event arguments Events with just 1 argument fail before this change --- accounts/abi/abi.go | 2 +- accounts/abi/abi_test.go | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go index abcb403db..65e69d064 100644 --- a/accounts/abi/abi.go +++ b/accounts/abi/abi.go @@ -86,7 +86,7 @@ func (abi ABI) Unpack(v interface{}, name string, output []byte) (err error) { } return method.Outputs.Unpack(v, output) } else if event, ok := abi.Events[name]; ok { - return event.Inputs.Unpack(v, output) + return event.Inputs.unpackTuple(v, output) } return fmt.Errorf("abi: could not locate named method or event") } diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go index 2d43b631c..325f33a82 100644 --- a/accounts/abi/abi_test.go +++ b/accounts/abi/abi_test.go @@ -621,14 +621,16 @@ func TestBareEvents(t *testing.T) { // TestUnpackEvent is based on this contract: // contract T { // event received(address sender, uint amount, bytes memo); +// event receivedAddr(address sender); // function receive(bytes memo) external payable { // received(msg.sender, msg.value, memo); +// receivedAddr(msg.sender); // } // } // When receive("X") is called with sender 0x00... and value 1, it produces this tx receipt: // receipt{status=1 cgas=23949 bloom=00000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000040200000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 logs=[log: b6818c8064f645cd82d99b59a1a267d6d61117ef [75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed] 000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158 9ae378b6d4409eada347a5dc0c180f186cb62dc68fcc0f043425eb917335aa28 0 95d429d309bb9d753954195fe2d69bd140b4ae731b9b5b605c34323de162cf00 0]} func TestUnpackEvent(t *testing.T) { - const abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]` + const abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]` abi, err := JSON(strings.NewReader(abiJSON)) if err != nil { t.Fatal(err) @@ -656,6 +658,17 @@ func TestUnpackEvent(t *testing.T) { } else { t.Logf("len(data): %d; received event: %+v", len(data), ev) } + + type ReceivedAddrEvent struct { + Address common.Address + } + var receivedAddrEv ReceivedAddrEvent + err = abi.Unpack(&receivedAddrEv, "receivedAddr", data) + if err != nil { + t.Error(err) + } else { + t.Logf("len(data): %d; received event: %+v", len(data), receivedAddrEv) + } } func TestABI_MethodById(t *testing.T) { From 60a999f238fca7c83b26ecf94257ee648afe4df9 Mon Sep 17 00:00:00 2001 From: Javier Peletier Date: Mon, 5 Mar 2018 16:01:40 +0100 Subject: [PATCH 2/2] accounts/abi: Modified unpackAtomic to accept struct lvalues --- accounts/abi/abi.go | 2 +- accounts/abi/argument.go | 50 +++++++++++++++++++++++++--------------- accounts/abi/reflect.go | 16 +++++++++++++ 3 files changed, 49 insertions(+), 19 deletions(-) diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go index da2ef9178..254b1f7fb 100644 --- a/accounts/abi/abi.go +++ b/accounts/abi/abi.go @@ -86,7 +86,7 @@ func (abi ABI) Unpack(v interface{}, name string, output []byte) (err error) { } return method.Outputs.Unpack(v, output) } else if event, ok := abi.Events[name]; ok { - return event.Inputs.unpackTuple(v, output) + return event.Inputs.Unpack(v, output) } return fmt.Errorf("abi: could not locate named method or event") } diff --git a/accounts/abi/argument.go b/accounts/abi/argument.go index 1b480da60..512d8fdfa 100644 --- a/accounts/abi/argument.go +++ b/accounts/abi/argument.go @@ -113,16 +113,8 @@ func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interfa } // If the output interface is a struct, make sure names don't collide if kind == reflect.Struct { - exists := make(map[string]bool) - for _, arg := range arguments { - field := capitalise(arg.Name) - if field == "" { - return fmt.Errorf("abi: purely underscored output cannot unpack to struct") - } - if exists[field] { - return fmt.Errorf("abi: multiple outputs mapping to the same struct field '%s'", field) - } - exists[field] = true + if err := requireUniqueStructFieldNames(arguments); err != nil { + return err } } for i, arg := range arguments.NonIndexed() { @@ -131,14 +123,9 @@ func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interfa switch kind { case reflect.Struct: - name := capitalise(arg.Name) - for j := 0; j < typ.NumField(); j++ { - // TODO read tags: `abi:"fieldName"` - if typ.Field(j).Name == name { - if err := set(value.Field(j), reflectValue, arg); err != nil { - return err - } - } + err := unpackStruct(value, reflectValue, arg) + if err != nil { + return err } case reflect.Slice, reflect.Array: if value.Len() < i { @@ -165,8 +152,20 @@ func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues []interf return fmt.Errorf("abi: wrong length, expected single value, got %d", len(marshalledValues)) } elem := reflect.ValueOf(v).Elem() + kind := elem.Kind() reflectValue := reflect.ValueOf(marshalledValues[0]) + + if kind == reflect.Struct { + //make sure names don't collide + if err := requireUniqueStructFieldNames(arguments); err != nil { + return err + } + + return unpackStruct(elem, reflectValue, arguments[0]) + } + return set(elem, reflectValue, arguments.NonIndexed()[0]) + } // Computes the full size of an array; @@ -278,3 +277,18 @@ func capitalise(input string) string { } return strings.ToUpper(input[:1]) + input[1:] } + +//unpackStruct extracts each argument into its corresponding struct field +func unpackStruct(value, reflectValue reflect.Value, arg Argument) error { + name := capitalise(arg.Name) + typ := value.Type() + for j := 0; j < typ.NumField(); j++ { + // TODO read tags: `abi:"fieldName"` + if typ.Field(j).Name == name { + if err := set(value.Field(j), reflectValue, arg); err != nil { + return err + } + } + } + return nil +} diff --git a/accounts/abi/reflect.go b/accounts/abi/reflect.go index 7a9cdacd5..2e6bf7098 100644 --- a/accounts/abi/reflect.go +++ b/accounts/abi/reflect.go @@ -110,3 +110,19 @@ func requireUnpackKind(v reflect.Value, t reflect.Type, k reflect.Kind, } return nil } + +// requireUniqueStructFieldNames makes sure field names don't collide +func requireUniqueStructFieldNames(args Arguments) error { + exists := make(map[string]bool) + for _, arg := range args { + field := capitalise(arg.Name) + if field == "" { + return fmt.Errorf("abi: purely underscored output cannot unpack to struct") + } + if exists[field] { + return fmt.Errorf("abi: multiple outputs mapping to the same struct field '%s'", field) + } + exists[field] = true + } + return nil +}