github.com/myafeier/go-ethereum@v1.6.8-0.20170719123245-3e0dbe0eaa72/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  	"math/big"
    22  	"reflect"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/ethereum/go-ethereum/common"
    27  	"github.com/ethereum/go-ethereum/core"
    28  	"github.com/ethereum/go-ethereum/core/types"
    29  	"github.com/ethereum/go-ethereum/ethdb"
    30  	"github.com/ethereum/go-ethereum/event"
    31  	"github.com/ethereum/go-ethereum/params"
    32  	"github.com/ethereum/go-ethereum/rpc"
    33  )
    34  
    35  type testBackend struct {
    36  	mux *event.TypeMux
    37  	db  ethdb.Database
    38  }
    39  
    40  func (b *testBackend) ChainDb() ethdb.Database {
    41  	return b.db
    42  }
    43  
    44  func (b *testBackend) EventMux() *event.TypeMux {
    45  	return b.mux
    46  }
    47  
    48  func (b *testBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) {
    49  	var hash common.Hash
    50  	var num uint64
    51  	if blockNr == rpc.LatestBlockNumber {
    52  		hash = core.GetHeadBlockHash(b.db)
    53  		num = core.GetBlockNumber(b.db, hash)
    54  	} else {
    55  		num = uint64(blockNr)
    56  		hash = core.GetCanonicalHash(b.db, num)
    57  	}
    58  	return core.GetHeader(b.db, hash, num), nil
    59  }
    60  
    61  func (b *testBackend) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) {
    62  	num := core.GetBlockNumber(b.db, blockHash)
    63  	return core.GetBlockReceipts(b.db, blockHash, num), nil
    64  }
    65  
    66  // TestBlockSubscription tests if a block subscription returns block hashes for posted chain events.
    67  // It creates multiple subscriptions:
    68  // - one at the start and should receive all posted chain events and a second (blockHashes)
    69  // - one that is created after a cutoff moment and uninstalled after a second cutoff moment (blockHashes[cutoff1:cutoff2])
    70  // - one that is created after the second cutoff moment (blockHashes[cutoff2:])
    71  func TestBlockSubscription(t *testing.T) {
    72  	t.Parallel()
    73  
    74  	var (
    75  		mux         = new(event.TypeMux)
    76  		db, _       = ethdb.NewMemDatabase()
    77  		backend     = &testBackend{mux, db}
    78  		api         = NewPublicFilterAPI(backend, false)
    79  		genesis     = new(core.Genesis).MustCommit(db)
    80  		chain, _    = core.GenerateChain(params.TestChainConfig, genesis, db, 10, func(i int, gen *core.BlockGen) {})
    81  		chainEvents = []core.ChainEvent{}
    82  	)
    83  
    84  	for _, blk := range chain {
    85  		chainEvents = append(chainEvents, core.ChainEvent{Hash: blk.Hash(), Block: blk})
    86  	}
    87  
    88  	chan0 := make(chan *types.Header)
    89  	sub0 := api.events.SubscribeNewHeads(chan0)
    90  	chan1 := make(chan *types.Header)
    91  	sub1 := api.events.SubscribeNewHeads(chan1)
    92  
    93  	go func() { // simulate client
    94  		i1, i2 := 0, 0
    95  		for i1 != len(chainEvents) || i2 != len(chainEvents) {
    96  			select {
    97  			case header := <-chan0:
    98  				if chainEvents[i1].Hash != header.Hash() {
    99  					t.Errorf("sub0 received invalid hash on index %d, want %x, got %x", i1, chainEvents[i1].Hash, header.Hash())
   100  				}
   101  				i1++
   102  			case header := <-chan1:
   103  				if chainEvents[i2].Hash != header.Hash() {
   104  					t.Errorf("sub1 received invalid hash on index %d, want %x, got %x", i2, chainEvents[i2].Hash, header.Hash())
   105  				}
   106  				i2++
   107  			}
   108  		}
   109  
   110  		sub0.Unsubscribe()
   111  		sub1.Unsubscribe()
   112  	}()
   113  
   114  	time.Sleep(1 * time.Second)
   115  	for _, e := range chainEvents {
   116  		mux.Post(e)
   117  	}
   118  
   119  	<-sub0.Err()
   120  	<-sub1.Err()
   121  }
   122  
   123  // TestPendingTxFilter tests whether pending tx filters retrieve all pending transactions that are posted to the event mux.
   124  func TestPendingTxFilter(t *testing.T) {
   125  	t.Parallel()
   126  
   127  	var (
   128  		mux     = new(event.TypeMux)
   129  		db, _   = ethdb.NewMemDatabase()
   130  		backend = &testBackend{mux, db}
   131  		api     = NewPublicFilterAPI(backend, false)
   132  
   133  		transactions = []*types.Transaction{
   134  			types.NewTransaction(0, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), new(big.Int), new(big.Int), nil),
   135  			types.NewTransaction(1, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), new(big.Int), new(big.Int), nil),
   136  			types.NewTransaction(2, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), new(big.Int), new(big.Int), nil),
   137  			types.NewTransaction(3, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), new(big.Int), new(big.Int), nil),
   138  			types.NewTransaction(4, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), new(big.Int), new(big.Int), nil),
   139  		}
   140  
   141  		hashes []common.Hash
   142  	)
   143  
   144  	fid0 := api.NewPendingTransactionFilter()
   145  
   146  	time.Sleep(1 * time.Second)
   147  	for _, tx := range transactions {
   148  		ev := core.TxPreEvent{Tx: tx}
   149  		mux.Post(ev)
   150  	}
   151  
   152  	for {
   153  		results, err := api.GetFilterChanges(fid0)
   154  		if err != nil {
   155  			t.Fatalf("Unable to retrieve logs: %v", err)
   156  		}
   157  
   158  		h := results.([]common.Hash)
   159  		hashes = append(hashes, h...)
   160  		if len(hashes) >= len(transactions) {
   161  			break
   162  		}
   163  
   164  		time.Sleep(100 * time.Millisecond)
   165  	}
   166  
   167  	for i := range hashes {
   168  		if hashes[i] != transactions[i].Hash() {
   169  			t.Errorf("hashes[%d] invalid, want %x, got %x", i, transactions[i].Hash(), hashes[i])
   170  		}
   171  	}
   172  }
   173  
   174  // TestLogFilterCreation test whether a given filter criteria makes sense.
   175  // If not it must return an error.
   176  func TestLogFilterCreation(t *testing.T) {
   177  	var (
   178  		mux     = new(event.TypeMux)
   179  		db, _   = ethdb.NewMemDatabase()
   180  		backend = &testBackend{mux, db}
   181  		api     = NewPublicFilterAPI(backend, false)
   182  
   183  		testCases = []struct {
   184  			crit    FilterCriteria
   185  			success bool
   186  		}{
   187  			// defaults
   188  			{FilterCriteria{}, true},
   189  			// valid block number range
   190  			{FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(2)}, true},
   191  			// "mined" block range to pending
   192  			{FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, true},
   193  			// new mined and pending blocks
   194  			{FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())}, true},
   195  			// from block "higher" than to block
   196  			{FilterCriteria{FromBlock: big.NewInt(2), ToBlock: big.NewInt(1)}, false},
   197  			// from block "higher" than to block
   198  			{FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(100)}, false},
   199  			// from block "higher" than to block
   200  			{FilterCriteria{FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(100)}, false},
   201  			// from block "higher" than to block
   202  			{FilterCriteria{FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, false},
   203  		}
   204  	)
   205  
   206  	for i, test := range testCases {
   207  		_, err := api.NewFilter(test.crit)
   208  		if test.success && err != nil {
   209  			t.Errorf("expected filter creation for case %d to success, got %v", i, err)
   210  		}
   211  		if !test.success && err == nil {
   212  			t.Errorf("expected testcase %d to fail with an error", i)
   213  		}
   214  	}
   215  }
   216  
   217  // TestInvalidLogFilterCreation tests whether invalid filter log criteria results in an error
   218  // when the filter is created.
   219  func TestInvalidLogFilterCreation(t *testing.T) {
   220  	t.Parallel()
   221  
   222  	var (
   223  		mux     = new(event.TypeMux)
   224  		db, _   = ethdb.NewMemDatabase()
   225  		backend = &testBackend{mux, db}
   226  		api     = NewPublicFilterAPI(backend, false)
   227  	)
   228  
   229  	// different situations where log filter creation should fail.
   230  	// Reason: fromBlock > toBlock
   231  	testCases := []FilterCriteria{
   232  		0: {FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())},
   233  		1: {FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(100)},
   234  		2: {FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(100)},
   235  	}
   236  
   237  	for i, test := range testCases {
   238  		if _, err := api.NewFilter(test); err == nil {
   239  			t.Errorf("Expected NewFilter for case #%d to fail", i)
   240  		}
   241  	}
   242  }
   243  
   244  // TestLogFilter tests whether log filters match the correct logs that are posted to the event mux.
   245  func TestLogFilter(t *testing.T) {
   246  	t.Parallel()
   247  
   248  	var (
   249  		mux     = new(event.TypeMux)
   250  		db, _   = ethdb.NewMemDatabase()
   251  		backend = &testBackend{mux, db}
   252  		api     = NewPublicFilterAPI(backend, false)
   253  
   254  		firstAddr      = common.HexToAddress("0x1111111111111111111111111111111111111111")
   255  		secondAddr     = common.HexToAddress("0x2222222222222222222222222222222222222222")
   256  		thirdAddress   = common.HexToAddress("0x3333333333333333333333333333333333333333")
   257  		notUsedAddress = common.HexToAddress("0x9999999999999999999999999999999999999999")
   258  		firstTopic     = common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111")
   259  		secondTopic    = common.HexToHash("0x2222222222222222222222222222222222222222222222222222222222222222")
   260  		notUsedTopic   = common.HexToHash("0x9999999999999999999999999999999999999999999999999999999999999999")
   261  
   262  		// posted twice, once as vm.Logs and once as core.PendingLogsEvent
   263  		allLogs = []*types.Log{
   264  			{Address: firstAddr},
   265  			{Address: firstAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 1},
   266  			{Address: secondAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 1},
   267  			{Address: thirdAddress, Topics: []common.Hash{secondTopic}, BlockNumber: 2},
   268  			{Address: thirdAddress, Topics: []common.Hash{secondTopic}, BlockNumber: 3},
   269  		}
   270  
   271  		expectedCase7  = []*types.Log{allLogs[3], allLogs[4], allLogs[0], allLogs[1], allLogs[2], allLogs[3], allLogs[4]}
   272  		expectedCase11 = []*types.Log{allLogs[1], allLogs[2], allLogs[1], allLogs[2]}
   273  
   274  		testCases = []struct {
   275  			crit     FilterCriteria
   276  			expected []*types.Log
   277  			id       rpc.ID
   278  		}{
   279  			// match all
   280  			0: {FilterCriteria{}, allLogs, ""},
   281  			// match none due to no matching addresses
   282  			1: {FilterCriteria{Addresses: []common.Address{{}, notUsedAddress}, Topics: [][]common.Hash{allLogs[0].Topics}}, []*types.Log{}, ""},
   283  			// match logs based on addresses, ignore topics
   284  			2: {FilterCriteria{Addresses: []common.Address{firstAddr}}, allLogs[:2], ""},
   285  			// match none due to no matching topics (match with address)
   286  			3: {FilterCriteria{Addresses: []common.Address{secondAddr}, Topics: [][]common.Hash{{notUsedTopic}}}, []*types.Log{}, ""},
   287  			// match logs based on addresses and topics
   288  			4: {FilterCriteria{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, allLogs[3:5], ""},
   289  			// match logs based on multiple addresses and "or" topics
   290  			5: {FilterCriteria{Addresses: []common.Address{secondAddr, thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, allLogs[2:5], ""},
   291  			// logs in the pending block
   292  			6: {FilterCriteria{Addresses: []common.Address{firstAddr}, FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())}, allLogs[:2], ""},
   293  			// mined logs with block num >= 2 or pending logs
   294  			7: {FilterCriteria{FromBlock: big.NewInt(2), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())}, expectedCase7, ""},
   295  			// all "mined" logs with block num >= 2
   296  			8: {FilterCriteria{FromBlock: big.NewInt(2), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, allLogs[3:], ""},
   297  			// all "mined" logs
   298  			9: {FilterCriteria{ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, allLogs, ""},
   299  			// all "mined" logs with 1>= block num <=2 and topic secondTopic
   300  			10: {FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(2), Topics: [][]common.Hash{{secondTopic}}}, allLogs[3:4], ""},
   301  			// all "mined" and pending logs with topic firstTopic
   302  			11: {FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), Topics: [][]common.Hash{{firstTopic}}}, expectedCase11, ""},
   303  		}
   304  	)
   305  
   306  	// create all filters
   307  	for i := range testCases {
   308  		testCases[i].id, _ = api.NewFilter(testCases[i].crit)
   309  	}
   310  
   311  	// raise events
   312  	time.Sleep(1 * time.Second)
   313  	if err := mux.Post(allLogs); err != nil {
   314  		t.Fatal(err)
   315  	}
   316  	if err := mux.Post(core.PendingLogsEvent{Logs: allLogs}); err != nil {
   317  		t.Fatal(err)
   318  	}
   319  
   320  	for i, tt := range testCases {
   321  		var fetched []*types.Log
   322  		for { // fetch all expected logs
   323  			results, err := api.GetFilterChanges(tt.id)
   324  			if err != nil {
   325  				t.Fatalf("Unable to fetch logs: %v", err)
   326  			}
   327  
   328  			fetched = append(fetched, results.([]*types.Log)...)
   329  			if len(fetched) >= len(tt.expected) {
   330  				break
   331  			}
   332  
   333  			time.Sleep(100 * time.Millisecond)
   334  		}
   335  
   336  		if len(fetched) != len(tt.expected) {
   337  			t.Errorf("invalid number of logs for case %d, want %d log(s), got %d", i, len(tt.expected), len(fetched))
   338  			return
   339  		}
   340  
   341  		for l := range fetched {
   342  			if fetched[l].Removed {
   343  				t.Errorf("expected log not to be removed for log %d in case %d", l, i)
   344  			}
   345  			if !reflect.DeepEqual(fetched[l], tt.expected[l]) {
   346  				t.Errorf("invalid log on index %d for case %d", l, i)
   347  			}
   348  		}
   349  	}
   350  }
   351  
   352  // TestPendingLogsSubscription tests if a subscription receives the correct pending logs that are posted to the event mux.
   353  func TestPendingLogsSubscription(t *testing.T) {
   354  	t.Parallel()
   355  
   356  	var (
   357  		mux     = new(event.TypeMux)
   358  		db, _   = ethdb.NewMemDatabase()
   359  		backend = &testBackend{mux, db}
   360  		api     = NewPublicFilterAPI(backend, false)
   361  
   362  		firstAddr      = common.HexToAddress("0x1111111111111111111111111111111111111111")
   363  		secondAddr     = common.HexToAddress("0x2222222222222222222222222222222222222222")
   364  		thirdAddress   = common.HexToAddress("0x3333333333333333333333333333333333333333")
   365  		notUsedAddress = common.HexToAddress("0x9999999999999999999999999999999999999999")
   366  		firstTopic     = common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111")
   367  		secondTopic    = common.HexToHash("0x2222222222222222222222222222222222222222222222222222222222222222")
   368  		thirdTopic     = common.HexToHash("0x3333333333333333333333333333333333333333333333333333333333333333")
   369  		forthTopic     = common.HexToHash("0x4444444444444444444444444444444444444444444444444444444444444444")
   370  		notUsedTopic   = common.HexToHash("0x9999999999999999999999999999999999999999999999999999999999999999")
   371  
   372  		allLogs = []core.PendingLogsEvent{
   373  			{Logs: []*types.Log{{Address: firstAddr, Topics: []common.Hash{}, BlockNumber: 0}}},
   374  			{Logs: []*types.Log{{Address: firstAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 1}}},
   375  			{Logs: []*types.Log{{Address: secondAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 2}}},
   376  			{Logs: []*types.Log{{Address: thirdAddress, Topics: []common.Hash{secondTopic}, BlockNumber: 3}}},
   377  			{Logs: []*types.Log{{Address: thirdAddress, Topics: []common.Hash{secondTopic}, BlockNumber: 4}}},
   378  			{Logs: []*types.Log{
   379  				{Address: thirdAddress, Topics: []common.Hash{firstTopic}, BlockNumber: 5},
   380  				{Address: thirdAddress, Topics: []common.Hash{thirdTopic}, BlockNumber: 5},
   381  				{Address: thirdAddress, Topics: []common.Hash{forthTopic}, BlockNumber: 5},
   382  				{Address: firstAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 5},
   383  			}},
   384  		}
   385  
   386  		convertLogs = func(pl []core.PendingLogsEvent) []*types.Log {
   387  			var logs []*types.Log
   388  			for _, l := range pl {
   389  				logs = append(logs, l.Logs...)
   390  			}
   391  			return logs
   392  		}
   393  
   394  		testCases = []struct {
   395  			crit     FilterCriteria
   396  			expected []*types.Log
   397  			c        chan []*types.Log
   398  			sub      *Subscription
   399  		}{
   400  			// match all
   401  			{FilterCriteria{}, convertLogs(allLogs), nil, nil},
   402  			// match none due to no matching addresses
   403  			{FilterCriteria{Addresses: []common.Address{{}, notUsedAddress}, Topics: [][]common.Hash{{}}}, []*types.Log{}, nil, nil},
   404  			// match logs based on addresses, ignore topics
   405  			{FilterCriteria{Addresses: []common.Address{firstAddr}}, append(convertLogs(allLogs[:2]), allLogs[5].Logs[3]), nil, nil},
   406  			// match none due to no matching topics (match with address)
   407  			{FilterCriteria{Addresses: []common.Address{secondAddr}, Topics: [][]common.Hash{{notUsedTopic}}}, []*types.Log{}, nil, nil},
   408  			// match logs based on addresses and topics
   409  			{FilterCriteria{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, append(convertLogs(allLogs[3:5]), allLogs[5].Logs[0]), nil, nil},
   410  			// match logs based on multiple addresses and "or" topics
   411  			{FilterCriteria{Addresses: []common.Address{secondAddr, thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, append(convertLogs(allLogs[2:5]), allLogs[5].Logs[0]), nil, nil},
   412  			// block numbers are ignored for filters created with New***Filter, these return all logs that match the given criteria when the state changes
   413  			{FilterCriteria{Addresses: []common.Address{firstAddr}, FromBlock: big.NewInt(2), ToBlock: big.NewInt(3)}, append(convertLogs(allLogs[:2]), allLogs[5].Logs[3]), nil, nil},
   414  			// multiple pending logs, should match only 2 topics from the logs in block 5
   415  			{FilterCriteria{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, forthTopic}}}, []*types.Log{allLogs[5].Logs[0], allLogs[5].Logs[2]}, nil, nil},
   416  		}
   417  	)
   418  
   419  	// create all subscriptions, this ensures all subscriptions are created before the events are posted.
   420  	// on slow machines this could otherwise lead to missing events when the subscription is created after
   421  	// (some) events are posted.
   422  	for i := range testCases {
   423  		testCases[i].c = make(chan []*types.Log)
   424  		testCases[i].sub, _ = api.events.SubscribeLogs(testCases[i].crit, testCases[i].c)
   425  	}
   426  
   427  	for n, test := range testCases {
   428  		i := n
   429  		tt := test
   430  		go func() {
   431  			var fetched []*types.Log
   432  		fetchLoop:
   433  			for {
   434  				logs := <-tt.c
   435  				fetched = append(fetched, logs...)
   436  				if len(fetched) >= len(tt.expected) {
   437  					break fetchLoop
   438  				}
   439  			}
   440  
   441  			if len(fetched) != len(tt.expected) {
   442  				t.Fatalf("invalid number of logs for case %d, want %d log(s), got %d", i, len(tt.expected), len(fetched))
   443  			}
   444  
   445  			for l := range fetched {
   446  				if fetched[l].Removed {
   447  					t.Errorf("expected log not to be removed for log %d in case %d", l, i)
   448  				}
   449  				if !reflect.DeepEqual(fetched[l], tt.expected[l]) {
   450  					t.Errorf("invalid log on index %d for case %d", l, i)
   451  				}
   452  			}
   453  		}()
   454  	}
   455  
   456  	// raise events
   457  	time.Sleep(1 * time.Second)
   458  	for _, l := range allLogs {
   459  		if err := mux.Post(l); err != nil {
   460  			t.Fatal(err)
   461  		}
   462  	}
   463  }