github.com/ava-labs/subnet-evm@v0.6.4/eth/filters/filter_test.go (about)

     1  // (c) 2019-2022, Ava Labs, Inc.
     2  //
     3  // This file is a derived work, based on the go-ethereum library whose original
     4  // notices appear below.
     5  //
     6  // It is distributed under a license compatible with the licensing terms of the
     7  // original code from which it is derived.
     8  //
     9  // Much love to the original authors for their work.
    10  // **********
    11  // Copyright 2015 The go-ethereum Authors
    12  // This file is part of the go-ethereum library.
    13  //
    14  // The go-ethereum library is free software: you can redistribute it and/or modify
    15  // it under the terms of the GNU Lesser General Public License as published by
    16  // the Free Software Foundation, either version 3 of the License, or
    17  // (at your option) any later version.
    18  //
    19  // The go-ethereum library is distributed in the hope that it will be useful,
    20  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    21  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    22  // GNU Lesser General Public License for more details.
    23  //
    24  // You should have received a copy of the GNU Lesser General Public License
    25  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    26  
    27  package filters
    28  
    29  import (
    30  	"context"
    31  	"encoding/json"
    32  	"math/big"
    33  	"strings"
    34  	"testing"
    35  	"time"
    36  
    37  	"github.com/ava-labs/subnet-evm/accounts/abi"
    38  	"github.com/ava-labs/subnet-evm/consensus/dummy"
    39  	"github.com/ava-labs/subnet-evm/core"
    40  	"github.com/ava-labs/subnet-evm/core/rawdb"
    41  	"github.com/ava-labs/subnet-evm/core/types"
    42  	"github.com/ava-labs/subnet-evm/core/vm"
    43  	"github.com/ava-labs/subnet-evm/params"
    44  	"github.com/ava-labs/subnet-evm/rpc"
    45  	"github.com/ava-labs/subnet-evm/trie"
    46  	"github.com/ethereum/go-ethereum/common"
    47  	"github.com/ethereum/go-ethereum/crypto"
    48  	"github.com/stretchr/testify/require"
    49  )
    50  
    51  func makeReceipt(addr common.Address) *types.Receipt {
    52  	receipt := types.NewReceipt(nil, false, 0)
    53  	receipt.Logs = []*types.Log{
    54  		{Address: addr},
    55  	}
    56  	receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
    57  	return receipt
    58  }
    59  
    60  func BenchmarkFilters(b *testing.B) {
    61  	var (
    62  		db, _   = rawdb.NewLevelDBDatabase(b.TempDir(), 0, 0, "", false)
    63  		_, sys  = newTestFilterSystem(b, db, Config{})
    64  		key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
    65  		addr1   = crypto.PubkeyToAddress(key1.PublicKey)
    66  		addr2   = common.BytesToAddress([]byte("jeff"))
    67  		addr3   = common.BytesToAddress([]byte("ethereum"))
    68  		addr4   = common.BytesToAddress([]byte("random addresses please"))
    69  
    70  		gspec = &core.Genesis{
    71  			Alloc:   core.GenesisAlloc{addr1: {Balance: big.NewInt(1000000)}},
    72  			BaseFee: big.NewInt(1),
    73  			Config:  params.TestChainConfig,
    74  		}
    75  	)
    76  	defer db.Close()
    77  	_, chain, receipts, err := core.GenerateChainWithGenesis(gspec, dummy.NewFaker(), 100010, 10, func(i int, gen *core.BlockGen) {
    78  		switch i {
    79  		case 2403:
    80  			receipt := makeReceipt(addr1)
    81  			gen.AddUncheckedReceipt(receipt)
    82  			gen.AddUncheckedTx(types.NewTransaction(999, common.HexToAddress("0x999"), big.NewInt(999), 999, gen.BaseFee(), nil))
    83  		case 1034:
    84  			receipt := makeReceipt(addr2)
    85  			gen.AddUncheckedReceipt(receipt)
    86  			gen.AddUncheckedTx(types.NewTransaction(999, common.HexToAddress("0x999"), big.NewInt(999), 999, gen.BaseFee(), nil))
    87  		case 34:
    88  			receipt := makeReceipt(addr3)
    89  			gen.AddUncheckedReceipt(receipt)
    90  			gen.AddUncheckedTx(types.NewTransaction(999, common.HexToAddress("0x999"), big.NewInt(999), 999, gen.BaseFee(), nil))
    91  		case 99999:
    92  			receipt := makeReceipt(addr4)
    93  			gen.AddUncheckedReceipt(receipt)
    94  			gen.AddUncheckedTx(types.NewTransaction(999, common.HexToAddress("0x999"), big.NewInt(999), 999, gen.BaseFee(), nil))
    95  		}
    96  	})
    97  	require.NoError(b, err)
    98  	// The test txs are not properly signed, can't simply create a chain
    99  	// and then import blocks. TODO(rjl493456442) try to get rid of the
   100  	// manual database writes.
   101  	gspec.MustCommit(db, trie.NewDatabase(db, trie.HashDefaults))
   102  
   103  	for i, block := range chain {
   104  		rawdb.WriteBlock(db, block)
   105  		rawdb.WriteCanonicalHash(db, block.Hash(), block.NumberU64())
   106  		rawdb.WriteHeadBlockHash(db, block.Hash())
   107  		rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), receipts[i])
   108  	}
   109  	b.ResetTimer()
   110  
   111  	filter := sys.NewRangeFilter(0, int64(rpc.LatestBlockNumber), []common.Address{addr1, addr2, addr3, addr4}, nil)
   112  
   113  	for i := 0; i < b.N; i++ {
   114  		filter.begin = 0
   115  		logs, _ := filter.Logs(context.Background())
   116  		if len(logs) != 4 {
   117  			b.Fatal("expected 4 logs, got", len(logs))
   118  		}
   119  	}
   120  }
   121  
   122  func TestFilters(t *testing.T) {
   123  	var (
   124  		db     = rawdb.NewMemoryDatabase()
   125  		_, sys = newTestFilterSystem(t, db, Config{})
   126  		// Sender account
   127  		key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
   128  		addr    = crypto.PubkeyToAddress(key1.PublicKey)
   129  		signer  = types.NewLondonSigner(big.NewInt(1))
   130  		// Logging contract
   131  		contract  = common.Address{0xfe}
   132  		contract2 = common.Address{0xff}
   133  		abiStr    = `[{"inputs":[],"name":"log0","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"t1","type":"uint256"}],"name":"log1","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"t1","type":"uint256"},{"internalType":"uint256","name":"t2","type":"uint256"}],"name":"log2","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"t1","type":"uint256"},{"internalType":"uint256","name":"t2","type":"uint256"},{"internalType":"uint256","name":"t3","type":"uint256"}],"name":"log3","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"t1","type":"uint256"},{"internalType":"uint256","name":"t2","type":"uint256"},{"internalType":"uint256","name":"t3","type":"uint256"},{"internalType":"uint256","name":"t4","type":"uint256"}],"name":"log4","outputs":[],"stateMutability":"nonpayable","type":"function"}]`
   134  		/*
   135  			// SPDX-License-Identifier: GPL-3.0
   136  			pragma solidity >=0.7.0 <0.9.0;
   137  
   138  			contract Logger {
   139  			    function log0() external {
   140  			        assembly {
   141  			            log0(0, 0)
   142  			        }
   143  			    }
   144  
   145  			    function log1(uint t1) external {
   146  			        assembly {
   147  			            log1(0, 0, t1)
   148  			        }
   149  			    }
   150  
   151  			    function log2(uint t1, uint t2) external {
   152  			        assembly {
   153  			            log2(0, 0, t1, t2)
   154  			        }
   155  			    }
   156  
   157  			    function log3(uint t1, uint t2, uint t3) external {
   158  			        assembly {
   159  			            log3(0, 0, t1, t2, t3)
   160  			        }
   161  			    }
   162  
   163  			    function log4(uint t1, uint t2, uint t3, uint t4) external {
   164  			        assembly {
   165  			            log4(0, 0, t1, t2, t3, t4)
   166  			        }
   167  			    }
   168  			}
   169  		*/
   170  		bytecode = common.FromHex("608060405234801561001057600080fd5b50600436106100575760003560e01c80630aa731851461005c5780632a4c08961461006657806378b9a1f314610082578063c670f8641461009e578063c683d6a3146100ba575b600080fd5b6100646100d6565b005b610080600480360381019061007b9190610143565b6100dc565b005b61009c60048036038101906100979190610196565b6100e8565b005b6100b860048036038101906100b391906101d6565b6100f2565b005b6100d460048036038101906100cf9190610203565b6100fa565b005b600080a0565b808284600080a3505050565b8082600080a25050565b80600080a150565b80828486600080a450505050565b600080fd5b6000819050919050565b6101208161010d565b811461012b57600080fd5b50565b60008135905061013d81610117565b92915050565b60008060006060848603121561015c5761015b610108565b5b600061016a8682870161012e565b935050602061017b8682870161012e565b925050604061018c8682870161012e565b9150509250925092565b600080604083850312156101ad576101ac610108565b5b60006101bb8582860161012e565b92505060206101cc8582860161012e565b9150509250929050565b6000602082840312156101ec576101eb610108565b5b60006101fa8482850161012e565b91505092915050565b6000806000806080858703121561021d5761021c610108565b5b600061022b8782880161012e565b945050602061023c8782880161012e565b935050604061024d8782880161012e565b925050606061025e8782880161012e565b9150509295919450925056fea264697066735822122073a4b156f487e59970dc1ef449cc0d51467268f676033a17188edafcee861f9864736f6c63430008110033")
   171  
   172  		hash1 = common.BytesToHash([]byte("topic1"))
   173  		hash2 = common.BytesToHash([]byte("topic2"))
   174  		hash3 = common.BytesToHash([]byte("topic3"))
   175  		hash4 = common.BytesToHash([]byte("topic4"))
   176  
   177  		gspec = &core.Genesis{
   178  			Config: params.TestChainConfig,
   179  			Alloc: core.GenesisAlloc{
   180  				addr:      {Balance: big.NewInt(0).Mul(big.NewInt(100), big.NewInt(params.Ether))},
   181  				contract:  {Balance: big.NewInt(0), Code: bytecode},
   182  				contract2: {Balance: big.NewInt(0), Code: bytecode},
   183  			},
   184  			BaseFee: big.NewInt(1),
   185  		}
   186  	)
   187  
   188  	contractABI, err := abi.JSON(strings.NewReader(abiStr))
   189  	if err != nil {
   190  		t.Fatal(err)
   191  	}
   192  
   193  	// Hack: GenerateChainWithGenesis creates a new db.
   194  	// Commit the genesis manually and use GenerateChain.
   195  	_, err = gspec.Commit(db, trie.NewDatabase(db, nil))
   196  	if err != nil {
   197  		t.Fatal(err)
   198  	}
   199  	//_, chain, receipts, err := core.GenerateChainWithGenesis(gspec, dummy.NewFaker(), 1000, 10, func(i int, gen *core.BlockGen) {
   200  	chain, _, err := core.GenerateChain(gspec.Config, gspec.ToBlock(), dummy.NewFaker(), db, 1000, 10, func(i int, gen *core.BlockGen) {
   201  		switch i {
   202  		case 1:
   203  			data, err := contractABI.Pack("log1", hash1.Big())
   204  			if err != nil {
   205  				t.Fatal(err)
   206  			}
   207  			tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{
   208  				Nonce:    0,
   209  				GasPrice: gen.BaseFee(),
   210  				Gas:      30000,
   211  				To:       &contract,
   212  				Data:     data,
   213  			}), signer, key1)
   214  			gen.AddTx(tx)
   215  			tx2, _ := types.SignTx(types.NewTx(&types.LegacyTx{
   216  				Nonce:    1,
   217  				GasPrice: gen.BaseFee(),
   218  				Gas:      30000,
   219  				To:       &contract2,
   220  				Data:     data,
   221  			}), signer, key1)
   222  			gen.AddTx(tx2)
   223  		case 2:
   224  			data, err := contractABI.Pack("log2", hash2.Big(), hash1.Big())
   225  			if err != nil {
   226  				t.Fatal(err)
   227  			}
   228  			tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{
   229  				Nonce:    2,
   230  				GasPrice: gen.BaseFee(),
   231  				Gas:      30000,
   232  				To:       &contract,
   233  				Data:     data,
   234  			}), signer, key1)
   235  			gen.AddTx(tx)
   236  		case 998:
   237  			data, err := contractABI.Pack("log1", hash3.Big())
   238  			if err != nil {
   239  				t.Fatal(err)
   240  			}
   241  			tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{
   242  				Nonce:    3,
   243  				GasPrice: gen.BaseFee(),
   244  				Gas:      30000,
   245  				To:       &contract2,
   246  				Data:     data,
   247  			}), signer, key1)
   248  			gen.AddTx(tx)
   249  		case 999:
   250  			data, err := contractABI.Pack("log1", hash4.Big())
   251  			if err != nil {
   252  				t.Fatal(err)
   253  			}
   254  			tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{
   255  				Nonce:    4,
   256  				GasPrice: gen.BaseFee(),
   257  				Gas:      30000,
   258  				To:       &contract,
   259  				Data:     data,
   260  			}), signer, key1)
   261  			gen.AddTx(tx)
   262  		}
   263  	})
   264  	require.NoError(t, err)
   265  	bc, err := core.NewBlockChain(db, core.DefaultCacheConfig, gspec, dummy.NewCoinbaseFaker(), vm.Config{}, gspec.ToBlock().Hash(), false)
   266  	if err != nil {
   267  		t.Fatal(err)
   268  	}
   269  	_, err = bc.InsertChain(chain)
   270  	if err != nil {
   271  		t.Fatal(err)
   272  	}
   273  
   274  	// Set block 998 as Finalized (-3)
   275  	// bc.SetFinalized(chain[998].Header())
   276  	err = rawdb.WriteAcceptorTip(db, chain[998].Hash())
   277  	require.NoError(t, err)
   278  
   279  	for i, tc := range []struct {
   280  		f    *Filter
   281  		want string
   282  		err  string
   283  	}{
   284  		{
   285  			f:    sys.NewBlockFilter(chain[2].Hash(), []common.Address{contract}, nil),
   286  			want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0x7a7556792ca7d37882882e2b001fe14833eaf81c2c7f865c9c771ec37a024f6b","logIndex":"0x0","removed":false}]`,
   287  		}, {
   288  			f:    sys.NewRangeFilter(0, int64(rpc.LatestBlockNumber), []common.Address{contract}, [][]common.Hash{{hash1, hash2, hash3, hash4}}),
   289  			want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x2","transactionHash":"0xa8028c655b6423204c8edfbc339f57b042d6bec2b6a61145d76b7c08b4cccd42","transactionIndex":"0x0","blockHash":"0x24417bb49ce44cfad65da68f33b510bf2a129c0d89ccf06acb6958b8585ccf34","logIndex":"0x0","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0x7a7556792ca7d37882882e2b001fe14833eaf81c2c7f865c9c771ec37a024f6b","logIndex":"0x0","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0xb360bad5265261c075ece02d3bf0e39498a6a76310482cdfd90588748e6c5ee0","logIndex":"0x0","removed":false}]`,
   290  		}, {
   291  			f: sys.NewRangeFilter(900, 999, []common.Address{contract}, [][]common.Hash{{hash3}}),
   292  		}, {
   293  			f:    sys.NewRangeFilter(990, int64(rpc.LatestBlockNumber), []common.Address{contract2}, [][]common.Hash{{hash3}}),
   294  			want: `[{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696333"],"data":"0x","blockNumber":"0x3e7","transactionHash":"0x53e3675800c6908424b61b35a44e51ca4c73ca603e58a65b32c67968b4f42200","transactionIndex":"0x0","blockHash":"0x2e4620a2b426b0612ec6cad9603f466723edaed87f98c9137405dd4f7a2409ff","logIndex":"0x0","removed":false}]`,
   295  		}, {
   296  			f:    sys.NewRangeFilter(1, 10, []common.Address{contract}, [][]common.Hash{{hash2}, {hash1}}),
   297  			want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0x7a7556792ca7d37882882e2b001fe14833eaf81c2c7f865c9c771ec37a024f6b","logIndex":"0x0","removed":false}]`,
   298  		}, {
   299  			f:    sys.NewRangeFilter(1, 10, nil, [][]common.Hash{{hash1, hash2}}),
   300  			want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x2","transactionHash":"0xa8028c655b6423204c8edfbc339f57b042d6bec2b6a61145d76b7c08b4cccd42","transactionIndex":"0x0","blockHash":"0x24417bb49ce44cfad65da68f33b510bf2a129c0d89ccf06acb6958b8585ccf34","logIndex":"0x0","removed":false},{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x2","transactionHash":"0xdba3e2ea9a7d690b722d70ee605fd67ba4c00d1d3aecd5cf187a7b92ad8eb3df","transactionIndex":"0x1","blockHash":"0x24417bb49ce44cfad65da68f33b510bf2a129c0d89ccf06acb6958b8585ccf34","logIndex":"0x1","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0x7a7556792ca7d37882882e2b001fe14833eaf81c2c7f865c9c771ec37a024f6b","logIndex":"0x0","removed":false}]`,
   301  		}, {
   302  			f: sys.NewRangeFilter(0, int64(rpc.LatestBlockNumber), nil, [][]common.Hash{{common.BytesToHash([]byte("fail"))}}),
   303  		}, {
   304  			f: sys.NewRangeFilter(0, int64(rpc.LatestBlockNumber), []common.Address{common.BytesToAddress([]byte("failmenow"))}, nil),
   305  		}, {
   306  			f: sys.NewRangeFilter(0, int64(rpc.LatestBlockNumber), nil, [][]common.Hash{{common.BytesToHash([]byte("fail"))}, {hash1}}),
   307  		}, {
   308  			f:    sys.NewRangeFilter(int64(rpc.LatestBlockNumber), int64(rpc.LatestBlockNumber), nil, nil),
   309  			want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0xb360bad5265261c075ece02d3bf0e39498a6a76310482cdfd90588748e6c5ee0","logIndex":"0x0","removed":false}]`,
   310  		}, {
   311  			f:    sys.NewRangeFilter(int64(rpc.FinalizedBlockNumber), int64(rpc.LatestBlockNumber), nil, nil),
   312  			want: `[{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696333"],"data":"0x","blockNumber":"0x3e7","transactionHash":"0x53e3675800c6908424b61b35a44e51ca4c73ca603e58a65b32c67968b4f42200","transactionIndex":"0x0","blockHash":"0x2e4620a2b426b0612ec6cad9603f466723edaed87f98c9137405dd4f7a2409ff","logIndex":"0x0","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0xb360bad5265261c075ece02d3bf0e39498a6a76310482cdfd90588748e6c5ee0","logIndex":"0x0","removed":false}]`,
   313  		}, {
   314  			f:    sys.NewRangeFilter(int64(rpc.FinalizedBlockNumber), int64(rpc.FinalizedBlockNumber), nil, nil),
   315  			want: `[{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696333"],"data":"0x","blockNumber":"0x3e7","transactionHash":"0x53e3675800c6908424b61b35a44e51ca4c73ca603e58a65b32c67968b4f42200","transactionIndex":"0x0","blockHash":"0x2e4620a2b426b0612ec6cad9603f466723edaed87f98c9137405dd4f7a2409ff","logIndex":"0x0","removed":false}]`,
   316  		}, {
   317  			f: sys.NewRangeFilter(int64(rpc.LatestBlockNumber), int64(rpc.FinalizedBlockNumber), nil, nil),
   318  		}, {
   319  			f:   sys.NewRangeFilter(int64(rpc.SafeBlockNumber), int64(rpc.LatestBlockNumber), nil, nil),
   320  			err: "safe header not found",
   321  		}, {
   322  			f:   sys.NewRangeFilter(int64(rpc.SafeBlockNumber), int64(rpc.SafeBlockNumber), nil, nil),
   323  			err: "safe header not found",
   324  		}, {
   325  			f:   sys.NewRangeFilter(int64(rpc.LatestBlockNumber), int64(rpc.SafeBlockNumber), nil, nil),
   326  			err: "safe header not found",
   327  		}, {
   328  			f: sys.NewRangeFilter(int64(rpc.PendingBlockNumber), int64(rpc.PendingBlockNumber), nil, nil),
   329  		}, {
   330  			f:    sys.NewRangeFilter(int64(rpc.LatestBlockNumber), int64(rpc.PendingBlockNumber), nil, nil),
   331  			want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0xb360bad5265261c075ece02d3bf0e39498a6a76310482cdfd90588748e6c5ee0","logIndex":"0x0","removed":false}]`,
   332  		}, {
   333  			f:   sys.NewRangeFilter(int64(rpc.PendingBlockNumber), int64(rpc.LatestBlockNumber), nil, nil),
   334  			err: "invalid block range",
   335  		},
   336  	} {
   337  		logs, err := tc.f.Logs(context.Background())
   338  		if err == nil && tc.err != "" {
   339  			t.Fatalf("test %d, expected error %q, got nil", i, tc.err)
   340  		} else if err != nil && err.Error() != tc.err {
   341  			t.Fatalf("test %d, expected error %q, got %q", i, tc.err, err.Error())
   342  		}
   343  		if tc.want == "" && len(logs) == 0 {
   344  			continue
   345  		}
   346  		tc.want = patchWant(t, tc.want, chain)
   347  		have, err := json.Marshal(logs)
   348  		if err != nil {
   349  			t.Fatal(err)
   350  		}
   351  		if string(have) != tc.want {
   352  			t.Fatalf("test %d, have:\n%s\nwant:\n%s", i, have, tc.want)
   353  		}
   354  	}
   355  
   356  	t.Run("timeout", func(t *testing.T) {
   357  		f := sys.NewRangeFilter(0, -1, nil, nil)
   358  		ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(-time.Hour))
   359  		defer cancel()
   360  		_, err := f.Logs(ctx)
   361  		if err == nil {
   362  			t.Fatal("expected error")
   363  		}
   364  		if err != context.DeadlineExceeded {
   365  			t.Fatalf("expected context.DeadlineExceeded, got %v", err)
   366  		}
   367  	})
   368  }
   369  
   370  func patchWant(t *testing.T, want string, blocks []*types.Block) string {
   371  	var logs []*types.Log
   372  	err := json.Unmarshal([]byte(want), &logs)
   373  	if err != nil {
   374  		t.Fatal(err)
   375  	}
   376  
   377  	for _, log := range logs {
   378  		blockIndex := log.BlockNumber - 1
   379  		log.BlockHash = blocks[blockIndex].Hash()
   380  		log.TxHash = blocks[blockIndex].Transactions()[log.TxIndex].Hash()
   381  	}
   382  	result, err := json.Marshal(logs)
   383  	if err != nil {
   384  		t.Fatal(err)
   385  	}
   386  	return string(result)
   387  }