github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/eth/filters/filter_system.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 12:09:38</date>
    10  //</624342635535929344>
    11  
    12  
    13  //包过滤器为块实现以太坊过滤系统,
    14  //事务和日志事件。
    15  package filters
    16  
    17  import (
    18  	"context"
    19  	"errors"
    20  	"fmt"
    21  	"sync"
    22  	"time"
    23  
    24  	ethereum "github.com/ethereum/go-ethereum"
    25  	"github.com/ethereum/go-ethereum/common"
    26  	"github.com/ethereum/go-ethereum/core"
    27  	"github.com/ethereum/go-ethereum/core/rawdb"
    28  	"github.com/ethereum/go-ethereum/core/types"
    29  	"github.com/ethereum/go-ethereum/event"
    30  	"github.com/ethereum/go-ethereum/log"
    31  	"github.com/ethereum/go-ethereum/rpc"
    32  )
    33  
    34  //类型确定筛选器的类型,并用于将筛选器放入
    35  //添加正确的桶。
    36  type Type byte
    37  
    38  const (
    39  //UnknownSubscription表示未知的订阅类型
    40  	UnknownSubscription Type = iota
    41  //新日志或已删除日志的日志订阅查询(chain reorg)
    42  	LogsSubscription
    43  //PendingLogs对挂起块中的日志的订阅查询
    44  	PendingLogsSubscription
    45  //MinedAndPendingLogs对挖掘和挂起块中的日志的订阅查询。
    46  	MinedAndPendingLogsSubscription
    47  //PendingtTransactionsSubscription查询待处理的Tx哈希
    48  //进入挂起状态的事务
    49  	PendingTransactionsSubscription
    50  //块订阅查询导入块的哈希
    51  	BlocksSubscription
    52  //LastSubscription跟踪最后一个索引
    53  	LastIndexSubscription
    54  )
    55  
    56  const (
    57  
    58  //txchanSize是侦听newtxSevent的频道的大小。
    59  //该数字是根据Tx池的大小引用的。
    60  	txChanSize = 4096
    61  //rmlogschansize是侦听removedlogsevent的通道的大小。
    62  	rmLogsChanSize = 10
    63  //logschansize是侦听logsevent的通道的大小。
    64  	logsChanSize = 10
    65  //ChainevChansize是侦听ChainEvent的通道的大小。
    66  	chainEvChanSize = 10
    67  )
    68  
    69  var (
    70  	ErrInvalidSubscriptionID = errors.New("invalid id")
    71  )
    72  
    73  type subscription struct {
    74  	id        rpc.ID
    75  	typ       Type
    76  	created   time.Time
    77  	logsCrit  ethereum.FilterQuery
    78  	logs      chan []*types.Log
    79  	hashes    chan []common.Hash
    80  	headers   chan *types.Header
    81  installed chan struct{} //安装过滤器时关闭
    82  err       chan error    //卸载筛选器时关闭
    83  }
    84  
    85  //EventSystem创建订阅、处理事件并将其广播到
    86  //符合订阅条件的订阅。
    87  type EventSystem struct {
    88  	mux       *event.TypeMux
    89  	backend   Backend
    90  	lightMode bool
    91  	lastHead  *types.Header
    92  
    93  //订阅
    94  txsSub        event.Subscription         //订阅新事务事件
    95  logsSub       event.Subscription         //订阅新日志事件
    96  rmLogsSub     event.Subscription         //已删除日志事件的订阅
    97  chainSub      event.Subscription         //订阅新的链事件
    98  pendingLogSub *event.TypeMuxSubscription //订阅挂起日志事件
    99  
   100  //渠道
   101  install   chan *subscription         //安装事件通知筛选器
   102  uninstall chan *subscription         //删除事件通知筛选器
   103  txsCh     chan core.NewTxsEvent      //接收新事务事件的通道
   104  logsCh    chan []*types.Log          //接收新日志事件的通道
   105  rmLogsCh  chan core.RemovedLogsEvent //接收已删除日志事件的通道
   106  chainCh   chan core.ChainEvent       //接收新链事件的通道
   107  }
   108  
   109  //newEventSystem创建一个新的管理器,用于侦听给定mux上的事件,
   110  //分析并过滤它们。它使用全部映射来检索过滤器更改。这个
   111  //工作循环保存自己的索引,用于将事件转发到筛选器。
   112  //
   113  //返回的管理器有一个需要用stop函数停止的循环
   114  //或者停止给定的多路复用器。
   115  func NewEventSystem(mux *event.TypeMux, backend Backend, lightMode bool) *EventSystem {
   116  	m := &EventSystem{
   117  		mux:       mux,
   118  		backend:   backend,
   119  		lightMode: lightMode,
   120  		install:   make(chan *subscription),
   121  		uninstall: make(chan *subscription),
   122  		txsCh:     make(chan core.NewTxsEvent, txChanSize),
   123  		logsCh:    make(chan []*types.Log, logsChanSize),
   124  		rmLogsCh:  make(chan core.RemovedLogsEvent, rmLogsChanSize),
   125  		chainCh:   make(chan core.ChainEvent, chainEvChanSize),
   126  	}
   127  
   128  //订阅事件
   129  	m.txsSub = m.backend.SubscribeNewTxsEvent(m.txsCh)
   130  	m.logsSub = m.backend.SubscribeLogsEvent(m.logsCh)
   131  	m.rmLogsSub = m.backend.SubscribeRemovedLogsEvent(m.rmLogsCh)
   132  	m.chainSub = m.backend.SubscribeChainEvent(m.chainCh)
   133  //TODO(RJL493456442):使用feed订阅挂起的日志事件
   134  	m.pendingLogSub = m.mux.Subscribe(core.PendingLogsEvent{})
   135  
   136  //确保所有订阅都不为空
   137  	if m.txsSub == nil || m.logsSub == nil || m.rmLogsSub == nil || m.chainSub == nil ||
   138  		m.pendingLogSub.Closed() {
   139  		log.Crit("Subscribe for event system failed")
   140  	}
   141  
   142  	go m.eventLoop()
   143  	return m
   144  }
   145  
   146  //订阅是在客户端为特定事件注册自身时创建的。
   147  type Subscription struct {
   148  	ID        rpc.ID
   149  	f         *subscription
   150  	es        *EventSystem
   151  	unsubOnce sync.Once
   152  }
   153  
   154  //err返回在取消订阅时关闭的通道。
   155  func (sub *Subscription) Err() <-chan error {
   156  	return sub.f.err
   157  }
   158  
   159  //取消订阅从事件广播循环中卸载订阅。
   160  func (sub *Subscription) Unsubscribe() {
   161  	sub.unsubOnce.Do(func() {
   162  	uninstallLoop:
   163  		for {
   164  //编写卸载请求并使用日志/哈希。这防止
   165  //写入时死锁的EventLoop广播方法
   166  //订阅循环等待时筛选事件通道
   167  //此方法返回(因此不读取这些事件)。
   168  			select {
   169  			case sub.es.uninstall <- sub.f:
   170  				break uninstallLoop
   171  			case <-sub.f.logs:
   172  			case <-sub.f.hashes:
   173  			case <-sub.f.headers:
   174  			}
   175  		}
   176  
   177  //在返回之前,请等待在工作循环中卸载筛选器
   178  //这样可以确保管理器不会使用
   179  //在该方法返回后,客户端可能会尽快关闭。
   180  		<-sub.Err()
   181  	})
   182  }
   183  
   184  //订阅在事件广播循环中安装订阅。
   185  func (es *EventSystem) subscribe(sub *subscription) *Subscription {
   186  	es.install <- sub
   187  	<-sub.installed
   188  	return &Subscription{ID: sub.id, f: sub, es: es}
   189  }
   190  
   191  //subscriptLogs创建一个订阅,该订阅将写入与
   192  //给定日志通道的给定条件。从和到的默认值
   193  //块为“最新”。如果FromBlock>ToBlock,则返回错误。
   194  func (es *EventSystem) SubscribeLogs(crit ethereum.FilterQuery, logs chan []*types.Log) (*Subscription, error) {
   195  	var from, to rpc.BlockNumber
   196  	if crit.FromBlock == nil {
   197  		from = rpc.LatestBlockNumber
   198  	} else {
   199  		from = rpc.BlockNumber(crit.FromBlock.Int64())
   200  	}
   201  	if crit.ToBlock == nil {
   202  		to = rpc.LatestBlockNumber
   203  	} else {
   204  		to = rpc.BlockNumber(crit.ToBlock.Int64())
   205  	}
   206  
   207  //只对挂起的日志感兴趣
   208  	if from == rpc.PendingBlockNumber && to == rpc.PendingBlockNumber {
   209  		return es.subscribePendingLogs(crit, logs), nil
   210  	}
   211  //只对新开采的原木感兴趣
   212  	if from == rpc.LatestBlockNumber && to == rpc.LatestBlockNumber {
   213  		return es.subscribeLogs(crit, logs), nil
   214  	}
   215  //仅对特定区块范围内的开采原木感兴趣
   216  	if from >= 0 && to >= 0 && to >= from {
   217  		return es.subscribeLogs(crit, logs), nil
   218  	}
   219  //对特定块号、新日志和挂起日志中的挖掘日志感兴趣
   220  	if from >= rpc.LatestBlockNumber && to == rpc.PendingBlockNumber {
   221  		return es.subscribeMinedPendingLogs(crit, logs), nil
   222  	}
   223  //对从特定区块编号到新开采区块的原木感兴趣
   224  	if from >= 0 && to == rpc.LatestBlockNumber {
   225  		return es.subscribeLogs(crit, logs), nil
   226  	}
   227  	return nil, fmt.Errorf("invalid from and to block combination: from > to")
   228  }
   229  
   230  //subscripbeMinedPendingLogs创建一个返回已挖掘和
   231  //与给定条件匹配的挂起日志。
   232  func (es *EventSystem) subscribeMinedPendingLogs(crit ethereum.FilterQuery, logs chan []*types.Log) *Subscription {
   233  	sub := &subscription{
   234  		id:        rpc.NewID(),
   235  		typ:       MinedAndPendingLogsSubscription,
   236  		logsCrit:  crit,
   237  		created:   time.Now(),
   238  		logs:      logs,
   239  		hashes:    make(chan []common.Hash),
   240  		headers:   make(chan *types.Header),
   241  		installed: make(chan struct{}),
   242  		err:       make(chan error),
   243  	}
   244  	return es.subscribe(sub)
   245  }
   246  
   247  //subscriptLogs创建一个订阅,该订阅将写入与
   248  //给定日志通道的给定条件。
   249  func (es *EventSystem) subscribeLogs(crit ethereum.FilterQuery, logs chan []*types.Log) *Subscription {
   250  	sub := &subscription{
   251  		id:        rpc.NewID(),
   252  		typ:       LogsSubscription,
   253  		logsCrit:  crit,
   254  		created:   time.Now(),
   255  		logs:      logs,
   256  		hashes:    make(chan []common.Hash),
   257  		headers:   make(chan *types.Header),
   258  		installed: make(chan struct{}),
   259  		err:       make(chan error),
   260  	}
   261  	return es.subscribe(sub)
   262  }
   263  
   264  //subscribePendingLogs创建一个订阅,该订阅为
   265  //进入事务池的事务。
   266  func (es *EventSystem) subscribePendingLogs(crit ethereum.FilterQuery, logs chan []*types.Log) *Subscription {
   267  	sub := &subscription{
   268  		id:        rpc.NewID(),
   269  		typ:       PendingLogsSubscription,
   270  		logsCrit:  crit,
   271  		created:   time.Now(),
   272  		logs:      logs,
   273  		hashes:    make(chan []common.Hash),
   274  		headers:   make(chan *types.Header),
   275  		installed: make(chan struct{}),
   276  		err:       make(chan error),
   277  	}
   278  	return es.subscribe(sub)
   279  }
   280  
   281  //SUBSCRIBENEWEADS创建一个订阅,该订阅写入的块头为
   282  //进口链。
   283  func (es *EventSystem) SubscribeNewHeads(headers chan *types.Header) *Subscription {
   284  	sub := &subscription{
   285  		id:        rpc.NewID(),
   286  		typ:       BlocksSubscription,
   287  		created:   time.Now(),
   288  		logs:      make(chan []*types.Log),
   289  		hashes:    make(chan []common.Hash),
   290  		headers:   headers,
   291  		installed: make(chan struct{}),
   292  		err:       make(chan error),
   293  	}
   294  	return es.subscribe(sub)
   295  }
   296  
   297  //订阅BuffixTxs创建一个订阅事务哈希的订阅。
   298  //进入事务池的事务。
   299  func (es *EventSystem) SubscribePendingTxs(hashes chan []common.Hash) *Subscription {
   300  	sub := &subscription{
   301  		id:        rpc.NewID(),
   302  		typ:       PendingTransactionsSubscription,
   303  		created:   time.Now(),
   304  		logs:      make(chan []*types.Log),
   305  		hashes:    hashes,
   306  		headers:   make(chan *types.Header),
   307  		installed: make(chan struct{}),
   308  		err:       make(chan error),
   309  	}
   310  	return es.subscribe(sub)
   311  }
   312  
   313  type filterIndex map[Type]map[rpc.ID]*subscription
   314  
   315  //将事件广播到符合条件的筛选器。
   316  func (es *EventSystem) broadcast(filters filterIndex, ev interface{}) {
   317  	if ev == nil {
   318  		return
   319  	}
   320  
   321  	switch e := ev.(type) {
   322  	case []*types.Log:
   323  		if len(e) > 0 {
   324  			for _, f := range filters[LogsSubscription] {
   325  				if matchedLogs := filterLogs(e, f.logsCrit.FromBlock, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 {
   326  					f.logs <- matchedLogs
   327  				}
   328  			}
   329  		}
   330  	case core.RemovedLogsEvent:
   331  		for _, f := range filters[LogsSubscription] {
   332  			if matchedLogs := filterLogs(e.Logs, f.logsCrit.FromBlock, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 {
   333  				f.logs <- matchedLogs
   334  			}
   335  		}
   336  	case *event.TypeMuxEvent:
   337  		if muxe, ok := e.Data.(core.PendingLogsEvent); ok {
   338  			for _, f := range filters[PendingLogsSubscription] {
   339  				if e.Time.After(f.created) {
   340  					if matchedLogs := filterLogs(muxe.Logs, nil, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 {
   341  						f.logs <- matchedLogs
   342  					}
   343  				}
   344  			}
   345  		}
   346  	case core.NewTxsEvent:
   347  		hashes := make([]common.Hash, 0, len(e.Txs))
   348  		for _, tx := range e.Txs {
   349  			hashes = append(hashes, tx.Hash())
   350  		}
   351  		for _, f := range filters[PendingTransactionsSubscription] {
   352  			f.hashes <- hashes
   353  		}
   354  	case core.ChainEvent:
   355  		for _, f := range filters[BlocksSubscription] {
   356  			f.headers <- e.Block.Header()
   357  		}
   358  		if es.lightMode && len(filters[LogsSubscription]) > 0 {
   359  			es.lightFilterNewHead(e.Block.Header(), func(header *types.Header, remove bool) {
   360  				for _, f := range filters[LogsSubscription] {
   361  					if matchedLogs := es.lightFilterLogs(header, f.logsCrit.Addresses, f.logsCrit.Topics, remove); len(matchedLogs) > 0 {
   362  						f.logs <- matchedLogs
   363  					}
   364  				}
   365  			})
   366  		}
   367  	}
   368  }
   369  
   370  func (es *EventSystem) lightFilterNewHead(newHeader *types.Header, callBack func(*types.Header, bool)) {
   371  	oldh := es.lastHead
   372  	es.lastHead = newHeader
   373  	if oldh == nil {
   374  		return
   375  	}
   376  	newh := newHeader
   377  //查找公共祖先,创建回滚和新块哈希的列表
   378  	var oldHeaders, newHeaders []*types.Header
   379  	for oldh.Hash() != newh.Hash() {
   380  		if oldh.Number.Uint64() >= newh.Number.Uint64() {
   381  			oldHeaders = append(oldHeaders, oldh)
   382  			oldh = rawdb.ReadHeader(es.backend.ChainDb(), oldh.ParentHash, oldh.Number.Uint64()-1)
   383  		}
   384  		if oldh.Number.Uint64() < newh.Number.Uint64() {
   385  			newHeaders = append(newHeaders, newh)
   386  			newh = rawdb.ReadHeader(es.backend.ChainDb(), newh.ParentHash, newh.Number.Uint64()-1)
   387  			if newh == nil {
   388  //当CHT同步时发生,无需执行任何操作
   389  				newh = oldh
   390  			}
   391  		}
   392  	}
   393  //回滚旧块
   394  	for _, h := range oldHeaders {
   395  		callBack(h, true)
   396  	}
   397  //检查新块(数组顺序相反)
   398  	for i := len(newHeaders) - 1; i >= 0; i-- {
   399  		callBack(newHeaders[i], false)
   400  	}
   401  }
   402  
   403  //在轻型客户端模式下筛选单个头的日志
   404  func (es *EventSystem) lightFilterLogs(header *types.Header, addresses []common.Address, topics [][]common.Hash, remove bool) []*types.Log {
   405  	if bloomFilter(header.Bloom, addresses, topics) {
   406  //获取块的日志
   407  		ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
   408  		defer cancel()
   409  		logsList, err := es.backend.GetLogs(ctx, header.Hash())
   410  		if err != nil {
   411  			return nil
   412  		}
   413  		var unfiltered []*types.Log
   414  		for _, logs := range logsList {
   415  			for _, log := range logs {
   416  				logcopy := *log
   417  				logcopy.Removed = remove
   418  				unfiltered = append(unfiltered, &logcopy)
   419  			}
   420  		}
   421  		logs := filterLogs(unfiltered, nil, nil, addresses, topics)
   422  		if len(logs) > 0 && logs[0].TxHash == (common.Hash{}) {
   423  //我们有匹配但非派生的日志
   424  			receipts, err := es.backend.GetReceipts(ctx, header.Hash())
   425  			if err != nil {
   426  				return nil
   427  			}
   428  			unfiltered = unfiltered[:0]
   429  			for _, receipt := range receipts {
   430  				for _, log := range receipt.Logs {
   431  					logcopy := *log
   432  					logcopy.Removed = remove
   433  					unfiltered = append(unfiltered, &logcopy)
   434  				}
   435  			}
   436  			logs = filterLogs(unfiltered, nil, nil, addresses, topics)
   437  		}
   438  		return logs
   439  	}
   440  	return nil
   441  }
   442  
   443  //EventLoop(un)安装过滤器并处理mux事件。
   444  func (es *EventSystem) eventLoop() {
   445  //确保清除所有订阅
   446  	defer func() {
   447  		es.pendingLogSub.Unsubscribe()
   448  		es.txsSub.Unsubscribe()
   449  		es.logsSub.Unsubscribe()
   450  		es.rmLogsSub.Unsubscribe()
   451  		es.chainSub.Unsubscribe()
   452  	}()
   453  
   454  	index := make(filterIndex)
   455  	for i := UnknownSubscription; i < LastIndexSubscription; i++ {
   456  		index[i] = make(map[rpc.ID]*subscription)
   457  	}
   458  
   459  	for {
   460  		select {
   461  //处理订阅的事件
   462  		case ev := <-es.txsCh:
   463  			es.broadcast(index, ev)
   464  		case ev := <-es.logsCh:
   465  			es.broadcast(index, ev)
   466  		case ev := <-es.rmLogsCh:
   467  			es.broadcast(index, ev)
   468  		case ev := <-es.chainCh:
   469  			es.broadcast(index, ev)
   470  		case ev, active := <-es.pendingLogSub.Chan():
   471  if !active { //系统停止
   472  				return
   473  			}
   474  			es.broadcast(index, ev)
   475  
   476  		case f := <-es.install:
   477  			if f.typ == MinedAndPendingLogsSubscription {
   478  //类型为日志和挂起的日志订阅
   479  				index[LogsSubscription][f.id] = f
   480  				index[PendingLogsSubscription][f.id] = f
   481  			} else {
   482  				index[f.typ][f.id] = f
   483  			}
   484  			close(f.installed)
   485  
   486  		case f := <-es.uninstall:
   487  			if f.typ == MinedAndPendingLogsSubscription {
   488  //类型为日志和挂起的日志订阅
   489  				delete(index[LogsSubscription], f.id)
   490  				delete(index[PendingLogsSubscription], f.id)
   491  			} else {
   492  				delete(index[f.typ], f.id)
   493  			}
   494  			close(f.err)
   495  
   496  //系统停止
   497  		case <-es.txsSub.Err():
   498  			return
   499  		case <-es.logsSub.Err():
   500  			return
   501  		case <-es.rmLogsSub.Err():
   502  			return
   503  		case <-es.chainSub.Err():
   504  			return
   505  		}
   506  	}
   507  }
   508