github.com/benorgera/go-ethereum@v1.10.18-0.20220401011646-b3f57b1a73ba/accounts/abi/topics_test.go (about)

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