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