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 }