github.com/ethereum/go-ethereum@v1.14.3/accounts/abi/topics_test.go (about)

     1  // Copyright 2020 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package abi
    18  
    19  import (
    20  	"math"
    21  	"math/big"
    22  	"reflect"
    23  	"testing"
    24  
    25  	"github.com/ethereum/go-ethereum/common"
    26  	"github.com/ethereum/go-ethereum/crypto"
    27  )
    28  
    29  func TestMakeTopics(t *testing.T) {
    30  	t.Parallel()
    31  	type args struct {
    32  		query [][]interface{}
    33  	}
    34  	tests := []struct {
    35  		name    string
    36  		args    args
    37  		want    [][]common.Hash
    38  		wantErr bool
    39  	}{
    40  		{
    41  			"support fixed byte types, right padded to 32 bytes",
    42  			args{[][]interface{}{{[5]byte{1, 2, 3, 4, 5}}}},
    43  			[][]common.Hash{{common.Hash{1, 2, 3, 4, 5}}},
    44  			false,
    45  		},
    46  		{
    47  			"support common hash types in topics",
    48  			args{[][]interface{}{{common.Hash{1, 2, 3, 4, 5}}}},
    49  			[][]common.Hash{{common.Hash{1, 2, 3, 4, 5}}},
    50  			false,
    51  		},
    52  		{
    53  			"support address types in topics",
    54  			args{[][]interface{}{{common.Address{1, 2, 3, 4, 5}}}},
    55  			[][]common.Hash{{common.Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5}}},
    56  			false,
    57  		},
    58  		{
    59  			"support positive *big.Int types in topics",
    60  			args{[][]interface{}{
    61  				{big.NewInt(1)},
    62  				{big.NewInt(1).Lsh(big.NewInt(2), 254)},
    63  			}},
    64  			[][]common.Hash{
    65  				{common.HexToHash("0000000000000000000000000000000000000000000000000000000000000001")},
    66  				{common.Hash{128}},
    67  			},
    68  			false,
    69  		},
    70  		{
    71  			"support negative *big.Int types in topics",
    72  			args{[][]interface{}{
    73  				{big.NewInt(-1)},
    74  				{big.NewInt(math.MinInt64)},
    75  			}},
    76  			[][]common.Hash{
    77  				{common.MaxHash},
    78  				{common.HexToHash("ffffffffffffffffffffffffffffffffffffffffffffffff8000000000000000")},
    79  			},
    80  			false,
    81  		},
    82  		{
    83  			"support boolean types in topics",
    84  			args{[][]interface{}{
    85  				{true},
    86  				{false},
    87  			}},
    88  			[][]common.Hash{
    89  				{common.Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}},
    90  				{common.Hash{0}},
    91  			},
    92  			false,
    93  		},
    94  		{
    95  			"support int/uint(8/16/32/64) types in topics",
    96  			args{[][]interface{}{
    97  				{int8(-2)},
    98  				{int16(-3)},
    99  				{int32(-4)},
   100  				{int64(-5)},
   101  				{int8(1)},
   102  				{int16(256)},
   103  				{int32(65536)},
   104  				{int64(4294967296)},
   105  				{uint8(1)},
   106  				{uint16(256)},
   107  				{uint32(65536)},
   108  				{uint64(4294967296)},
   109  			}},
   110  			[][]common.Hash{
   111  				{common.Hash{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254}},
   112  				{common.Hash{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253}},
   113  				{common.Hash{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 252}},
   114  				{common.Hash{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 251}},
   115  				{common.Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}},
   116  				{common.Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}},
   117  				{common.Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}},
   118  				{common.Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}},
   119  				{common.Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}},
   120  				{common.Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}},
   121  				{common.Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}},
   122  				{common.Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}},
   123  			},
   124  			false,
   125  		},
   126  		{
   127  			"support string types in topics",
   128  			args{[][]interface{}{{"hello world"}}},
   129  			[][]common.Hash{{crypto.Keccak256Hash([]byte("hello world"))}},
   130  			false,
   131  		},
   132  		{
   133  			"support byte slice types in topics",
   134  			args{[][]interface{}{{[]byte{1, 2, 3}}}},
   135  			[][]common.Hash{{crypto.Keccak256Hash([]byte{1, 2, 3})}},
   136  			false,
   137  		},
   138  	}
   139  	for _, tt := range tests {
   140  		tt := tt
   141  		t.Run(tt.name, func(t *testing.T) {
   142  			t.Parallel()
   143  			got, err := MakeTopics(tt.args.query...)
   144  			if (err != nil) != tt.wantErr {
   145  				t.Errorf("makeTopics() error = %v, wantErr %v", err, tt.wantErr)
   146  				return
   147  			}
   148  			if !reflect.DeepEqual(got, tt.want) {
   149  				t.Errorf("makeTopics() = %v, want %v", got, tt.want)
   150  			}
   151  		})
   152  	}
   153  }
   154  
   155  type args struct {
   156  	createObj func() interface{}
   157  	resultObj func() interface{}
   158  	resultMap func() map[string]interface{}
   159  	fields    Arguments
   160  	topics    []common.Hash
   161  }
   162  
   163  type bytesStruct struct {
   164  	StaticBytes [5]byte
   165  }
   166  type int8Struct struct {
   167  	Int8Value int8
   168  }
   169  type int256Struct struct {
   170  	Int256Value *big.Int
   171  }
   172  
   173  type hashStruct struct {
   174  	HashValue common.Hash
   175  }
   176  
   177  type funcStruct struct {
   178  	FuncValue [24]byte
   179  }
   180  
   181  type topicTest struct {
   182  	name    string
   183  	args    args
   184  	wantErr bool
   185  }
   186  
   187  func setupTopicsTests() []topicTest {
   188  	bytesType, _ := NewType("bytes5", "", nil)
   189  	int8Type, _ := NewType("int8", "", nil)
   190  	int256Type, _ := NewType("int256", "", nil)
   191  	tupleType, _ := NewType("tuple(int256,int8)", "", nil)
   192  	stringType, _ := NewType("string", "", nil)
   193  	funcType, _ := NewType("function", "", nil)
   194  
   195  	tests := []topicTest{
   196  		{
   197  			name: "support fixed byte types, right padded to 32 bytes",
   198  			args: args{
   199  				createObj: func() interface{} { return &bytesStruct{} },
   200  				resultObj: func() interface{} { return &bytesStruct{StaticBytes: [5]byte{1, 2, 3, 4, 5}} },
   201  				resultMap: func() map[string]interface{} {
   202  					return map[string]interface{}{"staticBytes": [5]byte{1, 2, 3, 4, 5}}
   203  				},
   204  				fields: Arguments{Argument{
   205  					Name:    "staticBytes",
   206  					Type:    bytesType,
   207  					Indexed: true,
   208  				}},
   209  				topics: []common.Hash{
   210  					{1, 2, 3, 4, 5},
   211  				},
   212  			},
   213  			wantErr: false,
   214  		},
   215  		{
   216  			name: "int8 with negative value",
   217  			args: args{
   218  				createObj: func() interface{} { return &int8Struct{} },
   219  				resultObj: func() interface{} { return &int8Struct{Int8Value: -1} },
   220  				resultMap: func() map[string]interface{} {
   221  					return map[string]interface{}{"int8Value": int8(-1)}
   222  				},
   223  				fields: Arguments{Argument{
   224  					Name:    "int8Value",
   225  					Type:    int8Type,
   226  					Indexed: true,
   227  				}},
   228  				topics: []common.Hash{
   229  					{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
   230  						255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
   231  				},
   232  			},
   233  			wantErr: false,
   234  		},
   235  		{
   236  			name: "int256 with negative value",
   237  			args: args{
   238  				createObj: func() interface{} { return &int256Struct{} },
   239  				resultObj: func() interface{} { return &int256Struct{Int256Value: big.NewInt(-1)} },
   240  				resultMap: func() map[string]interface{} {
   241  					return map[string]interface{}{"int256Value": big.NewInt(-1)}
   242  				},
   243  				fields: Arguments{Argument{
   244  					Name:    "int256Value",
   245  					Type:    int256Type,
   246  					Indexed: true,
   247  				}},
   248  				topics: []common.Hash{
   249  					{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
   250  						255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
   251  				},
   252  			},
   253  			wantErr: false,
   254  		},
   255  		{
   256  			name: "hash type",
   257  			args: args{
   258  				createObj: func() interface{} { return &hashStruct{} },
   259  				resultObj: func() interface{} { return &hashStruct{crypto.Keccak256Hash([]byte("stringtopic"))} },
   260  				resultMap: func() map[string]interface{} {
   261  					return map[string]interface{}{"hashValue": crypto.Keccak256Hash([]byte("stringtopic"))}
   262  				},
   263  				fields: Arguments{Argument{
   264  					Name:    "hashValue",
   265  					Type:    stringType,
   266  					Indexed: true,
   267  				}},
   268  				topics: []common.Hash{
   269  					crypto.Keccak256Hash([]byte("stringtopic")),
   270  				},
   271  			},
   272  			wantErr: false,
   273  		},
   274  		{
   275  			name: "function type",
   276  			args: args{
   277  				createObj: func() interface{} { return &funcStruct{} },
   278  				resultObj: func() interface{} {
   279  					return &funcStruct{[24]byte{255, 255, 255, 255, 255, 255, 255, 255,
   280  						255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}}
   281  				},
   282  				resultMap: func() map[string]interface{} {
   283  					return map[string]interface{}{"funcValue": [24]byte{255, 255, 255, 255, 255, 255, 255, 255,
   284  						255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}}
   285  				},
   286  				fields: Arguments{Argument{
   287  					Name:    "funcValue",
   288  					Type:    funcType,
   289  					Indexed: true,
   290  				}},
   291  				topics: []common.Hash{
   292  					{0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255,
   293  						255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
   294  				},
   295  			},
   296  			wantErr: false,
   297  		},
   298  		{
   299  			name: "error on topic/field count mismatch",
   300  			args: args{
   301  				createObj: func() interface{} { return nil },
   302  				resultObj: func() interface{} { return nil },
   303  				resultMap: func() map[string]interface{} { return make(map[string]interface{}) },
   304  				fields: Arguments{Argument{
   305  					Name:    "tupletype",
   306  					Type:    tupleType,
   307  					Indexed: true,
   308  				}},
   309  				topics: []common.Hash{},
   310  			},
   311  			wantErr: true,
   312  		},
   313  		{
   314  			name: "error on unindexed arguments",
   315  			args: args{
   316  				createObj: func() interface{} { return &int256Struct{} },
   317  				resultObj: func() interface{} { return &int256Struct{} },
   318  				resultMap: func() map[string]interface{} { return make(map[string]interface{}) },
   319  				fields: Arguments{Argument{
   320  					Name:    "int256Value",
   321  					Type:    int256Type,
   322  					Indexed: false,
   323  				}},
   324  				topics: []common.Hash{
   325  					{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
   326  						255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
   327  				},
   328  			},
   329  			wantErr: true,
   330  		},
   331  		{
   332  			name: "error on tuple in topic reconstruction",
   333  			args: args{
   334  				createObj: func() interface{} { return &tupleType },
   335  				resultObj: func() interface{} { return &tupleType },
   336  				resultMap: func() map[string]interface{} { return make(map[string]interface{}) },
   337  				fields: Arguments{Argument{
   338  					Name:    "tupletype",
   339  					Type:    tupleType,
   340  					Indexed: true,
   341  				}},
   342  				topics: []common.Hash{{0}},
   343  			},
   344  			wantErr: true,
   345  		},
   346  		{
   347  			name: "error on improper encoded function",
   348  			args: args{
   349  				createObj: func() interface{} { return &funcStruct{} },
   350  				resultObj: func() interface{} { return &funcStruct{} },
   351  				resultMap: func() map[string]interface{} {
   352  					return make(map[string]interface{})
   353  				},
   354  				fields: Arguments{Argument{
   355  					Name:    "funcValue",
   356  					Type:    funcType,
   357  					Indexed: true,
   358  				}},
   359  				topics: []common.Hash{
   360  					{0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 255, 255, 255, 255, 255,
   361  						255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
   362  				},
   363  			},
   364  			wantErr: true,
   365  		},
   366  	}
   367  
   368  	return tests
   369  }
   370  
   371  func TestParseTopics(t *testing.T) {
   372  	t.Parallel()
   373  	tests := setupTopicsTests()
   374  
   375  	for _, tt := range tests {
   376  		tt := tt
   377  		t.Run(tt.name, func(t *testing.T) {
   378  			t.Parallel()
   379  			createObj := tt.args.createObj()
   380  			if err := ParseTopics(createObj, tt.args.fields, tt.args.topics); (err != nil) != tt.wantErr {
   381  				t.Errorf("parseTopics() error = %v, wantErr %v", err, tt.wantErr)
   382  			}
   383  			resultObj := tt.args.resultObj()
   384  			if !reflect.DeepEqual(createObj, resultObj) {
   385  				t.Errorf("parseTopics() = %v, want %v", createObj, resultObj)
   386  			}
   387  		})
   388  	}
   389  }
   390  
   391  func TestParseTopicsIntoMap(t *testing.T) {
   392  	t.Parallel()
   393  	tests := setupTopicsTests()
   394  
   395  	for _, tt := range tests {
   396  		tt := tt
   397  		t.Run(tt.name, func(t *testing.T) {
   398  			t.Parallel()
   399  			outMap := make(map[string]interface{})
   400  			if err := ParseTopicsIntoMap(outMap, tt.args.fields, tt.args.topics); (err != nil) != tt.wantErr {
   401  				t.Errorf("parseTopicsIntoMap() error = %v, wantErr %v", err, tt.wantErr)
   402  			}
   403  			resultMap := tt.args.resultMap()
   404  			if !reflect.DeepEqual(outMap, resultMap) {
   405  				t.Errorf("parseTopicsIntoMap() = %v, want %v", outMap, resultMap)
   406  			}
   407  		})
   408  	}
   409  }