func TestLogFilterCreation(t *testing.T) {
	var (
		backend = newTestBackend()
		api     = NewPublicFilterAPI(backend, testConfig())
		testCases = []struct {
			crit    FilterCriteria
			success bool
		}{
			{FilterCriteria{}, true},
			{FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(2)}, true},
			{FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, true},
			{FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())}, true},
			{FilterCriteria{FromBlock: big.NewInt(2), ToBlock: big.NewInt(1)}, false},
			{FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(100)}, false},
			{FilterCriteria{FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(100)}, false},
			{FilterCriteria{FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, false},
		}
	)
	for i, test := range testCases {
		_, err := api.NewFilter(test.crit)
		if test.success && err != nil {
			t.Errorf("expected filter creation for case %d to success, got %v", i, err)
		}
		if !test.success && err == nil {
			t.Errorf("expected testcase %d to fail with an error", i)
		}
	}
}
TestLogFilterCreation test whether a given filter criteria makes sense.
If not it must return an error.
package filters
import (
	"context"
	"math/big"
	"reflect"
	"testing"
	"time"
	"github.com/Fantom-foundation/lachesis-base/kvdb/memorydb"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core"
	"github.com/ethereum/go-ethereum/core/rawdb"
	"github.com/ethereum/go-ethereum/core/state"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/ethdb"
	notify "github.com/ethereum/go-ethereum/event"
	"github.com/ethereum/go-ethereum/params"
	"github.com/ethereum/go-ethereum/rpc"
	"github.com/syndtr/goleveldb/leveldb/opt"
	"github.com/Fantom-foundation/go-opera/evmcore"
	"github.com/Fantom-foundation/go-opera/integration/makegenesis"
	"github.com/Fantom-foundation/go-opera/topicsdb"
)
type testBackend struct {
	db         ethdb.Database
	logIndex   *topicsdb.Index
	blocksFeed *notify.Feed
	txsFeed    *notify.Feed
	logsFeed   *notify.Feed
}
func newTestBackend() *testBackend {
	return &testBackend{
		db:         rawdb.NewMemoryDatabase(),
		logIndex:   topicsdb.New(memorydb.New()),
		blocksFeed: new(notify.Feed),
		txsFeed:    new(notify.Feed),
		logsFeed:   new(notify.Feed),
	}
}
func (b *testBackend) ChainDb() ethdb.Database {
	return b.db
}
func (b *testBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*evmcore.EvmHeader, error) {
	var (
		hash common.Hash
		num  uint64
	)
	if blockNr == rpc.LatestBlockNumber {
		hash = rawdb.ReadHeadBlockHash(b.db)
		number := rawdb.ReadHeaderNumber(b.db, hash)
		if number == nil {
			return nil, nil
		}
		num = *number
	} else {
		num = uint64(blockNr)
		hash = rawdb.ReadCanonicalHash(b.db, num)
	}
	h := rawdb.ReadHeader(b.db, hash, num)
	eh := evmcore.ConvertFromEthHeader(h)
	eh.Hash = h.Hash()
	return eh, nil
}
func (b *testBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*evmcore.EvmHeader, error) {
	number := rawdb.ReadHeaderNumber(b.db, hash)
	if number == nil {
		return nil, nil
	}
	h := rawdb.ReadHeader(b.db, hash, *number)
	eh := evmcore.ConvertFromEthHeader(h)
	eh.Hash = h.Hash()
	return eh, nil
}
func (b *testBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
	if number := rawdb.ReadHeaderNumber(b.db, hash); number != nil {
		return rawdb.ReadReceipts(b.db, hash, *number, params.TestChainConfig), nil
	}
	return nil, nil
}
func (b *testBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*types.Log, error) {
	number := rawdb.ReadHeaderNumber(b.db, hash)
	if number == nil {
		return nil, nil
	}
	receipts := rawdb.ReadReceipts(b.db, hash, *number, params.TestChainConfig)
	logs := make([][]*types.Log, len(receipts))
	for i, receipt := range receipts {
		logs[i] = receipt.Logs
	}
	return logs, nil
}
func (b *testBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) notify.Subscription {
	return b.txsFeed.Subscribe(ch)
}
func (b *testBackend) SubscribeLogsEvent(ch chan<- []*types.Log) notify.Subscription {
	return b.logsFeed.Subscribe(ch)
}
func (b *testBackend) SubscribeNewBlockEvent(ch chan<- evmcore.ChainHeadNotify) notify.Subscription {
	return b.blocksFeed.Subscribe(ch)
}
func (b *testBackend) EvmLogIndex() *topicsdb.Index {
	return b.logIndex
}
func TestBlockSubscription(t *testing.T) {
	t.Parallel()
	var (
		backend = newTestBackend()
		api     = NewPublicFilterAPI(backend, testConfig())
		net         = makegenesis.FakeGenesisStore(5, big.NewInt(0), big.NewInt(1)).GetGenesis()
		statedb, _  = state.New(common.Hash{}, state.NewDatabase(backend.db), nil)
		genesis     = evmcore.MustApplyGenesis(net, statedb, opt.MiB)
		chain, _, _ = evmcore.GenerateChain(
			params.TestChainConfig, genesis, backend.db, 10, nil)
		chainEvents = []evmcore.ChainHeadNotify{}
	)
	for _, blk := range chain {
		chainEvents = append(chainEvents, evmcore.ChainHeadNotify{
			Block: &evmcore.EvmBlock{
				EvmHeader:    blk.EvmHeader,
				Transactions: blk.Transactions,
			},
		})
	}
	chan0 := make(chan *types.Header)
	sub0 := api.events.SubscribeNewHeads(chan0)
	chan1 := make(chan *types.Header)
	sub1 := api.events.SubscribeNewHeads(chan1)
	go func() {  
		i1, i2 := 0, 0
		for i1 != len(chainEvents) || i2 != len(chainEvents) {
			select {
			case header := <-chan0:
				got := common.BytesToHash(header.Extra)
				if chainEvents[i1].Block.Hash != got {
					t.Errorf("sub0 received invalid hash on index %d, want %x, got %x", i1, chainEvents[i1].Block.Hash, got)
				}
				i1++
			case header := <-chan1:
				got := common.BytesToHash(header.Extra)
				if chainEvents[i2].Block.Hash != got {
					t.Errorf("sub1 received invalid hash on index %d, want %x, got %x", i2, chainEvents[i2].Block.Hash, got)
				}
				i2++
			}
		}
		sub0.Unsubscribe()
		sub1.Unsubscribe()
	}()
	time.Sleep(1 * time.Second)
	for _, e := range chainEvents {
		backend.blocksFeed.Send(e)
	}
	<-sub0.Err()
	<-sub1.Err()
}
func TestPendingTxFilter(t *testing.T) {
	t.Parallel()
	var (
		backend = newTestBackend()
		api     = NewPublicFilterAPI(backend, testConfig())
		transactions = []*types.Transaction{
			types.NewTransaction(0, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil),
			types.NewTransaction(1, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil),
			types.NewTransaction(2, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil),
			types.NewTransaction(3, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil),
			types.NewTransaction(4, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil),
		}
		hashes []common.Hash
	)
	fid0 := api.NewPendingTransactionFilter()
	time.Sleep(1 * time.Second)
	backend.txsFeed.Send(core.NewTxsEvent{Txs: transactions})
	timeout := time.Now().Add(1 * time.Second)
	for {
		results, err := api.GetFilterChanges(fid0)
		if err != nil {
			t.Fatalf("Unable to retrieve logs: %v", err)
		}
		h := results.([]common.Hash)
		hashes = append(hashes, h...)
		if len(hashes) >= len(transactions) {
			break
		}
		if time.Now().After(timeout) {
			break
		}
		time.Sleep(100 * time.Millisecond)
	}
	if len(hashes) != len(transactions) {
		t.Errorf("invalid number of transactions, want %d transactions(s), got %d", len(transactions), len(hashes))
		return
	}
	for i := range hashes {
		if hashes[i] != transactions[i].Hash() {
			t.Errorf("hashes[%d] invalid, want %x, got %x", i, transactions[i].Hash(), hashes[i])
		}
	}
}