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