github.com/jimmyx0x/go-ethereum@v1.10.28/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 "errors" 22 "fmt" 23 "math/big" 24 "math/rand" 25 "reflect" 26 "runtime" 27 "testing" 28 "time" 29 30 "github.com/ethereum/go-ethereum" 31 "github.com/ethereum/go-ethereum/common" 32 "github.com/ethereum/go-ethereum/consensus/ethash" 33 "github.com/ethereum/go-ethereum/core" 34 "github.com/ethereum/go-ethereum/core/bloombits" 35 "github.com/ethereum/go-ethereum/core/rawdb" 36 "github.com/ethereum/go-ethereum/core/types" 37 "github.com/ethereum/go-ethereum/ethdb" 38 "github.com/ethereum/go-ethereum/event" 39 "github.com/ethereum/go-ethereum/params" 40 "github.com/ethereum/go-ethereum/rpc" 41 ) 42 43 type testBackend struct { 44 db ethdb.Database 45 sections uint64 46 txFeed event.Feed 47 logsFeed event.Feed 48 rmLogsFeed event.Feed 49 pendingLogsFeed event.Feed 50 chainFeed event.Feed 51 } 52 53 func (b *testBackend) ChainConfig() *params.ChainConfig { 54 panic("implement me") 55 } 56 57 func (b *testBackend) CurrentHeader() *types.Header { 58 panic("implement me") 59 } 60 61 func (b *testBackend) ChainDb() ethdb.Database { 62 return b.db 63 } 64 65 func (b *testBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) { 66 var ( 67 hash common.Hash 68 num uint64 69 ) 70 switch blockNr { 71 case rpc.LatestBlockNumber: 72 hash = rawdb.ReadHeadBlockHash(b.db) 73 number := rawdb.ReadHeaderNumber(b.db, hash) 74 if number == nil { 75 return nil, nil 76 } 77 num = *number 78 case rpc.FinalizedBlockNumber: 79 hash = rawdb.ReadFinalizedBlockHash(b.db) 80 number := rawdb.ReadHeaderNumber(b.db, hash) 81 if number == nil { 82 return nil, nil 83 } 84 num = *number 85 case rpc.SafeBlockNumber: 86 return nil, errors.New("safe block not found") 87 default: 88 num = uint64(blockNr) 89 hash = rawdb.ReadCanonicalHash(b.db, num) 90 } 91 return rawdb.ReadHeader(b.db, hash, num), nil 92 } 93 94 func (b *testBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) { 95 number := rawdb.ReadHeaderNumber(b.db, hash) 96 if number == nil { 97 return nil, nil 98 } 99 return rawdb.ReadHeader(b.db, hash, *number), nil 100 } 101 102 func (b *testBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { 103 if number := rawdb.ReadHeaderNumber(b.db, hash); number != nil { 104 return rawdb.ReadReceipts(b.db, hash, *number, params.TestChainConfig), nil 105 } 106 return nil, nil 107 } 108 109 func (b *testBackend) GetLogs(ctx context.Context, hash common.Hash, number uint64) ([][]*types.Log, error) { 110 logs := rawdb.ReadLogs(b.db, hash, number, params.TestChainConfig) 111 return logs, nil 112 } 113 114 func (b *testBackend) PendingBlockAndReceipts() (*types.Block, types.Receipts) { 115 return nil, nil 116 } 117 118 func (b *testBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription { 119 return b.txFeed.Subscribe(ch) 120 } 121 122 func (b *testBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription { 123 return b.rmLogsFeed.Subscribe(ch) 124 } 125 126 func (b *testBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription { 127 return b.logsFeed.Subscribe(ch) 128 } 129 130 func (b *testBackend) SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription { 131 return b.pendingLogsFeed.Subscribe(ch) 132 } 133 134 func (b *testBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription { 135 return b.chainFeed.Subscribe(ch) 136 } 137 138 func (b *testBackend) BloomStatus() (uint64, uint64) { 139 return params.BloomBitsBlocks, b.sections 140 } 141 142 func (b *testBackend) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) { 143 requests := make(chan chan *bloombits.Retrieval) 144 145 go session.Multiplex(16, 0, requests) 146 go func() { 147 for { 148 // Wait for a service request or a shutdown 149 select { 150 case <-ctx.Done(): 151 return 152 153 case request := <-requests: 154 task := <-request 155 156 task.Bitsets = make([][]byte, len(task.Sections)) 157 for i, section := range task.Sections { 158 if rand.Int()%4 != 0 { // Handle occasional missing deliveries 159 head := rawdb.ReadCanonicalHash(b.db, (section+1)*params.BloomBitsBlocks-1) 160 task.Bitsets[i], _ = rawdb.ReadBloomBits(b.db, task.Bit, section, head) 161 } 162 } 163 request <- task 164 } 165 } 166 }() 167 } 168 169 func newTestFilterSystem(t testing.TB, db ethdb.Database, cfg Config) (*testBackend, *FilterSystem) { 170 backend := &testBackend{db: db} 171 sys := NewFilterSystem(backend, cfg) 172 return backend, sys 173 } 174 175 // TestBlockSubscription tests if a block subscription returns block hashes for posted chain events. 176 // It creates multiple subscriptions: 177 // - one at the start and should receive all posted chain events and a second (blockHashes) 178 // - one that is created after a cutoff moment and uninstalled after a second cutoff moment (blockHashes[cutoff1:cutoff2]) 179 // - one that is created after the second cutoff moment (blockHashes[cutoff2:]) 180 func TestBlockSubscription(t *testing.T) { 181 t.Parallel() 182 183 var ( 184 db = rawdb.NewMemoryDatabase() 185 backend, sys = newTestFilterSystem(t, db, Config{}) 186 api = NewFilterAPI(sys, false) 187 genesis = &core.Genesis{ 188 Config: params.TestChainConfig, 189 BaseFee: big.NewInt(params.InitialBaseFee), 190 } 191 _, chain, _ = core.GenerateChainWithGenesis(genesis, ethash.NewFaker(), 10, func(i int, gen *core.BlockGen) {}) 192 chainEvents = []core.ChainEvent{} 193 ) 194 195 for _, blk := range chain { 196 chainEvents = append(chainEvents, core.ChainEvent{Hash: blk.Hash(), Block: blk}) 197 } 198 199 chan0 := make(chan *types.Header) 200 sub0 := api.events.SubscribeNewHeads(chan0) 201 chan1 := make(chan *types.Header) 202 sub1 := api.events.SubscribeNewHeads(chan1) 203 204 go func() { // simulate client 205 i1, i2 := 0, 0 206 for i1 != len(chainEvents) || i2 != len(chainEvents) { 207 select { 208 case header := <-chan0: 209 if chainEvents[i1].Hash != header.Hash() { 210 t.Errorf("sub0 received invalid hash on index %d, want %x, got %x", i1, chainEvents[i1].Hash, header.Hash()) 211 } 212 i1++ 213 case header := <-chan1: 214 if chainEvents[i2].Hash != header.Hash() { 215 t.Errorf("sub1 received invalid hash on index %d, want %x, got %x", i2, chainEvents[i2].Hash, header.Hash()) 216 } 217 i2++ 218 } 219 } 220 221 sub0.Unsubscribe() 222 sub1.Unsubscribe() 223 }() 224 225 time.Sleep(1 * time.Second) 226 for _, e := range chainEvents { 227 backend.chainFeed.Send(e) 228 } 229 230 <-sub0.Err() 231 <-sub1.Err() 232 } 233 234 // TestPendingTxFilter tests whether pending tx filters retrieve all pending transactions that are posted to the event mux. 235 func TestPendingTxFilter(t *testing.T) { 236 t.Parallel() 237 238 var ( 239 db = rawdb.NewMemoryDatabase() 240 backend, sys = newTestFilterSystem(t, db, Config{}) 241 api = NewFilterAPI(sys, false) 242 243 transactions = []*types.Transaction{ 244 types.NewTransaction(0, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil), 245 types.NewTransaction(1, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil), 246 types.NewTransaction(2, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil), 247 types.NewTransaction(3, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil), 248 types.NewTransaction(4, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil), 249 } 250 251 txs []*types.Transaction 252 ) 253 254 fid0 := api.NewPendingTransactionFilter() 255 256 time.Sleep(1 * time.Second) 257 backend.txFeed.Send(core.NewTxsEvent{Txs: transactions}) 258 259 timeout := time.Now().Add(1 * time.Second) 260 for { 261 results, err := api.GetFilterChanges(fid0) 262 if err != nil { 263 t.Fatalf("Unable to retrieve logs: %v", err) 264 } 265 266 tx := results.([]*types.Transaction) 267 txs = append(txs, tx...) 268 if len(txs) >= len(transactions) { 269 break 270 } 271 // check timeout 272 if time.Now().After(timeout) { 273 break 274 } 275 276 time.Sleep(100 * time.Millisecond) 277 } 278 279 if len(txs) != len(transactions) { 280 t.Errorf("invalid number of transactions, want %d transactions(s), got %d", len(transactions), len(txs)) 281 return 282 } 283 for i := range txs { 284 if txs[i].Hash() != transactions[i].Hash() { 285 t.Errorf("hashes[%d] invalid, want %x, got %x", i, transactions[i].Hash(), txs[i].Hash()) 286 } 287 } 288 } 289 290 // TestLogFilterCreation test whether a given filter criteria makes sense. 291 // If not it must return an error. 292 func TestLogFilterCreation(t *testing.T) { 293 var ( 294 db = rawdb.NewMemoryDatabase() 295 _, sys = newTestFilterSystem(t, db, Config{}) 296 api = NewFilterAPI(sys, false) 297 298 testCases = []struct { 299 crit FilterCriteria 300 success bool 301 }{ 302 // defaults 303 {FilterCriteria{}, true}, 304 // valid block number range 305 {FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(2)}, true}, 306 // "mined" block range to pending 307 {FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, true}, 308 // new mined and pending blocks 309 {FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())}, true}, 310 // from block "higher" than to block 311 {FilterCriteria{FromBlock: big.NewInt(2), ToBlock: big.NewInt(1)}, false}, 312 // from block "higher" than to block 313 {FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(100)}, false}, 314 // from block "higher" than to block 315 {FilterCriteria{FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(100)}, false}, 316 // from block "higher" than to block 317 {FilterCriteria{FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, false}, 318 } 319 ) 320 321 for i, test := range testCases { 322 id, err := api.NewFilter(test.crit) 323 if err != nil && test.success { 324 t.Errorf("expected filter creation for case %d to success, got %v", i, err) 325 } 326 if err == nil { 327 api.UninstallFilter(id) 328 if !test.success { 329 t.Errorf("expected testcase %d to fail with an error", i) 330 } 331 } 332 } 333 } 334 335 // TestInvalidLogFilterCreation tests whether invalid filter log criteria results in an error 336 // when the filter is created. 337 func TestInvalidLogFilterCreation(t *testing.T) { 338 t.Parallel() 339 340 var ( 341 db = rawdb.NewMemoryDatabase() 342 _, sys = newTestFilterSystem(t, db, Config{}) 343 api = NewFilterAPI(sys, false) 344 ) 345 346 // different situations where log filter creation should fail. 347 // Reason: fromBlock > toBlock 348 testCases := []FilterCriteria{ 349 0: {FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, 350 1: {FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(100)}, 351 2: {FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(100)}, 352 } 353 354 for i, test := range testCases { 355 if _, err := api.NewFilter(test); err == nil { 356 t.Errorf("Expected NewFilter for case #%d to fail", i) 357 } 358 } 359 } 360 361 func TestInvalidGetLogsRequest(t *testing.T) { 362 var ( 363 db = rawdb.NewMemoryDatabase() 364 _, sys = newTestFilterSystem(t, db, Config{}) 365 api = NewFilterAPI(sys, false) 366 blockHash = common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111") 367 ) 368 369 // Reason: Cannot specify both BlockHash and FromBlock/ToBlock) 370 testCases := []FilterCriteria{ 371 0: {BlockHash: &blockHash, FromBlock: big.NewInt(100)}, 372 1: {BlockHash: &blockHash, ToBlock: big.NewInt(500)}, 373 2: {BlockHash: &blockHash, FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, 374 } 375 376 for i, test := range testCases { 377 if _, err := api.GetLogs(context.Background(), test); err == nil { 378 t.Errorf("Expected Logs for case #%d to fail", i) 379 } 380 } 381 } 382 383 // TestLogFilter tests whether log filters match the correct logs that are posted to the event feed. 384 func TestLogFilter(t *testing.T) { 385 t.Parallel() 386 387 var ( 388 db = rawdb.NewMemoryDatabase() 389 backend, sys = newTestFilterSystem(t, db, Config{}) 390 api = NewFilterAPI(sys, false) 391 392 firstAddr = common.HexToAddress("0x1111111111111111111111111111111111111111") 393 secondAddr = common.HexToAddress("0x2222222222222222222222222222222222222222") 394 thirdAddress = common.HexToAddress("0x3333333333333333333333333333333333333333") 395 notUsedAddress = common.HexToAddress("0x9999999999999999999999999999999999999999") 396 firstTopic = common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111") 397 secondTopic = common.HexToHash("0x2222222222222222222222222222222222222222222222222222222222222222") 398 notUsedTopic = common.HexToHash("0x9999999999999999999999999999999999999999999999999999999999999999") 399 400 // posted twice, once as regular logs and once as pending logs. 401 allLogs = []*types.Log{ 402 {Address: firstAddr}, 403 {Address: firstAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 1}, 404 {Address: secondAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 1}, 405 {Address: thirdAddress, Topics: []common.Hash{secondTopic}, BlockNumber: 2}, 406 {Address: thirdAddress, Topics: []common.Hash{secondTopic}, BlockNumber: 3}, 407 } 408 409 expectedCase7 = []*types.Log{allLogs[3], allLogs[4], allLogs[0], allLogs[1], allLogs[2], allLogs[3], allLogs[4]} 410 expectedCase11 = []*types.Log{allLogs[1], allLogs[2], allLogs[1], allLogs[2]} 411 412 testCases = []struct { 413 crit FilterCriteria 414 expected []*types.Log 415 id rpc.ID 416 }{ 417 // match all 418 0: {FilterCriteria{}, allLogs, ""}, 419 // match none due to no matching addresses 420 1: {FilterCriteria{Addresses: []common.Address{{}, notUsedAddress}, Topics: [][]common.Hash{nil}}, []*types.Log{}, ""}, 421 // match logs based on addresses, ignore topics 422 2: {FilterCriteria{Addresses: []common.Address{firstAddr}}, allLogs[:2], ""}, 423 // match none due to no matching topics (match with address) 424 3: {FilterCriteria{Addresses: []common.Address{secondAddr}, Topics: [][]common.Hash{{notUsedTopic}}}, []*types.Log{}, ""}, 425 // match logs based on addresses and topics 426 4: {FilterCriteria{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, allLogs[3:5], ""}, 427 // match logs based on multiple addresses and "or" topics 428 5: {FilterCriteria{Addresses: []common.Address{secondAddr, thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, allLogs[2:5], ""}, 429 // logs in the pending block 430 6: {FilterCriteria{Addresses: []common.Address{firstAddr}, FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())}, allLogs[:2], ""}, 431 // mined logs with block num >= 2 or pending logs 432 7: {FilterCriteria{FromBlock: big.NewInt(2), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())}, expectedCase7, ""}, 433 // all "mined" logs with block num >= 2 434 8: {FilterCriteria{FromBlock: big.NewInt(2), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, allLogs[3:], ""}, 435 // all "mined" logs 436 9: {FilterCriteria{ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, allLogs, ""}, 437 // all "mined" logs with 1>= block num <=2 and topic secondTopic 438 10: {FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(2), Topics: [][]common.Hash{{secondTopic}}}, allLogs[3:4], ""}, 439 // all "mined" and pending logs with topic firstTopic 440 11: {FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), Topics: [][]common.Hash{{firstTopic}}}, expectedCase11, ""}, 441 // match all logs due to wildcard topic 442 12: {FilterCriteria{Topics: [][]common.Hash{nil}}, allLogs[1:], ""}, 443 } 444 ) 445 446 // create all filters 447 for i := range testCases { 448 testCases[i].id, _ = api.NewFilter(testCases[i].crit) 449 } 450 451 // raise events 452 time.Sleep(1 * time.Second) 453 if nsend := backend.logsFeed.Send(allLogs); nsend == 0 { 454 t.Fatal("Logs event not delivered") 455 } 456 if nsend := backend.pendingLogsFeed.Send(allLogs); nsend == 0 { 457 t.Fatal("Pending logs event not delivered") 458 } 459 460 for i, tt := range testCases { 461 var fetched []*types.Log 462 timeout := time.Now().Add(1 * time.Second) 463 for { // fetch all expected logs 464 results, err := api.GetFilterChanges(tt.id) 465 if err != nil { 466 t.Fatalf("Unable to fetch logs: %v", err) 467 } 468 469 fetched = append(fetched, results.([]*types.Log)...) 470 if len(fetched) >= len(tt.expected) { 471 break 472 } 473 // check timeout 474 if time.Now().After(timeout) { 475 break 476 } 477 478 time.Sleep(100 * time.Millisecond) 479 } 480 481 if len(fetched) != len(tt.expected) { 482 t.Errorf("invalid number of logs for case %d, want %d log(s), got %d", i, len(tt.expected), len(fetched)) 483 return 484 } 485 486 for l := range fetched { 487 if fetched[l].Removed { 488 t.Errorf("expected log not to be removed for log %d in case %d", l, i) 489 } 490 if !reflect.DeepEqual(fetched[l], tt.expected[l]) { 491 t.Errorf("invalid log on index %d for case %d", l, i) 492 } 493 } 494 } 495 } 496 497 // TestPendingLogsSubscription tests if a subscription receives the correct pending logs that are posted to the event feed. 498 func TestPendingLogsSubscription(t *testing.T) { 499 t.Parallel() 500 501 var ( 502 db = rawdb.NewMemoryDatabase() 503 backend, sys = newTestFilterSystem(t, db, Config{}) 504 api = NewFilterAPI(sys, false) 505 506 firstAddr = common.HexToAddress("0x1111111111111111111111111111111111111111") 507 secondAddr = common.HexToAddress("0x2222222222222222222222222222222222222222") 508 thirdAddress = common.HexToAddress("0x3333333333333333333333333333333333333333") 509 notUsedAddress = common.HexToAddress("0x9999999999999999999999999999999999999999") 510 firstTopic = common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111") 511 secondTopic = common.HexToHash("0x2222222222222222222222222222222222222222222222222222222222222222") 512 thirdTopic = common.HexToHash("0x3333333333333333333333333333333333333333333333333333333333333333") 513 fourthTopic = common.HexToHash("0x4444444444444444444444444444444444444444444444444444444444444444") 514 notUsedTopic = common.HexToHash("0x9999999999999999999999999999999999999999999999999999999999999999") 515 516 allLogs = [][]*types.Log{ 517 {{Address: firstAddr, Topics: []common.Hash{}, BlockNumber: 0}}, 518 {{Address: firstAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 1}}, 519 {{Address: secondAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 2}}, 520 {{Address: thirdAddress, Topics: []common.Hash{secondTopic}, BlockNumber: 3}}, 521 {{Address: thirdAddress, Topics: []common.Hash{secondTopic}, BlockNumber: 4}}, 522 { 523 {Address: thirdAddress, Topics: []common.Hash{firstTopic}, BlockNumber: 5}, 524 {Address: thirdAddress, Topics: []common.Hash{thirdTopic}, BlockNumber: 5}, 525 {Address: thirdAddress, Topics: []common.Hash{fourthTopic}, BlockNumber: 5}, 526 {Address: firstAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 5}, 527 }, 528 } 529 530 pendingBlockNumber = big.NewInt(rpc.PendingBlockNumber.Int64()) 531 532 testCases = []struct { 533 crit ethereum.FilterQuery 534 expected []*types.Log 535 c chan []*types.Log 536 sub *Subscription 537 err chan error 538 }{ 539 // match all 540 { 541 ethereum.FilterQuery{FromBlock: pendingBlockNumber, ToBlock: pendingBlockNumber}, 542 flattenLogs(allLogs), 543 nil, nil, nil, 544 }, 545 // match none due to no matching addresses 546 { 547 ethereum.FilterQuery{Addresses: []common.Address{{}, notUsedAddress}, Topics: [][]common.Hash{nil}, FromBlock: pendingBlockNumber, ToBlock: pendingBlockNumber}, 548 nil, 549 nil, nil, nil, 550 }, 551 // match logs based on addresses, ignore topics 552 { 553 ethereum.FilterQuery{Addresses: []common.Address{firstAddr}, FromBlock: pendingBlockNumber, ToBlock: pendingBlockNumber}, 554 append(flattenLogs(allLogs[:2]), allLogs[5][3]), 555 nil, nil, nil, 556 }, 557 // match none due to no matching topics (match with address) 558 { 559 ethereum.FilterQuery{Addresses: []common.Address{secondAddr}, Topics: [][]common.Hash{{notUsedTopic}}, FromBlock: pendingBlockNumber, ToBlock: pendingBlockNumber}, 560 nil, 561 nil, nil, nil, 562 }, 563 // match logs based on addresses and topics 564 { 565 ethereum.FilterQuery{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}, FromBlock: pendingBlockNumber, ToBlock: pendingBlockNumber}, 566 append(flattenLogs(allLogs[3:5]), allLogs[5][0]), 567 nil, nil, nil, 568 }, 569 // match logs based on multiple addresses and "or" topics 570 { 571 ethereum.FilterQuery{Addresses: []common.Address{secondAddr, thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}, FromBlock: pendingBlockNumber, ToBlock: pendingBlockNumber}, 572 append(flattenLogs(allLogs[2:5]), allLogs[5][0]), 573 nil, nil, nil, 574 }, 575 // multiple pending logs, should match only 2 topics from the logs in block 5 576 { 577 ethereum.FilterQuery{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, fourthTopic}}, FromBlock: pendingBlockNumber, ToBlock: pendingBlockNumber}, 578 []*types.Log{allLogs[5][0], allLogs[5][2]}, 579 nil, nil, nil, 580 }, 581 // match none due to only matching new mined logs 582 { 583 ethereum.FilterQuery{}, 584 nil, 585 nil, nil, nil, 586 }, 587 // match none due to only matching mined logs within a specific block range 588 { 589 ethereum.FilterQuery{FromBlock: big.NewInt(1), ToBlock: big.NewInt(2)}, 590 nil, 591 nil, nil, nil, 592 }, 593 // match all due to matching mined and pending logs 594 { 595 ethereum.FilterQuery{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())}, 596 flattenLogs(allLogs), 597 nil, nil, nil, 598 }, 599 // match none due to matching logs from a specific block number to new mined blocks 600 { 601 ethereum.FilterQuery{FromBlock: big.NewInt(1), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, 602 nil, 603 nil, nil, nil, 604 }, 605 } 606 ) 607 608 // create all subscriptions, this ensures all subscriptions are created before the events are posted. 609 // on slow machines this could otherwise lead to missing events when the subscription is created after 610 // (some) events are posted. 611 for i := range testCases { 612 testCases[i].c = make(chan []*types.Log) 613 testCases[i].err = make(chan error, 1) 614 615 var err error 616 testCases[i].sub, err = api.events.SubscribeLogs(testCases[i].crit, testCases[i].c) 617 if err != nil { 618 t.Fatalf("SubscribeLogs %d failed: %v\n", i, err) 619 } 620 } 621 622 for n, test := range testCases { 623 i := n 624 tt := test 625 go func() { 626 defer tt.sub.Unsubscribe() 627 628 var fetched []*types.Log 629 630 timeout := time.After(1 * time.Second) 631 fetchLoop: 632 for { 633 select { 634 case logs := <-tt.c: 635 // Do not break early if we've fetched greater, or equal, 636 // to the number of logs expected. This ensures we do not 637 // deadlock the filter system because it will do a blocking 638 // send on this channel if another log arrives. 639 fetched = append(fetched, logs...) 640 case <-timeout: 641 break fetchLoop 642 } 643 } 644 645 if len(fetched) != len(tt.expected) { 646 tt.err <- fmt.Errorf("invalid number of logs for case %d, want %d log(s), got %d", i, len(tt.expected), len(fetched)) 647 return 648 } 649 650 for l := range fetched { 651 if fetched[l].Removed { 652 tt.err <- fmt.Errorf("expected log not to be removed for log %d in case %d", l, i) 653 return 654 } 655 if !reflect.DeepEqual(fetched[l], tt.expected[l]) { 656 tt.err <- fmt.Errorf("invalid log on index %d for case %d\n", l, i) 657 return 658 } 659 } 660 tt.err <- nil 661 }() 662 } 663 664 // raise events 665 for _, ev := range allLogs { 666 backend.pendingLogsFeed.Send(ev) 667 } 668 669 for i := range testCases { 670 err := <-testCases[i].err 671 if err != nil { 672 t.Fatalf("test %d failed: %v", i, err) 673 } 674 <-testCases[i].sub.Err() 675 } 676 } 677 678 // TestPendingTxFilterDeadlock tests if the event loop hangs when pending 679 // txes arrive at the same time that one of multiple filters is timing out. 680 // Please refer to #22131 for more details. 681 func TestPendingTxFilterDeadlock(t *testing.T) { 682 t.Parallel() 683 timeout := 100 * time.Millisecond 684 685 var ( 686 db = rawdb.NewMemoryDatabase() 687 backend, sys = newTestFilterSystem(t, db, Config{Timeout: timeout}) 688 api = NewFilterAPI(sys, false) 689 done = make(chan struct{}) 690 ) 691 692 go func() { 693 // Bombard feed with txes until signal was received to stop 694 i := uint64(0) 695 for { 696 select { 697 case <-done: 698 return 699 default: 700 } 701 702 tx := types.NewTransaction(i, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil) 703 backend.txFeed.Send(core.NewTxsEvent{Txs: []*types.Transaction{tx}}) 704 i++ 705 } 706 }() 707 708 // Create a bunch of filters that will 709 // timeout either in 100ms or 200ms 710 fids := make([]rpc.ID, 20) 711 for i := 0; i < len(fids); i++ { 712 fid := api.NewPendingTransactionFilter() 713 fids[i] = fid 714 // Wait for at least one tx to arrive in filter 715 for { 716 txs, err := api.GetFilterChanges(fid) 717 if err != nil { 718 t.Fatalf("Filter should exist: %v\n", err) 719 } 720 if len(txs.([]*types.Transaction)) > 0 { 721 break 722 } 723 runtime.Gosched() 724 } 725 } 726 727 // Wait until filters have timed out 728 time.Sleep(3 * timeout) 729 730 // If tx loop doesn't consume `done` after a second 731 // it's hanging. 732 select { 733 case done <- struct{}{}: 734 // Check that all filters have been uninstalled 735 for _, fid := range fids { 736 if _, err := api.GetFilterChanges(fid); err == nil { 737 t.Errorf("Filter %s should have been uninstalled\n", fid) 738 } 739 } 740 case <-time.After(1 * time.Second): 741 t.Error("Tx sending loop hangs") 742 } 743 } 744 745 func flattenLogs(pl [][]*types.Log) []*types.Log { 746 var logs []*types.Log 747 for _, l := range pl { 748 logs = append(logs, l...) 749 } 750 return logs 751 }