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