github.com/jeffallen/go-ethereum@v1.1.4-0.20150910155051-571d3236c49c/core/filter.go (about)

     1  // Copyright 2014 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package core
    18  
    19  import (
    20  	"math"
    21  
    22  	"github.com/ethereum/go-ethereum/common"
    23  	"github.com/ethereum/go-ethereum/core/state"
    24  	"github.com/ethereum/go-ethereum/core/types"
    25  	"github.com/ethereum/go-ethereum/logger"
    26  	"github.com/ethereum/go-ethereum/logger/glog"
    27  )
    28  
    29  type AccountChange struct {
    30  	Address, StateAddress []byte
    31  }
    32  
    33  // Filtering interface
    34  type Filter struct {
    35  	eth      Backend
    36  	earliest int64
    37  	latest   int64
    38  	skip     int
    39  	address  []common.Address
    40  	max      int
    41  	topics   [][]common.Hash
    42  
    43  	BlockCallback       func(*types.Block, state.Logs)
    44  	TransactionCallback func(*types.Transaction)
    45  	LogsCallback        func(state.Logs)
    46  }
    47  
    48  // Create a new filter which uses a bloom filter on blocks to figure out whether a particular block
    49  // is interesting or not.
    50  func NewFilter(eth Backend) *Filter {
    51  	return &Filter{eth: eth}
    52  }
    53  
    54  // Set the earliest and latest block for filtering.
    55  // -1 = latest block (i.e., the current block)
    56  // hash = particular hash from-to
    57  func (self *Filter) SetEarliestBlock(earliest int64) {
    58  	self.earliest = earliest
    59  }
    60  
    61  func (self *Filter) SetLatestBlock(latest int64) {
    62  	self.latest = latest
    63  }
    64  
    65  func (self *Filter) SetAddress(addr []common.Address) {
    66  	self.address = addr
    67  }
    68  
    69  func (self *Filter) SetTopics(topics [][]common.Hash) {
    70  	self.topics = topics
    71  }
    72  
    73  func (self *Filter) SetMax(max int) {
    74  	self.max = max
    75  }
    76  
    77  func (self *Filter) SetSkip(skip int) {
    78  	self.skip = skip
    79  }
    80  
    81  // Run filters logs with the current parameters set
    82  func (self *Filter) Find() state.Logs {
    83  	earliestBlock := self.eth.ChainManager().CurrentBlock()
    84  	var earliestBlockNo uint64 = uint64(self.earliest)
    85  	if self.earliest == -1 {
    86  		earliestBlockNo = earliestBlock.NumberU64()
    87  	}
    88  	var latestBlockNo uint64 = uint64(self.latest)
    89  	if self.latest == -1 {
    90  		latestBlockNo = earliestBlock.NumberU64()
    91  	}
    92  
    93  	var (
    94  		logs  state.Logs
    95  		block = self.eth.ChainManager().GetBlockByNumber(latestBlockNo)
    96  	)
    97  
    98  done:
    99  	for i := 0; block != nil; i++ {
   100  		// Quit on latest
   101  		switch {
   102  		case block.NumberU64() == 0:
   103  			break done
   104  		case block.NumberU64() < earliestBlockNo:
   105  			break done
   106  		case self.max <= len(logs):
   107  			break done
   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  				glog.V(logger.Warn).Infoln("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 []common.Address, a common.Address) bool {
   133  	for _, addr := range addresses {
   134  		if 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 len(self.address) > 0 && !includes(self.address, log.Address) {
   149  			continue
   150  		}
   151  
   152  		logTopics := make([]common.Hash, len(self.topics))
   153  		copy(logTopics, log.Topics)
   154  
   155  		// If the to filtered topics is greater than the amount of topics in
   156  		//  logs, skip.
   157  		if len(self.topics) > len(log.Topics) {
   158  			continue Logs
   159  		}
   160  
   161  		for i, topics := range self.topics {
   162  			var match bool
   163  			for _, topic := range topics {
   164  				// common.Hash{} is a match all (wildcard)
   165  				if (topic == common.Hash{}) || log.Topics[i] == topic {
   166  					match = true
   167  					break
   168  				}
   169  			}
   170  
   171  			if !match {
   172  				continue Logs
   173  			}
   174  
   175  		}
   176  
   177  		ret = append(ret, log)
   178  	}
   179  
   180  	return ret
   181  }
   182  
   183  func (self *Filter) bloomFilter(block *types.Block) bool {
   184  	if len(self.address) > 0 {
   185  		var included bool
   186  		for _, addr := range self.address {
   187  			if types.BloomLookup(block.Bloom(), addr) {
   188  				included = true
   189  				break
   190  			}
   191  		}
   192  
   193  		if !included {
   194  			return false
   195  		}
   196  	}
   197  
   198  	for _, sub := range self.topics {
   199  		var included bool
   200  		for _, topic := range sub {
   201  			if (topic == common.Hash{}) || types.BloomLookup(block.Bloom(), topic) {
   202  				included = true
   203  				break
   204  			}
   205  		}
   206  		if !included {
   207  			return false
   208  		}
   209  	}
   210  
   211  	return true
   212  }