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