github.com/digdeepmining/go-atheios@v1.5.13-0.20180902133602-d5687a2e6f43/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  	"math/big"
    23  
    24  	"github.com/atheioschain/go-atheios/common"
    25  	"github.com/atheioschain/go-atheios/core"
    26  	"github.com/atheioschain/go-atheios/core/types"
    27  	"github.com/atheioschain/go-atheios/ethdb"
    28  	"github.com/atheioschain/go-atheios/event"
    29  	"github.com/atheioschain/go-atheios/rpc"
    30  	"golang.org/x/net/context"
    31  )
    32  
    33  type Backend interface {
    34  	ChainDb() ethdb.Database
    35  	EventMux() *event.TypeMux
    36  	HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error)
    37  	GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error)
    38  }
    39  
    40  // Filter can be used to retrieve and filter logs.
    41  type Filter struct {
    42  	backend   Backend
    43  	useMipMap bool
    44  
    45  	created time.Time
    46  
    47  	db         ethdb.Database
    48  	begin, end int64
    49  	addresses  []common.Address
    50  	topics     [][]common.Hash
    51  }
    52  
    53  // New creates a new filter which uses a bloom filter on blocks to figure out whether
    54  // a particular block is interesting or not.
    55  // MipMaps allow past blocks to be searched much more efficiently, but are not available
    56  // to light clients.
    57  func New(backend Backend, useMipMap bool) *Filter {
    58  	return &Filter{
    59  		backend:   backend,
    60  		useMipMap: useMipMap,
    61  		db:        backend.ChainDb(),
    62  	}
    63  }
    64  
    65  // SetBeginBlock sets the earliest block for filtering.
    66  // -1 = latest block (i.e., the current block)
    67  // hash = particular hash from-to
    68  func (f *Filter) SetBeginBlock(begin int64) {
    69  	f.begin = begin
    70  }
    71  
    72  // SetEndBlock sets the latest block for filtering.
    73  func (f *Filter) SetEndBlock(end int64) {
    74  	f.end = end
    75  }
    76  
    77  // SetAddresses matches only logs that are generated from addresses that are included
    78  // in the given addresses.
    79  func (f *Filter) SetAddresses(addr []common.Address) {
    80  	f.addresses = addr
    81  }
    82  
    83  // SetTopics matches only logs that have topics matching the given topics.
    84  func (f *Filter) SetTopics(topics [][]common.Hash) {
    85  	f.topics = topics
    86  }
    87  
    88  // FindOnce searches the blockchain for matching log entries, returning
    89  // all matching entries from the first block that contains matches,
    90  // updating the start point of the filter accordingly. If no results are
    91  // found, a nil slice is returned.
    92  func (f *Filter) FindOnce(ctx context.Context) ([]*types.Log, error) {
    93  	head, _ := f.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
    94  	if head == nil {
    95  		return nil, nil
    96  	}
    97  	headBlockNumber := head.Number.Uint64()
    98  
    99  	var beginBlockNo uint64 = uint64(f.begin)
   100  	if f.begin == -1 {
   101  		beginBlockNo = headBlockNumber
   102  	}
   103  	var endBlockNo uint64 = uint64(f.end)
   104  	if f.end == -1 {
   105  		endBlockNo = headBlockNumber
   106  	}
   107  
   108  	// if no addresses are present we can't make use of fast search which
   109  	// uses the mipmap bloom filters to check for fast inclusion and uses
   110  	// higher range probability in order to ensure at least a false positive
   111  	if !f.useMipMap || len(f.addresses) == 0 {
   112  		logs, blockNumber, err := f.getLogs(ctx, beginBlockNo, endBlockNo)
   113  		f.begin = int64(blockNumber + 1)
   114  		return logs, err
   115  	}
   116  
   117  	logs, blockNumber := f.mipFind(beginBlockNo, endBlockNo, 0)
   118  	f.begin = int64(blockNumber + 1)
   119  	return logs, nil
   120  }
   121  
   122  // Run filters logs with the current parameters set
   123  func (f *Filter) Find(ctx context.Context) (logs []*types.Log, err error) {
   124  	for {
   125  		newLogs, err := f.FindOnce(ctx)
   126  		if len(newLogs) == 0 || err != nil {
   127  			return logs, err
   128  		}
   129  		logs = append(logs, newLogs...)
   130  	}
   131  }
   132  
   133  func (f *Filter) mipFind(start, end uint64, depth int) (logs []*types.Log, blockNumber uint64) {
   134  	level := core.MIPMapLevels[depth]
   135  	// normalise numerator so we can work in level specific batches and
   136  	// work with the proper range checks
   137  	for num := start / level * level; num <= end; num += level {
   138  		// find addresses in bloom filters
   139  		bloom := core.GetMipmapBloom(f.db, num, level)
   140  		// Don't bother checking the first time through the loop - we're probably picking
   141  		// up where a previous run left off.
   142  		first := true
   143  		for _, addr := range f.addresses {
   144  			if first || bloom.TestBytes(addr[:]) {
   145  				first = false
   146  				// range check normalised values and make sure that
   147  				// we're resolving the correct range instead of the
   148  				// normalised values.
   149  				start := uint64(math.Max(float64(num), float64(start)))
   150  				end := uint64(math.Min(float64(num+level-1), float64(end)))
   151  				if depth+1 == len(core.MIPMapLevels) {
   152  					l, blockNumber, _ := f.getLogs(context.Background(), start, end)
   153  					if len(l) > 0 {
   154  						return l, blockNumber
   155  					}
   156  				} else {
   157  					l, blockNumber := f.mipFind(start, end, depth+1)
   158  					if len(l) > 0 {
   159  						return l, blockNumber
   160  					}
   161  				}
   162  			}
   163  		}
   164  	}
   165  
   166  	return nil, end
   167  }
   168  
   169  func (f *Filter) getLogs(ctx context.Context, start, end uint64) (logs []*types.Log, blockNumber uint64, err error) {
   170  	for i := start; i <= end; i++ {
   171  		blockNumber := rpc.BlockNumber(i)
   172  		header, err := f.backend.HeaderByNumber(ctx, blockNumber)
   173  		if header == nil || err != nil {
   174  			return logs, end, err
   175  		}
   176  
   177  		// Use bloom filtering to see if this block is interesting given the
   178  		// current parameters
   179  		if f.bloomFilter(header.Bloom) {
   180  			// Get the logs of the block
   181  			receipts, err := f.backend.GetReceipts(ctx, header.Hash())
   182  			if err != nil {
   183  				return nil, end, err
   184  			}
   185  			var unfiltered []*types.Log
   186  			for _, receipt := range receipts {
   187  				unfiltered = append(unfiltered, ([]*types.Log)(receipt.Logs)...)
   188  			}
   189  			logs = filterLogs(unfiltered, nil, nil, f.addresses, f.topics)
   190  			if len(logs) > 0 {
   191  				return logs, uint64(blockNumber), nil
   192  			}
   193  		}
   194  	}
   195  
   196  	return logs, end, nil
   197  }
   198  
   199  func includes(addresses []common.Address, a common.Address) bool {
   200  	for _, addr := range addresses {
   201  		if addr == a {
   202  			return true
   203  		}
   204  	}
   205  
   206  	return false
   207  }
   208  
   209  // filterLogs creates a slice of logs matching the given criteria.
   210  func filterLogs(logs []*types.Log, fromBlock, toBlock *big.Int, addresses []common.Address, topics [][]common.Hash) []*types.Log {
   211  	var ret []*types.Log
   212  Logs:
   213  	for _, log := range logs {
   214  		if fromBlock != nil && fromBlock.Int64() >= 0 && fromBlock.Uint64() > log.BlockNumber {
   215  			continue
   216  		}
   217  		if toBlock != nil && toBlock.Int64() >= 0 && toBlock.Uint64() < log.BlockNumber {
   218  			continue
   219  		}
   220  
   221  		if len(addresses) > 0 && !includes(addresses, log.Address) {
   222  			continue
   223  		}
   224  
   225  		logTopics := make([]common.Hash, len(topics))
   226  		copy(logTopics, log.Topics)
   227  
   228  		// If the to filtered topics is greater than the amount of topics in logs, skip.
   229  		if len(topics) > len(log.Topics) {
   230  			continue Logs
   231  		}
   232  
   233  		for i, topics := range topics {
   234  			var match bool
   235  			for _, topic := range topics {
   236  				// common.Hash{} is a match all (wildcard)
   237  				if (topic == common.Hash{}) || log.Topics[i] == topic {
   238  					match = true
   239  					break
   240  				}
   241  			}
   242  
   243  			if !match {
   244  				continue Logs
   245  			}
   246  		}
   247  		ret = append(ret, log)
   248  	}
   249  
   250  	return ret
   251  }
   252  
   253  func (f *Filter) bloomFilter(bloom types.Bloom) bool {
   254  	return bloomFilter(bloom, f.addresses, f.topics)
   255  }
   256  
   257  func bloomFilter(bloom types.Bloom, addresses []common.Address, topics [][]common.Hash) bool {
   258  	if len(addresses) > 0 {
   259  		var included bool
   260  		for _, addr := range addresses {
   261  			if types.BloomLookup(bloom, addr) {
   262  				included = true
   263  				break
   264  			}
   265  		}
   266  
   267  		if !included {
   268  			return false
   269  		}
   270  	}
   271  
   272  	for _, sub := range topics {
   273  		var included bool
   274  		for _, topic := range sub {
   275  			if (topic == common.Hash{}) || types.BloomLookup(bloom, topic) {
   276  				included = true
   277  				break
   278  			}
   279  		}
   280  		if !included {
   281  			return false
   282  		}
   283  	}
   284  
   285  	return true
   286  }