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 }