github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/eth/filters/filter.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 19:16:37</date>
    10  //</624450088852262912>
    11  
    12  
    13  package filters
    14  
    15  import (
    16  	"context"
    17  	"errors"
    18  	"math/big"
    19  
    20  	"github.com/ethereum/go-ethereum/common"
    21  	"github.com/ethereum/go-ethereum/core"
    22  	"github.com/ethereum/go-ethereum/core/bloombits"
    23  	"github.com/ethereum/go-ethereum/core/types"
    24  	"github.com/ethereum/go-ethereum/ethdb"
    25  	"github.com/ethereum/go-ethereum/event"
    26  	"github.com/ethereum/go-ethereum/rpc"
    27  )
    28  
    29  type Backend interface {
    30  	ChainDb() ethdb.Database
    31  	EventMux() *event.TypeMux
    32  	HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error)
    33  	HeaderByHash(ctx context.Context, blockHash common.Hash) (*types.Header, error)
    34  	GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error)
    35  	GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error)
    36  
    37  	SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription
    38  	SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription
    39  	SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription
    40  	SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription
    41  
    42  	BloomStatus() (uint64, uint64)
    43  	ServiceFilter(ctx context.Context, session *bloombits.MatcherSession)
    44  }
    45  
    46  //筛选器可用于检索和筛选日志。
    47  type Filter struct {
    48  	backend Backend
    49  
    50  	db        ethdb.Database
    51  	addresses []common.Address
    52  	topics    [][]common.Hash
    53  
    54  block      common.Hash //如果筛选单个块,则阻止哈希
    55  begin, end int64       //过滤多个块时的范围间隔
    56  
    57  	matcher *bloombits.Matcher
    58  }
    59  
    60  //newrangefilter创建一个新的过滤器,它在块上使用bloom过滤器来
    61  //找出一个特定的块是否有趣。
    62  func NewRangeFilter(backend Backend, begin, end int64, addresses []common.Address, topics [][]common.Hash) *Filter {
    63  //将地址和主题筛选子句展平为单个bloombits筛选器
    64  //系统。因为bloombits不是位置的,所以不允许使用任何主题,
    65  //它被压扁成一个零字节的片。
    66  	var filters [][][]byte
    67  	if len(addresses) > 0 {
    68  		filter := make([][]byte, len(addresses))
    69  		for i, address := range addresses {
    70  			filter[i] = address.Bytes()
    71  		}
    72  		filters = append(filters, filter)
    73  	}
    74  	for _, topicList := range topics {
    75  		filter := make([][]byte, len(topicList))
    76  		for i, topic := range topicList {
    77  			filter[i] = topic.Bytes()
    78  		}
    79  		filters = append(filters, filter)
    80  	}
    81  	size, _ := backend.BloomStatus()
    82  
    83  //创建通用筛选器并将其转换为范围筛选器
    84  	filter := newFilter(backend, addresses, topics)
    85  
    86  	filter.matcher = bloombits.NewMatcher(size, filters)
    87  	filter.begin = begin
    88  	filter.end = end
    89  
    90  	return filter
    91  }
    92  
    93  //newblockfilter创建一个新的过滤器,它直接检查
    94  //用来判断它是否有趣的块。
    95  func NewBlockFilter(backend Backend, block common.Hash, addresses []common.Address, topics [][]common.Hash) *Filter {
    96  //创建通用筛选器并将其转换为块筛选器
    97  	filter := newFilter(backend, addresses, topics)
    98  	filter.block = block
    99  	return filter
   100  }
   101  
   102  //newfilter创建一个通用筛选器,该筛选器可以基于块哈希进行筛选,
   103  //或者基于范围查询。需要显式设置搜索条件。
   104  func newFilter(backend Backend, addresses []common.Address, topics [][]common.Hash) *Filter {
   105  	return &Filter{
   106  		backend:   backend,
   107  		addresses: addresses,
   108  		topics:    topics,
   109  		db:        backend.ChainDb(),
   110  	}
   111  }
   112  
   113  //日志在区块链中搜索匹配的日志条目,从
   114  //包含匹配项的第一个块,相应地更新筛选器的开头。
   115  func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) {
   116  //如果我们进行单例块过滤,执行并返回
   117  	if f.block != (common.Hash{}) {
   118  		header, err := f.backend.HeaderByHash(ctx, f.block)
   119  		if err != nil {
   120  			return nil, err
   121  		}
   122  		if header == nil {
   123  			return nil, errors.New("unknown block")
   124  		}
   125  		return f.blockLogs(ctx, header)
   126  	}
   127  //找出过滤范围的限制
   128  	header, _ := f.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
   129  	if header == nil {
   130  		return nil, nil
   131  	}
   132  	head := header.Number.Uint64()
   133  
   134  	if f.begin == -1 {
   135  		f.begin = int64(head)
   136  	}
   137  	end := uint64(f.end)
   138  	if f.end == -1 {
   139  		end = head
   140  	}
   141  //收集所有索引日志,并使用非索引日志完成
   142  	var (
   143  		logs []*types.Log
   144  		err  error
   145  	)
   146  	size, sections := f.backend.BloomStatus()
   147  	if indexed := sections * size; indexed > uint64(f.begin) {
   148  		if indexed > end {
   149  			logs, err = f.indexedLogs(ctx, end)
   150  		} else {
   151  			logs, err = f.indexedLogs(ctx, indexed-1)
   152  		}
   153  		if err != nil {
   154  			return logs, err
   155  		}
   156  	}
   157  	rest, err := f.unindexedLogs(ctx, end)
   158  	logs = append(logs, rest...)
   159  	return logs, err
   160  }
   161  
   162  //indexedlogs返回与基于bloom的筛选条件匹配的日志
   163  //在本地或通过网络可用的索引位。
   164  func (f *Filter) indexedLogs(ctx context.Context, end uint64) ([]*types.Log, error) {
   165  //创建Matcher会话并从后端请求服务
   166  	matches := make(chan uint64, 64)
   167  
   168  	session, err := f.matcher.Start(ctx, uint64(f.begin), end, matches)
   169  	if err != nil {
   170  		return nil, err
   171  	}
   172  	defer session.Close()
   173  
   174  	f.backend.ServiceFilter(ctx, session)
   175  
   176  //迭代匹配项,直到耗尽或上下文关闭
   177  	var logs []*types.Log
   178  
   179  	for {
   180  		select {
   181  		case number, ok := <-matches:
   182  //如果满足所有匹配,则中止
   183  			if !ok {
   184  				err := session.Error()
   185  				if err == nil {
   186  					f.begin = int64(end) + 1
   187  				}
   188  				return logs, err
   189  			}
   190  			f.begin = int64(number) + 1
   191  
   192  //检索建议的块并提取任何真正匹配的日志
   193  			header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(number))
   194  			if header == nil || err != nil {
   195  				return logs, err
   196  			}
   197  			found, err := f.checkMatches(ctx, header)
   198  			if err != nil {
   199  				return logs, err
   200  			}
   201  			logs = append(logs, found...)
   202  
   203  		case <-ctx.Done():
   204  			return logs, ctx.Err()
   205  		}
   206  	}
   207  }
   208  
   209  //indexedlogs返回与基于原始块的筛选条件匹配的日志
   210  //迭代和开花匹配。
   211  func (f *Filter) unindexedLogs(ctx context.Context, end uint64) ([]*types.Log, error) {
   212  	var logs []*types.Log
   213  
   214  	for ; f.begin <= int64(end); f.begin++ {
   215  		header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(f.begin))
   216  		if header == nil || err != nil {
   217  			return logs, err
   218  		}
   219  		found, err := f.blockLogs(ctx, header)
   220  		if err != nil {
   221  			return logs, err
   222  		}
   223  		logs = append(logs, found...)
   224  	}
   225  	return logs, nil
   226  }
   227  
   228  //block logs返回与单个块中的筛选条件匹配的日志。
   229  func (f *Filter) blockLogs(ctx context.Context, header *types.Header) (logs []*types.Log, err error) {
   230  	if bloomFilter(header.Bloom, f.addresses, f.topics) {
   231  		found, err := f.checkMatches(ctx, header)
   232  		if err != nil {
   233  			return logs, err
   234  		}
   235  		logs = append(logs, found...)
   236  	}
   237  	return logs, nil
   238  }
   239  
   240  //checkmatches检查属于给定头的收据是否包含
   241  //匹配筛选条件。当布卢姆滤波器发出潜在匹配信号时,调用此函数。
   242  func (f *Filter) checkMatches(ctx context.Context, header *types.Header) (logs []*types.Log, err error) {
   243  //获取块的日志
   244  	logsList, err := f.backend.GetLogs(ctx, header.Hash())
   245  	if err != nil {
   246  		return nil, err
   247  	}
   248  	var unfiltered []*types.Log
   249  	for _, logs := range logsList {
   250  		unfiltered = append(unfiltered, logs...)
   251  	}
   252  	logs = filterLogs(unfiltered, nil, nil, f.addresses, f.topics)
   253  	if len(logs) > 0 {
   254  //我们有匹配的日志,检查是否需要通过Light客户端解析完整的日志
   255  		if logs[0].TxHash == (common.Hash{}) {
   256  			receipts, err := f.backend.GetReceipts(ctx, header.Hash())
   257  			if err != nil {
   258  				return nil, err
   259  			}
   260  			unfiltered = unfiltered[:0]
   261  			for _, receipt := range receipts {
   262  				unfiltered = append(unfiltered, receipt.Logs...)
   263  			}
   264  			logs = filterLogs(unfiltered, nil, nil, f.addresses, f.topics)
   265  		}
   266  		return logs, nil
   267  	}
   268  	return nil, nil
   269  }
   270  
   271  func includes(addresses []common.Address, a common.Address) bool {
   272  	for _, addr := range addresses {
   273  		if addr == a {
   274  			return true
   275  		}
   276  	}
   277  
   278  	return false
   279  }
   280  
   281  //FieldLtG创建一个与给定标准匹配的日志片段。
   282  func filterLogs(logs []*types.Log, fromBlock, toBlock *big.Int, addresses []common.Address, topics [][]common.Hash) []*types.Log {
   283  	var ret []*types.Log
   284  Logs:
   285  	for _, log := range logs {
   286  		if fromBlock != nil && fromBlock.Int64() >= 0 && fromBlock.Uint64() > log.BlockNumber {
   287  			continue
   288  		}
   289  		if toBlock != nil && toBlock.Int64() >= 0 && toBlock.Uint64() < log.BlockNumber {
   290  			continue
   291  		}
   292  
   293  		if len(addresses) > 0 && !includes(addresses, log.Address) {
   294  			continue
   295  		}
   296  //如果到筛选的主题大于日志中的主题数量,则跳过。
   297  		if len(topics) > len(log.Topics) {
   298  			continue Logs
   299  		}
   300  		for i, sub := range topics {
   301  match := len(sub) == 0 //empty rule set == wildcard
   302  			for _, topic := range sub {
   303  				if log.Topics[i] == topic {
   304  					match = true
   305  					break
   306  				}
   307  			}
   308  			if !match {
   309  				continue Logs
   310  			}
   311  		}
   312  		ret = append(ret, log)
   313  	}
   314  	return ret
   315  }
   316  
   317  func bloomFilter(bloom types.Bloom, addresses []common.Address, topics [][]common.Hash) bool {
   318  	if len(addresses) > 0 {
   319  		var included bool
   320  		for _, addr := range addresses {
   321  			if types.BloomLookup(bloom, addr) {
   322  				included = true
   323  				break
   324  			}
   325  		}
   326  		if !included {
   327  			return false
   328  		}
   329  	}
   330  
   331  	for _, sub := range topics {
   332  included := len(sub) == 0 //空规则集==通配符
   333  		for _, topic := range sub {
   334  			if types.BloomLookup(bloom, topic) {
   335  				included = true
   336  				break
   337  			}
   338  		}
   339  		if !included {
   340  			return false
   341  		}
   342  	}
   343  	return true
   344  }
   345