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