github.com/chapsuk/go-ethereum@v1.8.12-0.20180615081455-574378edb50c/eth/filters/filter_system_test.go (about) 1 // Copyright 2016 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 filters 18 19 import ( 20 "context" 21 "fmt" 22 "math/big" 23 "math/rand" 24 "reflect" 25 "testing" 26 "time" 27 28 ethereum "github.com/ethereum/go-ethereum" 29 "github.com/ethereum/go-ethereum/common" 30 "github.com/ethereum/go-ethereum/consensus/ethash" 31 "github.com/ethereum/go-ethereum/core" 32 "github.com/ethereum/go-ethereum/core/bloombits" 33 "github.com/ethereum/go-ethereum/core/rawdb" 34 "github.com/ethereum/go-ethereum/core/types" 35 "github.com/ethereum/go-ethereum/ethdb" 36 "github.com/ethereum/go-ethereum/event" 37 "github.com/ethereum/go-ethereum/params" 38 "github.com/ethereum/go-ethereum/rpc" 39 ) 40 41 type testBackend struct { 42 mux *event.TypeMux 43 db ethdb.Database 44 sections uint64 45 txFeed *event.Feed 46 rmLogsFeed *event.Feed 47 logsFeed *event.Feed 48 chainFeed *event.Feed 49 } 50 51 func (b *testBackend) ChainDb() ethdb.Database { 52 return b.db 53 } 54 55 func (b *testBackend) EventMux() *event.TypeMux { 56 return b.mux 57 } 58 59 func (b *testBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) { 60 var ( 61 hash common.Hash 62 num uint64 63 ) 64 if blockNr == rpc.LatestBlockNumber { 65 hash = rawdb.ReadHeadBlockHash(b.db) 66 number := rawdb.ReadHeaderNumber(b.db, hash) 67 if number == nil { 68 return nil, nil 69 } 70 num = *number 71 } else { 72 num = uint64(blockNr) 73 hash = rawdb.ReadCanonicalHash(b.db, num) 74 } 75 return rawdb.ReadHeader(b.db, hash, num), nil 76 } 77 78 func (b *testBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { 79 if number := rawdb.ReadHeaderNumber(b.db, hash); number != nil { 80 return rawdb.ReadReceipts(b.db, hash, *number), nil 81 } 82 return nil, nil 83 } 84 85 func (b *testBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*types.Log, error) { 86 number := rawdb.ReadHeaderNumber(b.db, hash) 87 if number == nil { 88 return nil, nil 89 } 90 receipts := rawdb.ReadReceipts(b.db, hash, *number) 91 92 logs := make([][]*types.Log, len(receipts)) 93 for i, receipt := range receipts { 94 logs[i] = receipt.Logs 95 } 96 return logs, nil 97 } 98 99 func (b *testBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription { 100 return b.txFeed.Subscribe(ch) 101 } 102 103 func (b *testBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription { 104 return b.rmLogsFeed.Subscribe(ch) 105 } 106 107 func (b *testBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription { 108 return b.logsFeed.Subscribe(ch) 109 } 110 111 func (b *testBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription { 112 return b.chainFeed.Subscribe(ch) 113 } 114 115 func (b *testBackend) BloomStatus() (uint64, uint64) { 116 return params.BloomBitsBlocks, b.sections 117 } 118 119 func (b *testBackend) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) { 120 requests := make(chan chan *bloombits.Retrieval) 121 122 go session.Multiplex(16, 0, requests) 123 go func() { 124 for { 125 // Wait for a service request or a shutdown 126 select { 127 case <-ctx.Done(): 128 return 129 130 case request := <-requests: 131 task := <-request 132 133 task.Bitsets = make([][]byte, len(task.Sections)) 134 for i, section := range task.Sections { 135 if rand.Int()%4 != 0 { // Handle occasional missing deliveries 136 head := rawdb.ReadCanonicalHash(b.db, (section+1)*params.BloomBitsBlocks-1) 137 task.Bitsets[i], _ = rawdb.ReadBloomBits(b.db, task.Bit, section, head) 138 } 139 } 140 request <- task 141 } 142 } 143 }() 144 } 145 146 // TestBlockSubscription tests if a block subscription returns block hashes for posted chain events. 147 // It creates multiple subscriptions: 148 // - one at the start and should receive all posted chain events and a second (blockHashes) 149 // - one that is created after a cutoff moment and uninstalled after a second cutoff moment (blockHashes[cutoff1:cutoff2]) 150 // - one that is created after the second cutoff moment (blockHashes[cutoff2:]) 151 func TestBlockSubscription(t *testing.T) { 152 t.Parallel() 153 154 var ( 155 mux = new(event.TypeMux) 156 db = ethdb.NewMemDatabase() 157 txFeed = new(event.Feed) 158 rmLogsFeed = new(event.Feed) 159 logsFeed = new(event.Feed) 160 chainFeed = new(event.Feed) 161 backend = &testBackend{mux, db, 0, txFeed, rmLogsFeed, logsFeed, chainFeed} 162 api = NewPublicFilterAPI(backend, false) 163 genesis = new(core.Genesis).MustCommit(db) 164 chain, _ = core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 10, func(i int, gen *core.BlockGen) {}) 165 chainEvents = []core.ChainEvent{} 166 ) 167 168 for _, blk := range chain { 169 chainEvents = append(chainEvents, core.ChainEvent{Hash: blk.Hash(), Block: blk}) 170 } 171 172 chan0 := make(chan *types.Header) 173 sub0 := api.events.SubscribeNewHeads(chan0) 174 chan1 := make(chan *types.Header) 175 sub1 := api.events.SubscribeNewHeads(chan1) 176 177 go func() { // simulate client 178 i1, i2 := 0, 0 179 for i1 != len(chainEvents) || i2 != len(chainEvents) { 180 select { 181 case header := <-chan0: 182 if chainEvents[i1].Hash != header.Hash() { 183 t.Errorf("sub0 received invalid hash on index %d, want %x, got %x", i1, chainEvents[i1].Hash, header.Hash()) 184 } 185 i1++ 186 case header := <-chan1: 187 if chainEvents[i2].Hash != header.Hash() { 188 t.Errorf("sub1 received invalid hash on index %d, want %x, got %x", i2, chainEvents[i2].Hash, header.Hash()) 189 } 190 i2++ 191 } 192 } 193 194 sub0.Unsubscribe() 195 sub1.Unsubscribe() 196 }() 197 198 time.Sleep(1 * time.Second) 199 for _, e := range chainEvents { 200 chainFeed.Send(e) 201 } 202 203 <-sub0.Err() 204 <-sub1.Err() 205 } 206 207 // TestPendingTxFilter tests whether pending tx filters retrieve all pending transactions that are posted to the event mux. 208 func TestPendingTxFilter(t *testing.T) { 209 t.Parallel() 210 211 var ( 212 mux = new(event.TypeMux) 213 db = ethdb.NewMemDatabase() 214 txFeed = new(event.Feed) 215 rmLogsFeed = new(event.Feed) 216 logsFeed = new(event.Feed) 217 chainFeed = new(event.Feed) 218 backend = &testBackend{mux, db, 0, txFeed, rmLogsFeed, logsFeed, chainFeed} 219 api = NewPublicFilterAPI(backend, false) 220 221 transactions = []*types.Transaction{ 222 types.NewTransaction(0, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil), 223 types.NewTransaction(1, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil), 224 types.NewTransaction(2, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil), 225 types.NewTransaction(3, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil), 226 types.NewTransaction(4, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil), 227 } 228 229 hashes []common.Hash 230 ) 231 232 fid0 := api.NewPendingTransactionFilter() 233 234 time.Sleep(1 * time.Second) 235 txFeed.Send(core.NewTxsEvent{Txs: transactions}) 236 237 timeout := time.Now().Add(1 * time.Second) 238 for { 239 results, err := api.GetFilterChanges(fid0) 240 if err != nil { 241 t.Fatalf("Unable to retrieve logs: %v", err) 242 } 243 244 h := results.([]common.Hash) 245 hashes = append(hashes, h...) 246 if len(hashes) >= len(transactions) { 247 break 248 } 249 // check timeout 250 if time.Now().After(timeout) { 251 break 252 } 253 254 time.Sleep(100 * time.Millisecond) 255 } 256 257 if len(hashes) != len(transactions) { 258 t.Errorf("invalid number of transactions, want %d transactions(s), got %d", len(transactions), len(hashes)) 259 return 260 } 261 for i := range hashes { 262 if hashes[i] != transactions[i].Hash() { 263 t.Errorf("hashes[%d] invalid, want %x, got %x", i, transactions[i].Hash(), hashes[i]) 264 } 265 } 266 } 267 268 // TestLogFilterCreation test whether a given filter criteria makes sense. 269 // If not it must return an error. 270 func TestLogFilterCreation(t *testing.T) { 271 var ( 272 mux = new(event.TypeMux) 273 db = ethdb.NewMemDatabase() 274 txFeed = new(event.Feed) 275 rmLogsFeed = new(event.Feed) 276 logsFeed = new(event.Feed) 277 chainFeed = new(event.Feed) 278 backend = &testBackend{mux, db, 0, txFeed, rmLogsFeed, logsFeed, chainFeed} 279 api = NewPublicFilterAPI(backend, false) 280 281 testCases = []struct { 282 crit FilterCriteria 283 success bool 284 }{ 285 // defaults 286 {FilterCriteria{}, true}, 287 // valid block number range 288 {FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(2)}, true}, 289 // "mined" block range to pending 290 {FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, true}, 291 // new mined and pending blocks 292 {FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())}, true}, 293 // from block "higher" than to block 294 {FilterCriteria{FromBlock: big.NewInt(2), ToBlock: big.NewInt(1)}, false}, 295 // from block "higher" than to block 296 {FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(100)}, false}, 297 // from block "higher" than to block 298 {FilterCriteria{FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(100)}, false}, 299 // from block "higher" than to block 300 {FilterCriteria{FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, false}, 301 } 302 ) 303 304 for i, test := range testCases { 305 _, err := api.NewFilter(test.crit) 306 if test.success && err != nil { 307 t.Errorf("expected filter creation for case %d to success, got %v", i, err) 308 } 309 if !test.success && err == nil { 310 t.Errorf("expected testcase %d to fail with an error", i) 311 } 312 } 313 } 314 315 // TestInvalidLogFilterCreation tests whether invalid filter log criteria results in an error 316 // when the filter is created. 317 func TestInvalidLogFilterCreation(t *testing.T) { 318 t.Parallel() 319 320 var ( 321 mux = new(event.TypeMux) 322 db = ethdb.NewMemDatabase() 323 txFeed = new(event.Feed) 324 rmLogsFeed = new(event.Feed) 325 logsFeed = new(event.Feed) 326 chainFeed = new(event.Feed) 327 backend = &testBackend{mux, db, 0, txFeed, rmLogsFeed, logsFeed, chainFeed} 328 api = NewPublicFilterAPI(backend, false) 329 ) 330 331 // different situations where log filter creation should fail. 332 // Reason: fromBlock > toBlock 333 testCases := []FilterCriteria{ 334 0: {FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, 335 1: {FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(100)}, 336 2: {FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(100)}, 337 } 338 339 for i, test := range testCases { 340 if _, err := api.NewFilter(test); err == nil { 341 t.Errorf("Expected NewFilter for case #%d to fail", i) 342 } 343 } 344 } 345 346 // TestLogFilter tests whether log filters match the correct logs that are posted to the event feed. 347 func TestLogFilter(t *testing.T) { 348 t.Parallel() 349 350 var ( 351 mux = new(event.TypeMux) 352 db = ethdb.NewMemDatabase() 353 txFeed = new(event.Feed) 354 rmLogsFeed = new(event.Feed) 355 logsFeed = new(event.Feed) 356 chainFeed = new(event.Feed) 357 backend = &testBackend{mux, db, 0, txFeed, rmLogsFeed, logsFeed, chainFeed} 358 api = NewPublicFilterAPI(backend, false) 359 360 firstAddr = common.HexToAddress("0x1111111111111111111111111111111111111111") 361 secondAddr = common.HexToAddress("0x2222222222222222222222222222222222222222") 362 thirdAddress = common.HexToAddress("0x3333333333333333333333333333333333333333") 363 notUsedAddress = common.HexToAddress("0x9999999999999999999999999999999999999999") 364 firstTopic = common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111") 365 secondTopic = common.HexToHash("0x2222222222222222222222222222222222222222222222222222222222222222") 366 notUsedTopic = common.HexToHash("0x9999999999999999999999999999999999999999999999999999999999999999") 367 368 // posted twice, once as vm.Logs and once as core.PendingLogsEvent 369 allLogs = []*types.Log{ 370 {Address: firstAddr}, 371 {Address: firstAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 1}, 372 {Address: secondAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 1}, 373 {Address: thirdAddress, Topics: []common.Hash{secondTopic}, BlockNumber: 2}, 374 {Address: thirdAddress, Topics: []common.Hash{secondTopic}, BlockNumber: 3}, 375 } 376 377 expectedCase7 = []*types.Log{allLogs[3], allLogs[4], allLogs[0], allLogs[1], allLogs[2], allLogs[3], allLogs[4]} 378 expectedCase11 = []*types.Log{allLogs[1], allLogs[2], allLogs[1], allLogs[2]} 379 380 testCases = []struct { 381 crit FilterCriteria 382 expected []*types.Log 383 id rpc.ID 384 }{ 385 // match all 386 0: {FilterCriteria{}, allLogs, ""}, 387 // match none due to no matching addresses 388 1: {FilterCriteria{Addresses: []common.Address{{}, notUsedAddress}, Topics: [][]common.Hash{nil}}, []*types.Log{}, ""}, 389 // match logs based on addresses, ignore topics 390 2: {FilterCriteria{Addresses: []common.Address{firstAddr}}, allLogs[:2], ""}, 391 // match none due to no matching topics (match with address) 392 3: {FilterCriteria{Addresses: []common.Address{secondAddr}, Topics: [][]common.Hash{{notUsedTopic}}}, []*types.Log{}, ""}, 393 // match logs based on addresses and topics 394 4: {FilterCriteria{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, allLogs[3:5], ""}, 395 // match logs based on multiple addresses and "or" topics 396 5: {FilterCriteria{Addresses: []common.Address{secondAddr, thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, allLogs[2:5], ""}, 397 // logs in the pending block 398 6: {FilterCriteria{Addresses: []common.Address{firstAddr}, FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())}, allLogs[:2], ""}, 399 // mined logs with block num >= 2 or pending logs 400 7: {FilterCriteria{FromBlock: big.NewInt(2), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())}, expectedCase7, ""}, 401 // all "mined" logs with block num >= 2 402 8: {FilterCriteria{FromBlock: big.NewInt(2), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, allLogs[3:], ""}, 403 // all "mined" logs 404 9: {FilterCriteria{ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, allLogs, ""}, 405 // all "mined" logs with 1>= block num <=2 and topic secondTopic 406 10: {FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(2), Topics: [][]common.Hash{{secondTopic}}}, allLogs[3:4], ""}, 407 // all "mined" and pending logs with topic firstTopic 408 11: {FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), Topics: [][]common.Hash{{firstTopic}}}, expectedCase11, ""}, 409 // match all logs due to wildcard topic 410 12: {FilterCriteria{Topics: [][]common.Hash{nil}}, allLogs[1:], ""}, 411 } 412 ) 413 414 // create all filters 415 for i := range testCases { 416 testCases[i].id, _ = api.NewFilter(testCases[i].crit) 417 } 418 419 // raise events 420 time.Sleep(1 * time.Second) 421 if nsend := logsFeed.Send(allLogs); nsend == 0 { 422 t.Fatal("Shoud have at least one subscription") 423 } 424 if err := mux.Post(core.PendingLogsEvent{Logs: allLogs}); err != nil { 425 t.Fatal(err) 426 } 427 428 for i, tt := range testCases { 429 var fetched []*types.Log 430 timeout := time.Now().Add(1 * time.Second) 431 for { // fetch all expected logs 432 results, err := api.GetFilterChanges(tt.id) 433 if err != nil { 434 t.Fatalf("Unable to fetch logs: %v", err) 435 } 436 437 fetched = append(fetched, results.([]*types.Log)...) 438 if len(fetched) >= len(tt.expected) { 439 break 440 } 441 // check timeout 442 if time.Now().After(timeout) { 443 break 444 } 445 446 time.Sleep(100 * time.Millisecond) 447 } 448 449 if len(fetched) != len(tt.expected) { 450 t.Errorf("invalid number of logs for case %d, want %d log(s), got %d", i, len(tt.expected), len(fetched)) 451 return 452 } 453 454 for l := range fetched { 455 if fetched[l].Removed { 456 t.Errorf("expected log not to be removed for log %d in case %d", l, i) 457 } 458 if !reflect.DeepEqual(fetched[l], tt.expected[l]) { 459 t.Errorf("invalid log on index %d for case %d", l, i) 460 } 461 } 462 } 463 } 464 465 // TestPendingLogsSubscription tests if a subscription receives the correct pending logs that are posted to the event feed. 466 func TestPendingLogsSubscription(t *testing.T) { 467 t.Parallel() 468 469 var ( 470 mux = new(event.TypeMux) 471 db = ethdb.NewMemDatabase() 472 txFeed = new(event.Feed) 473 rmLogsFeed = new(event.Feed) 474 logsFeed = new(event.Feed) 475 chainFeed = new(event.Feed) 476 backend = &testBackend{mux, db, 0, txFeed, rmLogsFeed, logsFeed, chainFeed} 477 api = NewPublicFilterAPI(backend, false) 478 479 firstAddr = common.HexToAddress("0x1111111111111111111111111111111111111111") 480 secondAddr = common.HexToAddress("0x2222222222222222222222222222222222222222") 481 thirdAddress = common.HexToAddress("0x3333333333333333333333333333333333333333") 482 notUsedAddress = common.HexToAddress("0x9999999999999999999999999999999999999999") 483 firstTopic = common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111") 484 secondTopic = common.HexToHash("0x2222222222222222222222222222222222222222222222222222222222222222") 485 thirdTopic = common.HexToHash("0x3333333333333333333333333333333333333333333333333333333333333333") 486 fourthTopic = common.HexToHash("0x4444444444444444444444444444444444444444444444444444444444444444") 487 notUsedTopic = common.HexToHash("0x9999999999999999999999999999999999999999999999999999999999999999") 488 489 allLogs = []core.PendingLogsEvent{ 490 {Logs: []*types.Log{{Address: firstAddr, Topics: []common.Hash{}, BlockNumber: 0}}}, 491 {Logs: []*types.Log{{Address: firstAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 1}}}, 492 {Logs: []*types.Log{{Address: secondAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 2}}}, 493 {Logs: []*types.Log{{Address: thirdAddress, Topics: []common.Hash{secondTopic}, BlockNumber: 3}}}, 494 {Logs: []*types.Log{{Address: thirdAddress, Topics: []common.Hash{secondTopic}, BlockNumber: 4}}}, 495 {Logs: []*types.Log{ 496 {Address: thirdAddress, Topics: []common.Hash{firstTopic}, BlockNumber: 5}, 497 {Address: thirdAddress, Topics: []common.Hash{thirdTopic}, BlockNumber: 5}, 498 {Address: thirdAddress, Topics: []common.Hash{fourthTopic}, BlockNumber: 5}, 499 {Address: firstAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 5}, 500 }}, 501 } 502 503 convertLogs = func(pl []core.PendingLogsEvent) []*types.Log { 504 var logs []*types.Log 505 for _, l := range pl { 506 logs = append(logs, l.Logs...) 507 } 508 return logs 509 } 510 511 testCases = []struct { 512 crit ethereum.FilterQuery 513 expected []*types.Log 514 c chan []*types.Log 515 sub *Subscription 516 }{ 517 // match all 518 {ethereum.FilterQuery{}, convertLogs(allLogs), nil, nil}, 519 // match none due to no matching addresses 520 {ethereum.FilterQuery{Addresses: []common.Address{{}, notUsedAddress}, Topics: [][]common.Hash{nil}}, []*types.Log{}, nil, nil}, 521 // match logs based on addresses, ignore topics 522 {ethereum.FilterQuery{Addresses: []common.Address{firstAddr}}, append(convertLogs(allLogs[:2]), allLogs[5].Logs[3]), nil, nil}, 523 // match none due to no matching topics (match with address) 524 {ethereum.FilterQuery{Addresses: []common.Address{secondAddr}, Topics: [][]common.Hash{{notUsedTopic}}}, []*types.Log{}, nil, nil}, 525 // match logs based on addresses and topics 526 {ethereum.FilterQuery{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, append(convertLogs(allLogs[3:5]), allLogs[5].Logs[0]), nil, nil}, 527 // match logs based on multiple addresses and "or" topics 528 {ethereum.FilterQuery{Addresses: []common.Address{secondAddr, thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, append(convertLogs(allLogs[2:5]), allLogs[5].Logs[0]), nil, nil}, 529 // block numbers are ignored for filters created with New***Filter, these return all logs that match the given criteria when the state changes 530 {ethereum.FilterQuery{Addresses: []common.Address{firstAddr}, FromBlock: big.NewInt(2), ToBlock: big.NewInt(3)}, append(convertLogs(allLogs[:2]), allLogs[5].Logs[3]), nil, nil}, 531 // multiple pending logs, should match only 2 topics from the logs in block 5 532 {ethereum.FilterQuery{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, fourthTopic}}}, []*types.Log{allLogs[5].Logs[0], allLogs[5].Logs[2]}, nil, nil}, 533 } 534 ) 535 536 // create all subscriptions, this ensures all subscriptions are created before the events are posted. 537 // on slow machines this could otherwise lead to missing events when the subscription is created after 538 // (some) events are posted. 539 for i := range testCases { 540 testCases[i].c = make(chan []*types.Log) 541 testCases[i].sub, _ = api.events.SubscribeLogs(testCases[i].crit, testCases[i].c) 542 } 543 544 for n, test := range testCases { 545 i := n 546 tt := test 547 go func() { 548 var fetched []*types.Log 549 fetchLoop: 550 for { 551 logs := <-tt.c 552 fetched = append(fetched, logs...) 553 if len(fetched) >= len(tt.expected) { 554 break fetchLoop 555 } 556 } 557 558 if len(fetched) != len(tt.expected) { 559 panic(fmt.Sprintf("invalid number of logs for case %d, want %d log(s), got %d", i, len(tt.expected), len(fetched))) 560 } 561 562 for l := range fetched { 563 if fetched[l].Removed { 564 panic(fmt.Sprintf("expected log not to be removed for log %d in case %d", l, i)) 565 } 566 if !reflect.DeepEqual(fetched[l], tt.expected[l]) { 567 panic(fmt.Sprintf("invalid log on index %d for case %d", l, i)) 568 } 569 } 570 }() 571 } 572 573 // raise events 574 time.Sleep(1 * time.Second) 575 // allLogs are type of core.PendingLogsEvent 576 for _, l := range allLogs { 577 if err := mux.Post(l); err != nil { 578 t.Fatal(err) 579 } 580 } 581 }