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

     1  // (c) 2019-2020, 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 implements an ethereum filtering system for block,
    28  // transactions and log events.
    29  package filters
    30  
    31  import (
    32  	"context"
    33  	"errors"
    34  	"fmt"
    35  	"sync"
    36  	"time"
    37  
    38  	"github.com/ava-labs/subnet-evm/core"
    39  	"github.com/ava-labs/subnet-evm/core/bloombits"
    40  	"github.com/ava-labs/subnet-evm/core/types"
    41  	"github.com/ava-labs/subnet-evm/interfaces"
    42  	"github.com/ava-labs/subnet-evm/params"
    43  	"github.com/ava-labs/subnet-evm/rpc"
    44  	"github.com/ethereum/go-ethereum/common"
    45  	"github.com/ethereum/go-ethereum/ethdb"
    46  	"github.com/ethereum/go-ethereum/event"
    47  	"github.com/ethereum/go-ethereum/log"
    48  )
    49  
    50  // Config represents the configuration of the filter system.
    51  type Config struct {
    52  	Timeout time.Duration // how long filters stay active (default: 5min)
    53  }
    54  
    55  func (cfg Config) withDefaults() Config {
    56  	if cfg.Timeout == 0 {
    57  		cfg.Timeout = 5 * time.Minute
    58  	}
    59  	return cfg
    60  }
    61  
    62  type Backend interface {
    63  	ChainDb() ethdb.Database
    64  	HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error)
    65  	HeaderByHash(ctx context.Context, blockHash common.Hash) (*types.Header, error)
    66  	GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error)
    67  	GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error)
    68  	GetLogs(ctx context.Context, blockHash common.Hash, number uint64) ([][]*types.Log, error)
    69  
    70  	CurrentHeader() *types.Header
    71  	ChainConfig() *params.ChainConfig
    72  	SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription
    73  	SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription
    74  	SubscribeChainAcceptedEvent(ch chan<- core.ChainEvent) event.Subscription
    75  	SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription
    76  	SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription
    77  	SubscribeAcceptedLogsEvent(ch chan<- []*types.Log) event.Subscription
    78  
    79  	SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription
    80  
    81  	SubscribeAcceptedTransactionEvent(ch chan<- core.NewTxsEvent) event.Subscription
    82  
    83  	BloomStatus() (uint64, uint64)
    84  	ServiceFilter(ctx context.Context, session *bloombits.MatcherSession)
    85  
    86  	// Added to the backend interface to support limiting of logs requests
    87  	IsAllowUnfinalizedQueries() bool
    88  	LastAcceptedBlock() *types.Block
    89  	GetMaxBlocksPerRequest() int64
    90  }
    91  
    92  // FilterSystem holds resources shared by all filters.
    93  type FilterSystem struct {
    94  	// Note: go-ethereum uses an LRU cache for logs,
    95  	// instead we cache logs on the blockchain object itself.
    96  	backend Backend
    97  	cfg     *Config
    98  }
    99  
   100  // NewFilterSystem creates a filter system.
   101  func NewFilterSystem(backend Backend, config Config) *FilterSystem {
   102  	config = config.withDefaults()
   103  	return &FilterSystem{
   104  		backend: backend,
   105  		cfg:     &config,
   106  	}
   107  }
   108  
   109  // getLogs loads block logs from the backend. The backend is responsible for
   110  // performing any log caching.
   111  func (sys *FilterSystem) getLogs(ctx context.Context, blockHash common.Hash, number uint64) ([][]*types.Log, error) {
   112  	logs, err := sys.backend.GetLogs(ctx, blockHash, number)
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  	if logs == nil {
   117  		return nil, fmt.Errorf("failed to get logs for block #%d (0x%s)", number, blockHash.TerminalString())
   118  	}
   119  	return logs, nil
   120  }
   121  
   122  // Type determines the kind of filter and is used to put the filter in to
   123  // the correct bucket when added.
   124  type Type byte
   125  
   126  const (
   127  	// UnknownSubscription indicates an unknown subscription type
   128  	UnknownSubscription Type = iota
   129  	// LogsSubscription queries for new or removed (chain reorg) logs
   130  	LogsSubscription
   131  	// AcceptedLogsSubscription queries for new or removed (chain reorg) logs
   132  	AcceptedLogsSubscription
   133  	// PendingLogsSubscription queries for logs in pending blocks
   134  	PendingLogsSubscription
   135  	// MinedAndPendingLogsSubscription queries for logs in mined and pending blocks.
   136  	MinedAndPendingLogsSubscription
   137  	// PendingTransactionsSubscription queries for pending transactions entering
   138  	// the pending state
   139  	PendingTransactionsSubscription
   140  	// AcceptedTransactionsSubscription queries for accepted transactions
   141  	AcceptedTransactionsSubscription
   142  	// BlocksSubscription queries hashes for blocks that are imported
   143  	BlocksSubscription
   144  	// AcceptedBlocksSubscription queries hashes for blocks that are accepted
   145  	AcceptedBlocksSubscription
   146  	// LastIndexSubscription keeps track of the last index
   147  	LastIndexSubscription
   148  )
   149  
   150  const (
   151  	// txChanSize is the size of channel listening to NewTxsEvent.
   152  	// The number is referenced from the size of tx pool.
   153  	txChanSize = 4096
   154  	// rmLogsChanSize is the size of channel listening to RemovedLogsEvent.
   155  	rmLogsChanSize = 10
   156  	// logsChanSize is the size of channel listening to LogsEvent.
   157  	logsChanSize = 10
   158  	// chainEvChanSize is the size of channel listening to ChainEvent.
   159  	chainEvChanSize = 10
   160  )
   161  
   162  type subscription struct {
   163  	id        rpc.ID
   164  	typ       Type
   165  	created   time.Time
   166  	logsCrit  interfaces.FilterQuery
   167  	logs      chan []*types.Log
   168  	txs       chan []*types.Transaction
   169  	headers   chan *types.Header
   170  	installed chan struct{} // closed when the filter is installed
   171  	err       chan error    // closed when the filter is uninstalled
   172  }
   173  
   174  // EventSystem creates subscriptions, processes events and broadcasts them to the
   175  // subscription which match the subscription criteria.
   176  type EventSystem struct {
   177  	backend Backend
   178  	sys     *FilterSystem
   179  
   180  	// Subscriptions
   181  	txsSub           event.Subscription // Subscription for new transaction event
   182  	logsSub          event.Subscription // Subscription for new log event
   183  	logsAcceptedSub  event.Subscription // Subscription for new accepted log event
   184  	rmLogsSub        event.Subscription // Subscription for removed log event
   185  	pendingLogsSub   event.Subscription // Subscription for pending log event
   186  	chainSub         event.Subscription // Subscription for new chain event
   187  	chainAcceptedSub event.Subscription // Subscription for new chain accepted event
   188  	txsAcceptedSub   event.Subscription // Subscription for new accepted txs
   189  
   190  	// Channels
   191  	install         chan *subscription         // install filter for event notification
   192  	uninstall       chan *subscription         // remove filter for event notification
   193  	txsCh           chan core.NewTxsEvent      // Channel to receive new transactions event
   194  	logsCh          chan []*types.Log          // Channel to receive new log event
   195  	logsAcceptedCh  chan []*types.Log          // Channel to receive new accepted log event
   196  	pendingLogsCh   chan []*types.Log          // Channel to receive new log event
   197  	rmLogsCh        chan core.RemovedLogsEvent // Channel to receive removed log event
   198  	chainCh         chan core.ChainEvent       // Channel to receive new chain event
   199  	chainAcceptedCh chan core.ChainEvent       // Channel to receive new chain accepted event
   200  	txsAcceptedCh   chan core.NewTxsEvent      // Channel to receive new accepted txs
   201  }
   202  
   203  // NewEventSystem creates a new manager that listens for event on the given mux,
   204  // parses and filters them. It uses the all map to retrieve filter changes. The
   205  // work loop holds its own index that is used to forward events to filters.
   206  //
   207  // The returned manager has a loop that needs to be stopped with the Stop function
   208  // or by stopping the given mux.
   209  func NewEventSystem(sys *FilterSystem) *EventSystem {
   210  	m := &EventSystem{
   211  		sys:             sys,
   212  		backend:         sys.backend,
   213  		install:         make(chan *subscription),
   214  		uninstall:       make(chan *subscription),
   215  		txsCh:           make(chan core.NewTxsEvent, txChanSize),
   216  		logsCh:          make(chan []*types.Log, logsChanSize),
   217  		logsAcceptedCh:  make(chan []*types.Log, logsChanSize),
   218  		rmLogsCh:        make(chan core.RemovedLogsEvent, rmLogsChanSize),
   219  		pendingLogsCh:   make(chan []*types.Log, logsChanSize),
   220  		chainCh:         make(chan core.ChainEvent, chainEvChanSize),
   221  		chainAcceptedCh: make(chan core.ChainEvent, chainEvChanSize),
   222  		txsAcceptedCh:   make(chan core.NewTxsEvent, txChanSize),
   223  	}
   224  
   225  	// Subscribe events
   226  	m.txsSub = m.backend.SubscribeNewTxsEvent(m.txsCh)
   227  	m.logsSub = m.backend.SubscribeLogsEvent(m.logsCh)
   228  	m.logsAcceptedSub = m.backend.SubscribeAcceptedLogsEvent(m.logsAcceptedCh)
   229  	m.rmLogsSub = m.backend.SubscribeRemovedLogsEvent(m.rmLogsCh)
   230  	m.chainSub = m.backend.SubscribeChainEvent(m.chainCh)
   231  	m.chainAcceptedSub = m.backend.SubscribeChainAcceptedEvent(m.chainAcceptedCh)
   232  	m.pendingLogsSub = m.backend.SubscribePendingLogsEvent(m.pendingLogsCh)
   233  	m.txsAcceptedSub = m.backend.SubscribeAcceptedTransactionEvent(m.txsAcceptedCh)
   234  
   235  	// Make sure none of the subscriptions are empty
   236  	if m.txsSub == nil || m.logsSub == nil || m.logsAcceptedSub == nil || m.rmLogsSub == nil || m.chainSub == nil || m.chainAcceptedSub == nil || m.pendingLogsSub == nil || m.txsAcceptedSub == nil {
   237  		log.Crit("Subscribe for event system failed")
   238  	}
   239  
   240  	go m.eventLoop()
   241  	return m
   242  }
   243  
   244  // Subscription is created when the client registers itself for a particular event.
   245  type Subscription struct {
   246  	ID        rpc.ID
   247  	f         *subscription
   248  	es        *EventSystem
   249  	unsubOnce sync.Once
   250  }
   251  
   252  // Err returns a channel that is closed when unsubscribed.
   253  func (sub *Subscription) Err() <-chan error {
   254  	return sub.f.err
   255  }
   256  
   257  // Unsubscribe uninstalls the subscription from the event broadcast loop.
   258  func (sub *Subscription) Unsubscribe() {
   259  	sub.unsubOnce.Do(func() {
   260  	uninstallLoop:
   261  		for {
   262  			// write uninstall request and consume logs/hashes. This prevents
   263  			// the eventLoop broadcast method to deadlock when writing to the
   264  			// filter event channel while the subscription loop is waiting for
   265  			// this method to return (and thus not reading these events).
   266  			select {
   267  			case sub.es.uninstall <- sub.f:
   268  				break uninstallLoop
   269  			case <-sub.f.logs:
   270  			case <-sub.f.txs:
   271  			case <-sub.f.headers:
   272  			}
   273  		}
   274  
   275  		// wait for filter to be uninstalled in work loop before returning
   276  		// this ensures that the manager won't use the event channel which
   277  		// will probably be closed by the client asap after this method returns.
   278  		<-sub.Err()
   279  	})
   280  }
   281  
   282  // subscribe installs the subscription in the event broadcast loop.
   283  func (es *EventSystem) subscribe(sub *subscription) *Subscription {
   284  	es.install <- sub
   285  	<-sub.installed
   286  	return &Subscription{ID: sub.id, f: sub, es: es}
   287  }
   288  
   289  // SubscribeLogs creates a subscription that will write all logs matching the
   290  // given criteria to the given logs channel. Default value for the from and to
   291  // block is "latest". If the fromBlock > toBlock an error is returned.
   292  func (es *EventSystem) SubscribeLogs(crit interfaces.FilterQuery, logs chan []*types.Log) (*Subscription, error) {
   293  	var from, to rpc.BlockNumber
   294  	if crit.FromBlock == nil {
   295  		from = rpc.LatestBlockNumber
   296  	} else {
   297  		from = rpc.BlockNumber(crit.FromBlock.Int64())
   298  	}
   299  	if crit.ToBlock == nil {
   300  		to = rpc.LatestBlockNumber
   301  	} else {
   302  		to = rpc.BlockNumber(crit.ToBlock.Int64())
   303  	}
   304  
   305  	// only interested in pending logs
   306  	if from == rpc.PendingBlockNumber && to == rpc.PendingBlockNumber {
   307  		return es.subscribePendingLogs(crit, logs), nil
   308  	}
   309  	// only interested in new mined logs
   310  	if from == rpc.LatestBlockNumber && to == rpc.LatestBlockNumber {
   311  		return es.subscribeLogs(crit, logs), nil
   312  	}
   313  	// only interested in mined logs within a specific block range
   314  	if from >= 0 && to >= 0 && to >= from {
   315  		return es.subscribeLogs(crit, logs), nil
   316  	}
   317  	// interested in mined logs from a specific block number, new logs and pending logs
   318  	if from >= rpc.LatestBlockNumber && to == rpc.PendingBlockNumber {
   319  		return es.subscribeMinedPendingLogs(crit, logs), nil
   320  	}
   321  	// interested in logs from a specific block number to new mined blocks
   322  	if from >= 0 && to == rpc.LatestBlockNumber {
   323  		return es.subscribeLogs(crit, logs), nil
   324  	}
   325  	return nil, errors.New("invalid from and to block combination: from > to")
   326  }
   327  
   328  func (es *EventSystem) SubscribeAcceptedLogs(crit interfaces.FilterQuery, logs chan []*types.Log) (*Subscription, error) {
   329  	var from, to rpc.BlockNumber
   330  	if crit.FromBlock == nil {
   331  		from = rpc.LatestBlockNumber
   332  	} else {
   333  		from = rpc.BlockNumber(crit.FromBlock.Int64())
   334  	}
   335  	if crit.ToBlock == nil {
   336  		to = rpc.LatestBlockNumber
   337  	} else {
   338  		to = rpc.BlockNumber(crit.ToBlock.Int64())
   339  	}
   340  
   341  	// subscribeAcceptedLogs if filter is valid (from SubscribeLogs)
   342  	if from == rpc.PendingBlockNumber && to == rpc.PendingBlockNumber ||
   343  		from == rpc.LatestBlockNumber && to == rpc.LatestBlockNumber ||
   344  		from >= 0 && to >= 0 && to >= from ||
   345  		from >= rpc.LatestBlockNumber && to == rpc.PendingBlockNumber ||
   346  		from >= 0 && to == rpc.LatestBlockNumber {
   347  		return es.subscribeAcceptedLogs(crit, logs), nil
   348  	}
   349  
   350  	return nil, fmt.Errorf("invalid from and to block combination: from > to")
   351  }
   352  
   353  func (es *EventSystem) subscribeAcceptedLogs(crit interfaces.FilterQuery, logs chan []*types.Log) *Subscription {
   354  	sub := &subscription{
   355  		id:        rpc.NewID(),
   356  		typ:       AcceptedLogsSubscription,
   357  		logsCrit:  crit,
   358  		created:   time.Now(),
   359  		logs:      logs,
   360  		txs:       make(chan []*types.Transaction),
   361  		headers:   make(chan *types.Header),
   362  		installed: make(chan struct{}),
   363  		err:       make(chan error),
   364  	}
   365  	return es.subscribe(sub)
   366  }
   367  
   368  // subscribeMinedPendingLogs creates a subscription that returned mined and
   369  // pending logs that match the given criteria.
   370  func (es *EventSystem) subscribeMinedPendingLogs(crit interfaces.FilterQuery, logs chan []*types.Log) *Subscription {
   371  	sub := &subscription{
   372  		id:        rpc.NewID(),
   373  		typ:       MinedAndPendingLogsSubscription,
   374  		logsCrit:  crit,
   375  		created:   time.Now(),
   376  		logs:      logs,
   377  		txs:       make(chan []*types.Transaction),
   378  		headers:   make(chan *types.Header),
   379  		installed: make(chan struct{}),
   380  		err:       make(chan error),
   381  	}
   382  	return es.subscribe(sub)
   383  }
   384  
   385  // subscribeLogs creates a subscription that will write all logs matching the
   386  // given criteria to the given logs channel.
   387  func (es *EventSystem) subscribeLogs(crit interfaces.FilterQuery, logs chan []*types.Log) *Subscription {
   388  	sub := &subscription{
   389  		id:        rpc.NewID(),
   390  		typ:       LogsSubscription,
   391  		logsCrit:  crit,
   392  		created:   time.Now(),
   393  		logs:      logs,
   394  		txs:       make(chan []*types.Transaction),
   395  		headers:   make(chan *types.Header),
   396  		installed: make(chan struct{}),
   397  		err:       make(chan error),
   398  	}
   399  	return es.subscribe(sub)
   400  }
   401  
   402  // subscribePendingLogs creates a subscription that writes contract event logs for
   403  // transactions that enter the transaction pool.
   404  func (es *EventSystem) subscribePendingLogs(crit interfaces.FilterQuery, logs chan []*types.Log) *Subscription {
   405  	sub := &subscription{
   406  		id:        rpc.NewID(),
   407  		typ:       PendingLogsSubscription,
   408  		logsCrit:  crit,
   409  		created:   time.Now(),
   410  		logs:      logs,
   411  		txs:       make(chan []*types.Transaction),
   412  		headers:   make(chan *types.Header),
   413  		installed: make(chan struct{}),
   414  		err:       make(chan error),
   415  	}
   416  	return es.subscribe(sub)
   417  }
   418  
   419  // SubscribeNewHeads creates a subscription that writes the header of a block that is
   420  // imported in the chain.
   421  func (es *EventSystem) SubscribeNewHeads(headers chan *types.Header) *Subscription {
   422  	sub := &subscription{
   423  		id:        rpc.NewID(),
   424  		typ:       BlocksSubscription,
   425  		created:   time.Now(),
   426  		logs:      make(chan []*types.Log),
   427  		txs:       make(chan []*types.Transaction),
   428  		headers:   headers,
   429  		installed: make(chan struct{}),
   430  		err:       make(chan error),
   431  	}
   432  	return es.subscribe(sub)
   433  }
   434  
   435  // SubscribeAcceptedHeads creates a subscription that writes the header of an accepted block that is
   436  // imported in the chain.
   437  func (es *EventSystem) SubscribeAcceptedHeads(headers chan *types.Header) *Subscription {
   438  	sub := &subscription{
   439  		id:        rpc.NewID(),
   440  		typ:       AcceptedBlocksSubscription,
   441  		created:   time.Now(),
   442  		logs:      make(chan []*types.Log),
   443  		txs:       make(chan []*types.Transaction),
   444  		headers:   headers,
   445  		installed: make(chan struct{}),
   446  		err:       make(chan error),
   447  	}
   448  	return es.subscribe(sub)
   449  }
   450  
   451  // SubscribePendingTxs creates a subscription that writes transactions for
   452  // transactions that enter the transaction pool.
   453  func (es *EventSystem) SubscribePendingTxs(txs chan []*types.Transaction) *Subscription {
   454  	sub := &subscription{
   455  		id:        rpc.NewID(),
   456  		typ:       PendingTransactionsSubscription,
   457  		created:   time.Now(),
   458  		logs:      make(chan []*types.Log),
   459  		txs:       txs,
   460  		headers:   make(chan *types.Header),
   461  		installed: make(chan struct{}),
   462  		err:       make(chan error),
   463  	}
   464  	return es.subscribe(sub)
   465  }
   466  
   467  // SubscribeAcceptedTxs creates a subscription that writes transactions for
   468  // transactions have been accepted.
   469  func (es *EventSystem) SubscribeAcceptedTxs(txs chan []*types.Transaction) *Subscription {
   470  	sub := &subscription{
   471  		id:        rpc.NewID(),
   472  		typ:       AcceptedTransactionsSubscription,
   473  		created:   time.Now(),
   474  		logs:      make(chan []*types.Log),
   475  		txs:       txs,
   476  		headers:   make(chan *types.Header),
   477  		installed: make(chan struct{}),
   478  		err:       make(chan error),
   479  	}
   480  	return es.subscribe(sub)
   481  }
   482  
   483  type filterIndex map[Type]map[rpc.ID]*subscription
   484  
   485  func (es *EventSystem) handleLogs(filters filterIndex, ev []*types.Log) {
   486  	if len(ev) == 0 {
   487  		return
   488  	}
   489  	for _, f := range filters[LogsSubscription] {
   490  		matchedLogs := filterLogs(ev, f.logsCrit.FromBlock, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics)
   491  		if len(matchedLogs) > 0 {
   492  			f.logs <- matchedLogs
   493  		}
   494  	}
   495  }
   496  
   497  func (es *EventSystem) handleAcceptedLogs(filters filterIndex, ev []*types.Log) {
   498  	if len(ev) == 0 {
   499  		return
   500  	}
   501  	for _, f := range filters[AcceptedLogsSubscription] {
   502  		matchedLogs := filterLogs(ev, f.logsCrit.FromBlock, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics)
   503  		if len(matchedLogs) > 0 {
   504  			f.logs <- matchedLogs
   505  		}
   506  	}
   507  }
   508  
   509  func (es *EventSystem) handlePendingLogs(filters filterIndex, ev []*types.Log) {
   510  	if len(ev) == 0 {
   511  		return
   512  	}
   513  	for _, f := range filters[PendingLogsSubscription] {
   514  		matchedLogs := filterLogs(ev, nil, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics)
   515  		if len(matchedLogs) > 0 {
   516  			f.logs <- matchedLogs
   517  		}
   518  	}
   519  }
   520  
   521  func (es *EventSystem) handleTxsEvent(filters filterIndex, ev core.NewTxsEvent, accepted bool) {
   522  	for _, f := range filters[PendingTransactionsSubscription] {
   523  		f.txs <- ev.Txs
   524  	}
   525  	if accepted {
   526  		for _, f := range filters[AcceptedTransactionsSubscription] {
   527  			f.txs <- ev.Txs
   528  		}
   529  	}
   530  }
   531  
   532  func (es *EventSystem) handleChainEvent(filters filterIndex, ev core.ChainEvent) {
   533  	for _, f := range filters[BlocksSubscription] {
   534  		f.headers <- ev.Block.Header()
   535  	}
   536  }
   537  
   538  func (es *EventSystem) handleChainAcceptedEvent(filters filterIndex, ev core.ChainEvent) {
   539  	for _, f := range filters[AcceptedBlocksSubscription] {
   540  		f.headers <- ev.Block.Header()
   541  	}
   542  }
   543  
   544  // eventLoop (un)installs filters and processes mux events.
   545  func (es *EventSystem) eventLoop() {
   546  	// Ensure all subscriptions get cleaned up
   547  	defer func() {
   548  		es.txsSub.Unsubscribe()
   549  		es.logsSub.Unsubscribe()
   550  		es.logsAcceptedSub.Unsubscribe()
   551  		es.rmLogsSub.Unsubscribe()
   552  		es.pendingLogsSub.Unsubscribe()
   553  		es.chainSub.Unsubscribe()
   554  		es.chainAcceptedSub.Unsubscribe()
   555  		es.txsAcceptedSub.Unsubscribe()
   556  	}()
   557  
   558  	index := make(filterIndex)
   559  	for i := UnknownSubscription; i < LastIndexSubscription; i++ {
   560  		index[i] = make(map[rpc.ID]*subscription)
   561  	}
   562  
   563  	for {
   564  		select {
   565  		case ev := <-es.txsCh:
   566  			es.handleTxsEvent(index, ev, false)
   567  		case ev := <-es.logsCh:
   568  			es.handleLogs(index, ev)
   569  		case ev := <-es.logsAcceptedCh:
   570  			es.handleAcceptedLogs(index, ev)
   571  		case ev := <-es.rmLogsCh:
   572  			es.handleLogs(index, ev.Logs)
   573  		case ev := <-es.pendingLogsCh:
   574  			es.handlePendingLogs(index, ev)
   575  		case ev := <-es.chainCh:
   576  			es.handleChainEvent(index, ev)
   577  		case ev := <-es.chainAcceptedCh:
   578  			es.handleChainAcceptedEvent(index, ev)
   579  		case ev := <-es.txsAcceptedCh:
   580  			es.handleTxsEvent(index, ev, true)
   581  
   582  		case f := <-es.install:
   583  			if f.typ == MinedAndPendingLogsSubscription {
   584  				// the type are logs and pending logs subscriptions
   585  				index[LogsSubscription][f.id] = f
   586  				index[PendingLogsSubscription][f.id] = f
   587  			} else {
   588  				index[f.typ][f.id] = f
   589  			}
   590  			close(f.installed)
   591  
   592  		case f := <-es.uninstall:
   593  			if f.typ == MinedAndPendingLogsSubscription {
   594  				// the type are logs and pending logs subscriptions
   595  				delete(index[LogsSubscription], f.id)
   596  				delete(index[PendingLogsSubscription], f.id)
   597  			} else {
   598  				delete(index[f.typ], f.id)
   599  			}
   600  			close(f.err)
   601  
   602  		// System stopped
   603  		case <-es.txsSub.Err():
   604  			return
   605  		case <-es.logsSub.Err():
   606  			return
   607  		case <-es.logsAcceptedSub.Err():
   608  			return
   609  		case <-es.rmLogsSub.Err():
   610  			return
   611  		case <-es.chainSub.Err():
   612  			return
   613  		case <-es.chainAcceptedSub.Err():
   614  			return
   615  		case <-es.txsAcceptedSub.Err():
   616  			return
   617  		}
   618  	}
   619  }