github.com/ava-labs/subnet-evm@v0.6.4/accounts/abi/topics_test.go (about)

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