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