github.com/klaytn/klaytn@v1.12.1/accounts/abi/topics_test.go (about)

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