github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/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/neatlab/neatio"
    13  	"github.com/neatlab/neatio/chain/core/rawdb"
    14  
    15  	"github.com/neatlab/neatio/chain/core"
    16  	"github.com/neatlab/neatio/chain/core/bloombits"
    17  	"github.com/neatlab/neatio/chain/core/types"
    18  	"github.com/neatlab/neatio/neatdb"
    19  	"github.com/neatlab/neatio/network/rpc"
    20  	"github.com/neatlab/neatio/params"
    21  	"github.com/neatlab/neatio/utilities/common"
    22  	"github.com/neatlab/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  }