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