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