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