github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/eth/filters/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 filters
    18  
    19  import (
    20  	"math"
    21  	"time"
    22  
    23  	"github.com/ethereumproject/go-ethereum/common"
    24  	"github.com/ethereumproject/go-ethereum/core"
    25  	"github.com/ethereumproject/go-ethereum/core/types"
    26  	"github.com/ethereumproject/go-ethereum/core/vm"
    27  	"github.com/ethereumproject/go-ethereum/ethdb"
    28  )
    29  
    30  type AccountChange struct {
    31  	Address, StateAddress []byte
    32  }
    33  
    34  // Filtering interface
    35  type Filter struct {
    36  	created time.Time
    37  
    38  	db         ethdb.Database
    39  	begin, end int64
    40  	addresses  []common.Address
    41  	topics     [][]common.Hash
    42  
    43  	BlockCallback       func(*types.Block, vm.Logs)
    44  	TransactionCallback func(*types.Transaction)
    45  	LogCallback         func(*vm.Log, bool)
    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 New(db ethdb.Database) *Filter {
    51  	return &Filter{db: db}
    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) SetBeginBlock(begin int64) {
    58  	self.begin = begin
    59  }
    60  
    61  func (self *Filter) SetEndBlock(end int64) {
    62  	self.end = end
    63  }
    64  
    65  func (self *Filter) SetAddresses(addr []common.Address) {
    66  	self.addresses = addr
    67  }
    68  
    69  func (self *Filter) SetTopics(topics [][]common.Hash) {
    70  	self.topics = topics
    71  }
    72  
    73  // Run filters logs with the current parameters set
    74  func (self *Filter) Find() vm.Logs {
    75  	latestBlock := core.GetBlock(self.db, core.GetHeadBlockHash(self.db))
    76  	if latestBlock == nil {
    77  		return vm.Logs{}
    78  	}
    79  	var beginBlockNo uint64 = uint64(self.begin)
    80  	if self.begin == -1 {
    81  		beginBlockNo = latestBlock.NumberU64()
    82  	}
    83  	var endBlockNo uint64 = uint64(self.end)
    84  	if self.end == -1 {
    85  		endBlockNo = latestBlock.NumberU64()
    86  	}
    87  
    88  	// if no addresses are present we can't make use of fast search which
    89  	// uses the mipmap bloom filters to check for fast inclusion and uses
    90  	// higher range probability in order to ensure at least a false positive
    91  	if len(self.addresses) == 0 {
    92  		return self.getLogs(beginBlockNo, endBlockNo)
    93  	}
    94  	return self.mipFind(beginBlockNo, endBlockNo, 0)
    95  }
    96  
    97  func (self *Filter) mipFind(start, end uint64, depth int) (logs vm.Logs) {
    98  	level := core.MIPMapLevels[depth]
    99  	// normalise numerator so we can work in level specific batches and
   100  	// work with the proper range checks
   101  	for num := start / level * level; num <= end; num += level {
   102  		// find addresses in bloom filters
   103  		bloom := core.GetMipmapBloom(self.db, num, level)
   104  		for _, addr := range self.addresses {
   105  			if types.BloomLookup(bloom, addr[:]) {
   106  				// range check normalised values and make sure that
   107  				// we're resolving the correct range instead of the
   108  				// normalised values.
   109  				start := uint64(math.Max(float64(num), float64(start)))
   110  				end := uint64(math.Min(float64(num+level-1), float64(end)))
   111  				if depth+1 == len(core.MIPMapLevels) {
   112  					logs = append(logs, self.getLogs(start, end)...)
   113  				} else {
   114  					logs = append(logs, self.mipFind(start, end, depth+1)...)
   115  				}
   116  				// break so we don't check the same range for each
   117  				// possible address. Checks on multiple addresses
   118  				// are handled further down the stack.
   119  				break
   120  			}
   121  		}
   122  	}
   123  
   124  	return logs
   125  }
   126  
   127  func (self *Filter) getLogs(start, end uint64) (logs vm.Logs) {
   128  	for i := start; i <= end; i++ {
   129  		var block *types.Block
   130  		hash := core.GetCanonicalHash(self.db, i)
   131  		if hash != (common.Hash{}) {
   132  			block = core.GetBlock(self.db, hash)
   133  		}
   134  		if block == nil { // block not found/written
   135  			return logs
   136  		}
   137  
   138  		// Use bloom filtering to see if this block is interesting given the
   139  		// current parameters
   140  		if self.bloomFilter(block) {
   141  			// Get the logs of the block
   142  			var (
   143  				receipts   = core.GetBlockReceipts(self.db, block.Hash())
   144  				unfiltered vm.Logs
   145  			)
   146  			for _, receipt := range receipts {
   147  				unfiltered = append(unfiltered, receipt.Logs...)
   148  			}
   149  			logs = append(logs, self.FilterLogs(unfiltered)...)
   150  		}
   151  	}
   152  
   153  	return logs
   154  }
   155  
   156  func includes(addresses []common.Address, a common.Address) bool {
   157  	for _, addr := range addresses {
   158  		if addr == a {
   159  			return true
   160  		}
   161  	}
   162  
   163  	return false
   164  }
   165  
   166  func (self *Filter) FilterLogs(logs vm.Logs) vm.Logs {
   167  	var ret vm.Logs
   168  
   169  	// Filter the logs for interesting stuff
   170  Logs:
   171  	for _, log := range logs {
   172  		if len(self.addresses) > 0 && !includes(self.addresses, log.Address) {
   173  			continue
   174  		}
   175  
   176  		logTopics := make([]common.Hash, len(self.topics))
   177  		copy(logTopics, log.Topics)
   178  
   179  		// If the to filtered topics is greater than the amount of topics in
   180  		//  logs, skip.
   181  		if len(self.topics) > len(log.Topics) {
   182  			continue Logs
   183  		}
   184  
   185  		for i, topics := range self.topics {
   186  			var match bool
   187  			for _, topic := range topics {
   188  				// common.Hash{} is a match all (wildcard)
   189  				if (topic == common.Hash{}) || log.Topics[i] == topic {
   190  					match = true
   191  					break
   192  				}
   193  			}
   194  
   195  			if !match {
   196  				continue Logs
   197  			}
   198  
   199  		}
   200  
   201  		ret = append(ret, log)
   202  	}
   203  
   204  	return ret
   205  }
   206  
   207  func (self *Filter) bloomFilter(block *types.Block) bool {
   208  	if len(self.addresses) > 0 {
   209  		var included bool
   210  		for _, addr := range self.addresses {
   211  			if types.BloomLookup(block.Bloom(), addr[:]) {
   212  				included = true
   213  				break
   214  			}
   215  		}
   216  
   217  		if !included {
   218  			return false
   219  		}
   220  	}
   221  
   222  	for _, sub := range self.topics {
   223  		var included bool
   224  		for _, topic := range sub {
   225  			if (topic == common.Hash{}) || types.BloomLookup(block.Bloom(), topic[:]) {
   226  				included = true
   227  				break
   228  			}
   229  		}
   230  		if !included {
   231  			return false
   232  		}
   233  	}
   234  
   235  	return true
   236  }