github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/eth/filters/filter_system_test.go (about)

     1  // This file is part of the go-sberex library. The go-sberex library is 
     2  // free software: you can redistribute it and/or modify it under the terms 
     3  // of the GNU Lesser General Public License as published by the Free 
     4  // Software Foundation, either version 3 of the License, or (at your option)
     5  // any later version.
     6  //
     7  // The go-sberex library is distributed in the hope that it will be useful, 
     8  // but WITHOUT ANY WARRANTY; without even the implied warranty of
     9  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 
    10  // General Public License <http://www.gnu.org/licenses/> for more details.
    11  
    12  package filters
    13  
    14  import (
    15  	"context"
    16  	"fmt"
    17  	"math/big"
    18  	"math/rand"
    19  	"reflect"
    20  	"testing"
    21  	"time"
    22  
    23  	sberex "github.com/Sberex/go-sberex"
    24  	"github.com/Sberex/go-sberex/common"
    25  	"github.com/Sberex/go-sberex/consensus/ethash"
    26  	"github.com/Sberex/go-sberex/core"
    27  	"github.com/Sberex/go-sberex/core/bloombits"
    28  	"github.com/Sberex/go-sberex/core/types"
    29  	"github.com/Sberex/go-sberex/ethdb"
    30  	"github.com/Sberex/go-sberex/event"
    31  	"github.com/Sberex/go-sberex/params"
    32  	"github.com/Sberex/go-sberex/rpc"
    33  )
    34  
    35  type testBackend struct {
    36  	mux        *event.TypeMux
    37  	db         ethdb.Database
    38  	sections   uint64
    39  	txFeed     *event.Feed
    40  	rmLogsFeed *event.Feed
    41  	logsFeed   *event.Feed
    42  	chainFeed  *event.Feed
    43  }
    44  
    45  func (b *testBackend) ChainDb() ethdb.Database {
    46  	return b.db
    47  }
    48  
    49  func (b *testBackend) EventMux() *event.TypeMux {
    50  	return b.mux
    51  }
    52  
    53  func (b *testBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) {
    54  	var hash common.Hash
    55  	var num uint64
    56  	if blockNr == rpc.LatestBlockNumber {
    57  		hash = core.GetHeadBlockHash(b.db)
    58  		num = core.GetBlockNumber(b.db, hash)
    59  	} else {
    60  		num = uint64(blockNr)
    61  		hash = core.GetCanonicalHash(b.db, num)
    62  	}
    63  	return core.GetHeader(b.db, hash, num), nil
    64  }
    65  
    66  func (b *testBackend) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) {
    67  	number := core.GetBlockNumber(b.db, blockHash)
    68  	return core.GetBlockReceipts(b.db, blockHash, number), nil
    69  }
    70  
    71  func (b *testBackend) GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error) {
    72  	number := core.GetBlockNumber(b.db, blockHash)
    73  	receipts := core.GetBlockReceipts(b.db, blockHash, number)
    74  
    75  	logs := make([][]*types.Log, len(receipts))
    76  	for i, receipt := range receipts {
    77  		logs[i] = receipt.Logs
    78  	}
    79  	return logs, nil
    80  }
    81  
    82  func (b *testBackend) SubscribeTxPreEvent(ch chan<- core.TxPreEvent) event.Subscription {
    83  	return b.txFeed.Subscribe(ch)
    84  }
    85  
    86  func (b *testBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
    87  	return b.rmLogsFeed.Subscribe(ch)
    88  }
    89  
    90  func (b *testBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
    91  	return b.logsFeed.Subscribe(ch)
    92  }
    93  
    94  func (b *testBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
    95  	return b.chainFeed.Subscribe(ch)
    96  }
    97  
    98  func (b *testBackend) BloomStatus() (uint64, uint64) {
    99  	return params.BloomBitsBlocks, b.sections
   100  }
   101  
   102  func (b *testBackend) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) {
   103  	requests := make(chan chan *bloombits.Retrieval)
   104  
   105  	go session.Multiplex(16, 0, requests)
   106  	go func() {
   107  		for {
   108  			// Wait for a service request or a shutdown
   109  			select {
   110  			case <-ctx.Done():
   111  				return
   112  
   113  			case request := <-requests:
   114  				task := <-request
   115  
   116  				task.Bitsets = make([][]byte, len(task.Sections))
   117  				for i, section := range task.Sections {
   118  					if rand.Int()%4 != 0 { // Handle occasional missing deliveries
   119  						head := core.GetCanonicalHash(b.db, (section+1)*params.BloomBitsBlocks-1)
   120  						task.Bitsets[i], _ = core.GetBloomBits(b.db, task.Bit, section, head)
   121  					}
   122  				}
   123  				request <- task
   124  			}
   125  		}
   126  	}()
   127  }
   128  
   129  // TestBlockSubscription tests if a block subscription returns block hashes for posted chain events.
   130  // It creates multiple subscriptions:
   131  // - one at the start and should receive all posted chain events and a second (blockHashes)
   132  // - one that is created after a cutoff moment and uninstalled after a second cutoff moment (blockHashes[cutoff1:cutoff2])
   133  // - one that is created after the second cutoff moment (blockHashes[cutoff2:])
   134  func TestBlockSubscription(t *testing.T) {
   135  	t.Parallel()
   136  
   137  	var (
   138  		mux         = new(event.TypeMux)
   139  		db, _       = ethdb.NewMemDatabase()
   140  		txFeed      = new(event.Feed)
   141  		rmLogsFeed  = new(event.Feed)
   142  		logsFeed    = new(event.Feed)
   143  		chainFeed   = new(event.Feed)
   144  		backend     = &testBackend{mux, db, 0, txFeed, rmLogsFeed, logsFeed, chainFeed}
   145  		api         = NewPublicFilterAPI(backend, false)
   146  		genesis     = new(core.Genesis).MustCommit(db)
   147  		chain, _    = core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 10, func(i int, gen *core.BlockGen) {})
   148  		chainEvents = []core.ChainEvent{}
   149  	)
   150  
   151  	for _, blk := range chain {
   152  		chainEvents = append(chainEvents, core.ChainEvent{Hash: blk.Hash(), Block: blk})
   153  	}
   154  
   155  	chan0 := make(chan *types.Header)
   156  	sub0 := api.events.SubscribeNewHeads(chan0)
   157  	chan1 := make(chan *types.Header)
   158  	sub1 := api.events.SubscribeNewHeads(chan1)
   159  
   160  	go func() { // simulate client
   161  		i1, i2 := 0, 0
   162  		for i1 != len(chainEvents) || i2 != len(chainEvents) {
   163  			select {
   164  			case header := <-chan0:
   165  				if chainEvents[i1].Hash != header.Hash() {
   166  					t.Errorf("sub0 received invalid hash on index %d, want %x, got %x", i1, chainEvents[i1].Hash, header.Hash())
   167  				}
   168  				i1++
   169  			case header := <-chan1:
   170  				if chainEvents[i2].Hash != header.Hash() {
   171  					t.Errorf("sub1 received invalid hash on index %d, want %x, got %x", i2, chainEvents[i2].Hash, header.Hash())
   172  				}
   173  				i2++
   174  			}
   175  		}
   176  
   177  		sub0.Unsubscribe()
   178  		sub1.Unsubscribe()
   179  	}()
   180  
   181  	time.Sleep(1 * time.Second)
   182  	for _, e := range chainEvents {
   183  		chainFeed.Send(e)
   184  	}
   185  
   186  	<-sub0.Err()
   187  	<-sub1.Err()
   188  }
   189  
   190  // TestPendingTxFilter tests whether pending tx filters retrieve all pending transactions that are posted to the event mux.
   191  func TestPendingTxFilter(t *testing.T) {
   192  	t.Parallel()
   193  
   194  	var (
   195  		mux        = new(event.TypeMux)
   196  		db, _      = ethdb.NewMemDatabase()
   197  		txFeed     = new(event.Feed)
   198  		rmLogsFeed = new(event.Feed)
   199  		logsFeed   = new(event.Feed)
   200  		chainFeed  = new(event.Feed)
   201  		backend    = &testBackend{mux, db, 0, txFeed, rmLogsFeed, logsFeed, chainFeed}
   202  		api        = NewPublicFilterAPI(backend, false)
   203  
   204  		transactions = []*types.Transaction{
   205  			types.NewTransaction(0, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil),
   206  			types.NewTransaction(1, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil),
   207  			types.NewTransaction(2, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil),
   208  			types.NewTransaction(3, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil),
   209  			types.NewTransaction(4, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil),
   210  		}
   211  
   212  		hashes []common.Hash
   213  	)
   214  
   215  	fid0 := api.NewPendingTransactionFilter()
   216  
   217  	time.Sleep(1 * time.Second)
   218  	for _, tx := range transactions {
   219  		ev := core.TxPreEvent{Tx: tx}
   220  		txFeed.Send(ev)
   221  	}
   222  
   223  	timeout := time.Now().Add(1 * time.Second)
   224  	for {
   225  		results, err := api.GetFilterChanges(fid0)
   226  		if err != nil {
   227  			t.Fatalf("Unable to retrieve logs: %v", err)
   228  		}
   229  
   230  		h := results.([]common.Hash)
   231  		hashes = append(hashes, h...)
   232  		if len(hashes) >= len(transactions) {
   233  			break
   234  		}
   235  		// check timeout
   236  		if time.Now().After(timeout) {
   237  			break
   238  		}
   239  
   240  		time.Sleep(100 * time.Millisecond)
   241  	}
   242  
   243  	if len(hashes) != len(transactions) {
   244  		t.Errorf("invalid number of transactions, want %d transactions(s), got %d", len(transactions), len(hashes))
   245  		return
   246  	}
   247  	for i := range hashes {
   248  		if hashes[i] != transactions[i].Hash() {
   249  			t.Errorf("hashes[%d] invalid, want %x, got %x", i, transactions[i].Hash(), hashes[i])
   250  		}
   251  	}
   252  }
   253  
   254  // TestLogFilterCreation test whether a given filter criteria makes sense.
   255  // If not it must return an error.
   256  func TestLogFilterCreation(t *testing.T) {
   257  	var (
   258  		mux        = new(event.TypeMux)
   259  		db, _      = ethdb.NewMemDatabase()
   260  		txFeed     = new(event.Feed)
   261  		rmLogsFeed = new(event.Feed)
   262  		logsFeed   = new(event.Feed)
   263  		chainFeed  = new(event.Feed)
   264  		backend    = &testBackend{mux, db, 0, txFeed, rmLogsFeed, logsFeed, chainFeed}
   265  		api        = NewPublicFilterAPI(backend, false)
   266  
   267  		testCases = []struct {
   268  			crit    FilterCriteria
   269  			success bool
   270  		}{
   271  			// defaults
   272  			{FilterCriteria{}, true},
   273  			// valid block number range
   274  			{FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(2)}, true},
   275  			// "mined" block range to pending
   276  			{FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, true},
   277  			// new mined and pending blocks
   278  			{FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())}, true},
   279  			// from block "higher" than to block
   280  			{FilterCriteria{FromBlock: big.NewInt(2), ToBlock: big.NewInt(1)}, false},
   281  			// from block "higher" than to block
   282  			{FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(100)}, false},
   283  			// from block "higher" than to block
   284  			{FilterCriteria{FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(100)}, false},
   285  			// from block "higher" than to block
   286  			{FilterCriteria{FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, false},
   287  		}
   288  	)
   289  
   290  	for i, test := range testCases {
   291  		_, err := api.NewFilter(test.crit)
   292  		if test.success && err != nil {
   293  			t.Errorf("expected filter creation for case %d to success, got %v", i, err)
   294  		}
   295  		if !test.success && err == nil {
   296  			t.Errorf("expected testcase %d to fail with an error", i)
   297  		}
   298  	}
   299  }
   300  
   301  // TestInvalidLogFilterCreation tests whether invalid filter log criteria results in an error
   302  // when the filter is created.
   303  func TestInvalidLogFilterCreation(t *testing.T) {
   304  	t.Parallel()
   305  
   306  	var (
   307  		mux        = new(event.TypeMux)
   308  		db, _      = ethdb.NewMemDatabase()
   309  		txFeed     = new(event.Feed)
   310  		rmLogsFeed = new(event.Feed)
   311  		logsFeed   = new(event.Feed)
   312  		chainFeed  = new(event.Feed)
   313  		backend    = &testBackend{mux, db, 0, txFeed, rmLogsFeed, logsFeed, chainFeed}
   314  		api        = NewPublicFilterAPI(backend, false)
   315  	)
   316  
   317  	// different situations where log filter creation should fail.
   318  	// Reason: fromBlock > toBlock
   319  	testCases := []FilterCriteria{
   320  		0: {FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())},
   321  		1: {FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(100)},
   322  		2: {FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(100)},
   323  	}
   324  
   325  	for i, test := range testCases {
   326  		if _, err := api.NewFilter(test); err == nil {
   327  			t.Errorf("Expected NewFilter for case #%d to fail", i)
   328  		}
   329  	}
   330  }
   331  
   332  // TestLogFilter tests whether log filters match the correct logs that are posted to the event feed.
   333  func TestLogFilter(t *testing.T) {
   334  	t.Parallel()
   335  
   336  	var (
   337  		mux        = new(event.TypeMux)
   338  		db, _      = ethdb.NewMemDatabase()
   339  		txFeed     = new(event.Feed)
   340  		rmLogsFeed = new(event.Feed)
   341  		logsFeed   = new(event.Feed)
   342  		chainFeed  = new(event.Feed)
   343  		backend    = &testBackend{mux, db, 0, txFeed, rmLogsFeed, logsFeed, chainFeed}
   344  		api        = NewPublicFilterAPI(backend, false)
   345  
   346  		firstAddr      = common.HexToAddress("0x1111111111111111111111111111111111111111")
   347  		secondAddr     = common.HexToAddress("0x2222222222222222222222222222222222222222")
   348  		thirdAddress   = common.HexToAddress("0x3333333333333333333333333333333333333333")
   349  		notUsedAddress = common.HexToAddress("0x9999999999999999999999999999999999999999")
   350  		firstTopic     = common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111")
   351  		secondTopic    = common.HexToHash("0x2222222222222222222222222222222222222222222222222222222222222222")
   352  		notUsedTopic   = common.HexToHash("0x9999999999999999999999999999999999999999999999999999999999999999")
   353  
   354  		// posted twice, once as vm.Logs and once as core.PendingLogsEvent
   355  		allLogs = []*types.Log{
   356  			{Address: firstAddr},
   357  			{Address: firstAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 1},
   358  			{Address: secondAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 1},
   359  			{Address: thirdAddress, Topics: []common.Hash{secondTopic}, BlockNumber: 2},
   360  			{Address: thirdAddress, Topics: []common.Hash{secondTopic}, BlockNumber: 3},
   361  		}
   362  
   363  		expectedCase7  = []*types.Log{allLogs[3], allLogs[4], allLogs[0], allLogs[1], allLogs[2], allLogs[3], allLogs[4]}
   364  		expectedCase11 = []*types.Log{allLogs[1], allLogs[2], allLogs[1], allLogs[2]}
   365  
   366  		testCases = []struct {
   367  			crit     FilterCriteria
   368  			expected []*types.Log
   369  			id       rpc.ID
   370  		}{
   371  			// match all
   372  			0: {FilterCriteria{}, allLogs, ""},
   373  			// match none due to no matching addresses
   374  			1: {FilterCriteria{Addresses: []common.Address{{}, notUsedAddress}, Topics: [][]common.Hash{nil}}, []*types.Log{}, ""},
   375  			// match logs based on addresses, ignore topics
   376  			2: {FilterCriteria{Addresses: []common.Address{firstAddr}}, allLogs[:2], ""},
   377  			// match none due to no matching topics (match with address)
   378  			3: {FilterCriteria{Addresses: []common.Address{secondAddr}, Topics: [][]common.Hash{{notUsedTopic}}}, []*types.Log{}, ""},
   379  			// match logs based on addresses and topics
   380  			4: {FilterCriteria{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, allLogs[3:5], ""},
   381  			// match logs based on multiple addresses and "or" topics
   382  			5: {FilterCriteria{Addresses: []common.Address{secondAddr, thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, allLogs[2:5], ""},
   383  			// logs in the pending block
   384  			6: {FilterCriteria{Addresses: []common.Address{firstAddr}, FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())}, allLogs[:2], ""},
   385  			// mined logs with block num >= 2 or pending logs
   386  			7: {FilterCriteria{FromBlock: big.NewInt(2), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())}, expectedCase7, ""},
   387  			// all "mined" logs with block num >= 2
   388  			8: {FilterCriteria{FromBlock: big.NewInt(2), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, allLogs[3:], ""},
   389  			// all "mined" logs
   390  			9: {FilterCriteria{ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, allLogs, ""},
   391  			// all "mined" logs with 1>= block num <=2 and topic secondTopic
   392  			10: {FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(2), Topics: [][]common.Hash{{secondTopic}}}, allLogs[3:4], ""},
   393  			// all "mined" and pending logs with topic firstTopic
   394  			11: {FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), Topics: [][]common.Hash{{firstTopic}}}, expectedCase11, ""},
   395  			// match all logs due to wildcard topic
   396  			12: {FilterCriteria{Topics: [][]common.Hash{nil}}, allLogs[1:], ""},
   397  		}
   398  	)
   399  
   400  	// create all filters
   401  	for i := range testCases {
   402  		testCases[i].id, _ = api.NewFilter(testCases[i].crit)
   403  	}
   404  
   405  	// raise events
   406  	time.Sleep(1 * time.Second)
   407  	if nsend := logsFeed.Send(allLogs); nsend == 0 {
   408  		t.Fatal("Shoud have at least one subscription")
   409  	}
   410  	if err := mux.Post(core.PendingLogsEvent{Logs: allLogs}); err != nil {
   411  		t.Fatal(err)
   412  	}
   413  
   414  	for i, tt := range testCases {
   415  		var fetched []*types.Log
   416  		timeout := time.Now().Add(1 * time.Second)
   417  		for { // fetch all expected logs
   418  			results, err := api.GetFilterChanges(tt.id)
   419  			if err != nil {
   420  				t.Fatalf("Unable to fetch logs: %v", err)
   421  			}
   422  
   423  			fetched = append(fetched, results.([]*types.Log)...)
   424  			if len(fetched) >= len(tt.expected) {
   425  				break
   426  			}
   427  			// check timeout
   428  			if time.Now().After(timeout) {
   429  				break
   430  			}
   431  
   432  			time.Sleep(100 * time.Millisecond)
   433  		}
   434  
   435  		if len(fetched) != len(tt.expected) {
   436  			t.Errorf("invalid number of logs for case %d, want %d log(s), got %d", i, len(tt.expected), len(fetched))
   437  			return
   438  		}
   439  
   440  		for l := range fetched {
   441  			if fetched[l].Removed {
   442  				t.Errorf("expected log not to be removed for log %d in case %d", l, i)
   443  			}
   444  			if !reflect.DeepEqual(fetched[l], tt.expected[l]) {
   445  				t.Errorf("invalid log on index %d for case %d", l, i)
   446  			}
   447  		}
   448  	}
   449  }
   450  
   451  // TestPendingLogsSubscription tests if a subscription receives the correct pending logs that are posted to the event feed.
   452  func TestPendingLogsSubscription(t *testing.T) {
   453  	t.Parallel()
   454  
   455  	var (
   456  		mux        = new(event.TypeMux)
   457  		db, _      = ethdb.NewMemDatabase()
   458  		txFeed     = new(event.Feed)
   459  		rmLogsFeed = new(event.Feed)
   460  		logsFeed   = new(event.Feed)
   461  		chainFeed  = new(event.Feed)
   462  		backend    = &testBackend{mux, db, 0, txFeed, rmLogsFeed, logsFeed, chainFeed}
   463  		api        = NewPublicFilterAPI(backend, false)
   464  
   465  		firstAddr      = common.HexToAddress("0x1111111111111111111111111111111111111111")
   466  		secondAddr     = common.HexToAddress("0x2222222222222222222222222222222222222222")
   467  		thirdAddress   = common.HexToAddress("0x3333333333333333333333333333333333333333")
   468  		notUsedAddress = common.HexToAddress("0x9999999999999999999999999999999999999999")
   469  		firstTopic     = common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111")
   470  		secondTopic    = common.HexToHash("0x2222222222222222222222222222222222222222222222222222222222222222")
   471  		thirdTopic     = common.HexToHash("0x3333333333333333333333333333333333333333333333333333333333333333")
   472  		fourthTopic    = common.HexToHash("0x4444444444444444444444444444444444444444444444444444444444444444")
   473  		notUsedTopic   = common.HexToHash("0x9999999999999999999999999999999999999999999999999999999999999999")
   474  
   475  		allLogs = []core.PendingLogsEvent{
   476  			{Logs: []*types.Log{{Address: firstAddr, Topics: []common.Hash{}, BlockNumber: 0}}},
   477  			{Logs: []*types.Log{{Address: firstAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 1}}},
   478  			{Logs: []*types.Log{{Address: secondAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 2}}},
   479  			{Logs: []*types.Log{{Address: thirdAddress, Topics: []common.Hash{secondTopic}, BlockNumber: 3}}},
   480  			{Logs: []*types.Log{{Address: thirdAddress, Topics: []common.Hash{secondTopic}, BlockNumber: 4}}},
   481  			{Logs: []*types.Log{
   482  				{Address: thirdAddress, Topics: []common.Hash{firstTopic}, BlockNumber: 5},
   483  				{Address: thirdAddress, Topics: []common.Hash{thirdTopic}, BlockNumber: 5},
   484  				{Address: thirdAddress, Topics: []common.Hash{fourthTopic}, BlockNumber: 5},
   485  				{Address: firstAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 5},
   486  			}},
   487  		}
   488  
   489  		convertLogs = func(pl []core.PendingLogsEvent) []*types.Log {
   490  			var logs []*types.Log
   491  			for _, l := range pl {
   492  				logs = append(logs, l.Logs...)
   493  			}
   494  			return logs
   495  		}
   496  
   497  		testCases = []struct {
   498  			crit     sberex.FilterQuery
   499  			expected []*types.Log
   500  			c        chan []*types.Log
   501  			sub      *Subscription
   502  		}{
   503  			// match all
   504  			{sberex.FilterQuery{}, convertLogs(allLogs), nil, nil},
   505  			// match none due to no matching addresses
   506  			{sberex.FilterQuery{Addresses: []common.Address{{}, notUsedAddress}, Topics: [][]common.Hash{nil}}, []*types.Log{}, nil, nil},
   507  			// match logs based on addresses, ignore topics
   508  			{sberex.FilterQuery{Addresses: []common.Address{firstAddr}}, append(convertLogs(allLogs[:2]), allLogs[5].Logs[3]), nil, nil},
   509  			// match none due to no matching topics (match with address)
   510  			{sberex.FilterQuery{Addresses: []common.Address{secondAddr}, Topics: [][]common.Hash{{notUsedTopic}}}, []*types.Log{}, nil, nil},
   511  			// match logs based on addresses and topics
   512  			{sberex.FilterQuery{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, append(convertLogs(allLogs[3:5]), allLogs[5].Logs[0]), nil, nil},
   513  			// match logs based on multiple addresses and "or" topics
   514  			{sberex.FilterQuery{Addresses: []common.Address{secondAddr, thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, append(convertLogs(allLogs[2:5]), allLogs[5].Logs[0]), nil, nil},
   515  			// block numbers are ignored for filters created with New***Filter, these return all logs that match the given criteria when the state changes
   516  			{sberex.FilterQuery{Addresses: []common.Address{firstAddr}, FromBlock: big.NewInt(2), ToBlock: big.NewInt(3)}, append(convertLogs(allLogs[:2]), allLogs[5].Logs[3]), nil, nil},
   517  			// multiple pending logs, should match only 2 topics from the logs in block 5
   518  			{sberex.FilterQuery{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, fourthTopic}}}, []*types.Log{allLogs[5].Logs[0], allLogs[5].Logs[2]}, nil, nil},
   519  		}
   520  	)
   521  
   522  	// create all subscriptions, this ensures all subscriptions are created before the events are posted.
   523  	// on slow machines this could otherwise lead to missing events when the subscription is created after
   524  	// (some) events are posted.
   525  	for i := range testCases {
   526  		testCases[i].c = make(chan []*types.Log)
   527  		testCases[i].sub, _ = api.events.SubscribeLogs(testCases[i].crit, testCases[i].c)
   528  	}
   529  
   530  	for n, test := range testCases {
   531  		i := n
   532  		tt := test
   533  		go func() {
   534  			var fetched []*types.Log
   535  		fetchLoop:
   536  			for {
   537  				logs := <-tt.c
   538  				fetched = append(fetched, logs...)
   539  				if len(fetched) >= len(tt.expected) {
   540  					break fetchLoop
   541  				}
   542  			}
   543  
   544  			if len(fetched) != len(tt.expected) {
   545  				panic(fmt.Sprintf("invalid number of logs for case %d, want %d log(s), got %d", i, len(tt.expected), len(fetched)))
   546  			}
   547  
   548  			for l := range fetched {
   549  				if fetched[l].Removed {
   550  					panic(fmt.Sprintf("expected log not to be removed for log %d in case %d", l, i))
   551  				}
   552  				if !reflect.DeepEqual(fetched[l], tt.expected[l]) {
   553  					panic(fmt.Sprintf("invalid log on index %d for case %d", l, i))
   554  				}
   555  			}
   556  		}()
   557  	}
   558  
   559  	// raise events
   560  	time.Sleep(1 * time.Second)
   561  	// allLogs are type of core.PendingLogsEvent
   562  	for _, l := range allLogs {
   563  		if err := mux.Post(l); err != nil {
   564  			t.Fatal(err)
   565  		}
   566  	}
   567  }