github.com/karalabe/go-ethereum@v0.8.5/core/filter.go (about)

     1  package core
     2  
     3  import (
     4  	"bytes"
     5  	"math"
     6  
     7  	"github.com/ethereum/go-ethereum/core/types"
     8  	"github.com/ethereum/go-ethereum/state"
     9  )
    10  
    11  type AccountChange struct {
    12  	Address, StateAddress []byte
    13  }
    14  
    15  type FilterOptions struct {
    16  	Earliest int64
    17  	Latest   int64
    18  
    19  	Address [][]byte
    20  	Topics  [][]byte
    21  
    22  	Skip int
    23  	Max  int
    24  }
    25  
    26  // Filtering interface
    27  type Filter struct {
    28  	eth      Backend
    29  	earliest int64
    30  	latest   int64
    31  	skip     int
    32  	address  [][]byte
    33  	max      int
    34  	topics   [][]byte
    35  
    36  	BlockCallback   func(*types.Block)
    37  	PendingCallback func(*types.Block)
    38  	LogsCallback    func(state.Logs)
    39  }
    40  
    41  // Create a new filter which uses a bloom filter on blocks to figure out whether a particular block
    42  // is interesting or not.
    43  func NewFilter(eth Backend) *Filter {
    44  	return &Filter{eth: eth}
    45  }
    46  
    47  func (self *Filter) SetOptions(options FilterOptions) {
    48  	self.earliest = options.Earliest
    49  	self.latest = options.Latest
    50  	self.skip = options.Skip
    51  	self.max = options.Max
    52  	self.address = options.Address
    53  	self.topics = options.Topics
    54  
    55  }
    56  
    57  // Set the earliest and latest block for filtering.
    58  // -1 = latest block (i.e., the current block)
    59  // hash = particular hash from-to
    60  func (self *Filter) SetEarliestBlock(earliest int64) {
    61  	self.earliest = earliest
    62  }
    63  
    64  func (self *Filter) SetLatestBlock(latest int64) {
    65  	self.latest = latest
    66  }
    67  
    68  func (self *Filter) SetAddress(addr [][]byte) {
    69  	self.address = addr
    70  }
    71  
    72  func (self *Filter) SetTopics(topics [][]byte) {
    73  	self.topics = topics
    74  }
    75  
    76  func (self *Filter) SetMax(max int) {
    77  	self.max = max
    78  }
    79  
    80  func (self *Filter) SetSkip(skip int) {
    81  	self.skip = skip
    82  }
    83  
    84  // Run filters logs with the current parameters set
    85  func (self *Filter) Find() state.Logs {
    86  	earliestBlock := self.eth.ChainManager().CurrentBlock()
    87  	var earliestBlockNo uint64 = uint64(self.earliest)
    88  	if self.earliest == -1 {
    89  		earliestBlockNo = earliestBlock.NumberU64()
    90  	}
    91  	var latestBlockNo uint64 = uint64(self.latest)
    92  	if self.latest == -1 {
    93  		latestBlockNo = earliestBlock.NumberU64()
    94  	}
    95  
    96  	var (
    97  		logs  state.Logs
    98  		block = self.eth.ChainManager().GetBlockByNumber(latestBlockNo)
    99  		quit  bool
   100  	)
   101  	for i := 0; !quit && block != nil; i++ {
   102  		// Quit on latest
   103  		switch {
   104  		case block.NumberU64() == earliestBlockNo, block.NumberU64() == 0:
   105  			quit = true
   106  		case self.max <= len(logs):
   107  			break
   108  		}
   109  
   110  		// Use bloom filtering to see if this block is interesting given the
   111  		// current parameters
   112  		if self.bloomFilter(block) {
   113  			// Get the logs of the block
   114  			unfiltered, err := self.eth.BlockProcessor().GetLogs(block)
   115  			if err != nil {
   116  				chainlogger.Warnln("err: filter get logs ", err)
   117  
   118  				break
   119  			}
   120  
   121  			logs = append(logs, self.FilterLogs(unfiltered)...)
   122  		}
   123  
   124  		block = self.eth.ChainManager().GetBlock(block.ParentHash())
   125  	}
   126  
   127  	skip := int(math.Min(float64(len(logs)), float64(self.skip)))
   128  
   129  	return logs[skip:]
   130  }
   131  
   132  func includes(addresses [][]byte, a []byte) bool {
   133  	for _, addr := range addresses {
   134  		if !bytes.Equal(addr, a) {
   135  			return false
   136  		}
   137  	}
   138  
   139  	return true
   140  }
   141  
   142  func (self *Filter) FilterLogs(logs state.Logs) state.Logs {
   143  	var ret state.Logs
   144  
   145  	// Filter the logs for interesting stuff
   146  Logs:
   147  	for _, log := range logs {
   148  		if !includes(self.address, log.Address()) {
   149  			continue
   150  		}
   151  
   152  		max := int(math.Min(float64(len(self.topics)), float64(len(log.Topics()))))
   153  		for i := 0; i < max; i++ {
   154  			if !bytes.Equal(log.Topics()[i], self.topics[i]) {
   155  				continue Logs
   156  			}
   157  		}
   158  
   159  		ret = append(ret, log)
   160  	}
   161  
   162  	return ret
   163  }
   164  
   165  func (self *Filter) bloomFilter(block *types.Block) bool {
   166  	if len(self.address) > 0 {
   167  		var included bool
   168  		for _, addr := range self.address {
   169  			if types.BloomLookup(block.Bloom(), addr) {
   170  				included = true
   171  				break
   172  			}
   173  		}
   174  
   175  		if !included {
   176  			return false
   177  		}
   178  	}
   179  
   180  	for _, topic := range self.topics {
   181  		if !types.BloomLookup(block.Bloom(), topic) {
   182  			return false
   183  		}
   184  	}
   185  
   186  	return true
   187  }