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