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