From 7640c9c933cb7a60303f3424e7a6c4a0ab5a7af5 Mon Sep 17 00:00:00 2001 From: Jeremy McNevin Date: Mon, 28 Jan 2019 12:47:24 -0600 Subject: [PATCH] bind: Static byte arrays should be right-padded Per https://solidity.readthedocs.io/en/v0.5.3/abi-spec.html: "bytes: enc(X) is the sequence of bytes in X padded with trailing zero-bytes to a length of 32 bytes" --- accounts/abi/bind/topics.go | 8 ++- accounts/abi/bind/topics_test.go | 103 +++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 accounts/abi/bind/topics_test.go diff --git a/accounts/abi/bind/topics.go b/accounts/abi/bind/topics.go index 600dfcda9..af9c272cd 100644 --- a/accounts/abi/bind/topics.go +++ b/accounts/abi/bind/topics.go @@ -83,8 +83,10 @@ func makeTopics(query ...[]interface{}) ([][]common.Hash, error) { val := reflect.ValueOf(rule) switch { + + // static byte array case val.Kind() == reflect.Array && reflect.TypeOf(rule).Elem().Kind() == reflect.Uint8: - reflect.Copy(reflect.ValueOf(topic[common.HashLength-val.Len():]), val) + reflect.Copy(reflect.ValueOf(topic[:val.Len()]), val) default: return nil, fmt.Errorf("unsupported indexed type: %T", rule) @@ -175,8 +177,10 @@ func parseTopics(out interface{}, fields abi.Arguments, topics []common.Hash) er default: // Ran out of custom types, try the crazies switch { + + // static byte array case arg.Type.T == abi.FixedBytesTy: - reflect.Copy(field, reflect.ValueOf(topics[0][common.HashLength-arg.Type.Size:])) + reflect.Copy(field, reflect.ValueOf(topics[0][:arg.Type.Size])) default: return fmt.Errorf("unsupported indexed type: %v", arg.Type) diff --git a/accounts/abi/bind/topics_test.go b/accounts/abi/bind/topics_test.go new file mode 100644 index 000000000..c2bbd5af8 --- /dev/null +++ b/accounts/abi/bind/topics_test.go @@ -0,0 +1,103 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package bind + +import ( + "reflect" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" +) + +func Test_makeTopics(t *testing.T) { + type args struct { + query [][]interface{} + } + tests := []struct { + name string + args args + want [][]common.Hash + wantErr bool + }{ + { + "support fixed byte types, right padded to 32 bytes", + args{[][]interface{}{{[5]byte{1, 2, 3, 4, 5}}}}, + [][]common.Hash{{common.Hash{1, 2, 3, 4, 5}}}, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := makeTopics(tt.args.query...) + if (err != nil) != tt.wantErr { + t.Errorf("makeTopics() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("makeTopics() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_parseTopics(t *testing.T) { + type bytesStruct struct { + StaticBytes [5]byte + } + bytesType, _ := abi.NewType("bytes5", nil) + type args struct { + createObj func() interface{} + resultObj func() interface{} + fields abi.Arguments + topics []common.Hash + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "support fixed byte types, right padded to 32 bytes", + args: args{ + createObj: func() interface{} { return &bytesStruct{} }, + resultObj: func() interface{} { return &bytesStruct{StaticBytes: [5]byte{1, 2, 3, 4, 5}} }, + fields: abi.Arguments{abi.Argument{ + Name: "staticBytes", + Type: bytesType, + Indexed: true, + }}, + topics: []common.Hash{ + {1, 2, 3, 4, 5}, + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + createObj := tt.args.createObj() + if err := parseTopics(createObj, tt.args.fields, tt.args.topics); (err != nil) != tt.wantErr { + t.Errorf("parseTopics() error = %v, wantErr %v", err, tt.wantErr) + } + resultObj := tt.args.resultObj() + if !reflect.DeepEqual(createObj, resultObj) { + t.Errorf("parseTopics() = %v, want %v", createObj, resultObj) + } + }) + } +}