github.com/theQRL/go-zond@v0.1.1/zond/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  	"context"
    21  	"errors"
    22  	"math/big"
    23  
    24  	"github.com/theQRL/go-zond/common"
    25  	"github.com/theQRL/go-zond/core/bloombits"
    26  	"github.com/theQRL/go-zond/core/types"
    27  	"github.com/theQRL/go-zond/rpc"
    28  )
    29  
    30  // Filter can be used to retrieve and filter logs.
    31  type Filter struct {
    32  	sys *FilterSystem
    33  
    34  	addresses []common.Address
    35  	topics    [][]common.Hash
    36  
    37  	block      *common.Hash // Block hash if filtering a single block
    38  	begin, end int64        // Range interval if filtering multiple blocks
    39  
    40  	matcher *bloombits.Matcher
    41  }
    42  
    43  // NewRangeFilter creates a new filter which uses a bloom filter on blocks to
    44  // figure out whether a particular block is interesting or not.
    45  func (sys *FilterSystem) NewRangeFilter(begin, end int64, addresses []common.Address, topics [][]common.Hash) *Filter {
    46  	// Flatten the address and topic filter clauses into a single bloombits filter
    47  	// system. Since the bloombits are not positional, nil topics are permitted,
    48  	// which get flattened into a nil byte slice.
    49  	var filters [][][]byte
    50  	if len(addresses) > 0 {
    51  		filter := make([][]byte, len(addresses))
    52  		for i, address := range addresses {
    53  			filter[i] = address.Bytes()
    54  		}
    55  		filters = append(filters, filter)
    56  	}
    57  	for _, topicList := range topics {
    58  		filter := make([][]byte, len(topicList))
    59  		for i, topic := range topicList {
    60  			filter[i] = topic.Bytes()
    61  		}
    62  		filters = append(filters, filter)
    63  	}
    64  	size, _ := sys.backend.BloomStatus()
    65  
    66  	// Create a generic filter and convert it into a range filter
    67  	filter := newFilter(sys, addresses, topics)
    68  
    69  	filter.matcher = bloombits.NewMatcher(size, filters)
    70  	filter.begin = begin
    71  	filter.end = end
    72  
    73  	return filter
    74  }
    75  
    76  // NewBlockFilter creates a new filter which directly inspects the contents of
    77  // a block to figure out whether it is interesting or not.
    78  func (sys *FilterSystem) NewBlockFilter(block common.Hash, addresses []common.Address, topics [][]common.Hash) *Filter {
    79  	// Create a generic filter and convert it into a block filter
    80  	filter := newFilter(sys, addresses, topics)
    81  	filter.block = &block
    82  	return filter
    83  }
    84  
    85  // newFilter creates a generic filter that can either filter based on a block hash,
    86  // or based on range queries. The search criteria needs to be explicitly set.
    87  func newFilter(sys *FilterSystem, addresses []common.Address, topics [][]common.Hash) *Filter {
    88  	return &Filter{
    89  		sys:       sys,
    90  		addresses: addresses,
    91  		topics:    topics,
    92  	}
    93  }
    94  
    95  // Logs searches the blockchain for matching log entries, returning all from the
    96  // first block that contains matches, updating the start of the filter accordingly.
    97  func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) {
    98  	// If we're doing singleton block filtering, execute and return
    99  	if f.block != nil {
   100  		header, err := f.sys.backend.HeaderByHash(ctx, *f.block)
   101  		if err != nil {
   102  			return nil, err
   103  		}
   104  		if header == nil {
   105  			return nil, errors.New("unknown block")
   106  		}
   107  		return f.blockLogs(ctx, header)
   108  	}
   109  
   110  	var (
   111  		beginPending = f.begin == rpc.PendingBlockNumber.Int64()
   112  		endPending   = f.end == rpc.PendingBlockNumber.Int64()
   113  	)
   114  
   115  	// special case for pending logs
   116  	if beginPending && !endPending {
   117  		return nil, errors.New("invalid block range")
   118  	}
   119  
   120  	// Short-cut if all we care about is pending logs
   121  	if beginPending && endPending {
   122  		return f.pendingLogs(), nil
   123  	}
   124  
   125  	resolveSpecial := func(number int64) (int64, error) {
   126  		var hdr *types.Header
   127  		switch number {
   128  		case rpc.LatestBlockNumber.Int64(), rpc.PendingBlockNumber.Int64():
   129  			// we should return head here since we've already captured
   130  			// that we need to get the pending logs in the pending boolean above
   131  			hdr, _ = f.sys.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
   132  			if hdr == nil {
   133  				return 0, errors.New("latest header not found")
   134  			}
   135  		case rpc.FinalizedBlockNumber.Int64():
   136  			hdr, _ = f.sys.backend.HeaderByNumber(ctx, rpc.FinalizedBlockNumber)
   137  			if hdr == nil {
   138  				return 0, errors.New("finalized header not found")
   139  			}
   140  		case rpc.SafeBlockNumber.Int64():
   141  			hdr, _ = f.sys.backend.HeaderByNumber(ctx, rpc.SafeBlockNumber)
   142  			if hdr == nil {
   143  				return 0, errors.New("safe header not found")
   144  			}
   145  		default:
   146  			return number, nil
   147  		}
   148  		return hdr.Number.Int64(), nil
   149  	}
   150  
   151  	var err error
   152  	// range query need to resolve the special begin/end block number
   153  	if f.begin, err = resolveSpecial(f.begin); err != nil {
   154  		return nil, err
   155  	}
   156  	if f.end, err = resolveSpecial(f.end); err != nil {
   157  		return nil, err
   158  	}
   159  
   160  	logChan, errChan := f.rangeLogsAsync(ctx)
   161  	var logs []*types.Log
   162  	for {
   163  		select {
   164  		case log := <-logChan:
   165  			logs = append(logs, log)
   166  		case err := <-errChan:
   167  			if err != nil {
   168  				// if an error occurs during extraction, we do return the extracted data
   169  				return logs, err
   170  			}
   171  			// Append the pending ones
   172  			if endPending {
   173  				pendingLogs := f.pendingLogs()
   174  				logs = append(logs, pendingLogs...)
   175  			}
   176  			return logs, nil
   177  		}
   178  	}
   179  }
   180  
   181  // rangeLogsAsync retrieves block-range logs that match the filter criteria asynchronously,
   182  // it creates and returns two channels: one for delivering log data, and one for reporting errors.
   183  func (f *Filter) rangeLogsAsync(ctx context.Context) (chan *types.Log, chan error) {
   184  	var (
   185  		logChan = make(chan *types.Log)
   186  		errChan = make(chan error)
   187  	)
   188  
   189  	go func() {
   190  		defer func() {
   191  			close(errChan)
   192  			close(logChan)
   193  		}()
   194  
   195  		// Gather all indexed logs, and finish with non indexed ones
   196  		var (
   197  			end            = uint64(f.end)
   198  			size, sections = f.sys.backend.BloomStatus()
   199  			err            error
   200  		)
   201  		if indexed := sections * size; indexed > uint64(f.begin) {
   202  			if indexed > end {
   203  				indexed = end + 1
   204  			}
   205  			if err = f.indexedLogs(ctx, indexed-1, logChan); err != nil {
   206  				errChan <- err
   207  				return
   208  			}
   209  		}
   210  
   211  		if err := f.unindexedLogs(ctx, end, logChan); err != nil {
   212  			errChan <- err
   213  			return
   214  		}
   215  
   216  		errChan <- nil
   217  	}()
   218  
   219  	return logChan, errChan
   220  }
   221  
   222  // indexedLogs returns the logs matching the filter criteria based on the bloom
   223  // bits indexed available locally or via the network.
   224  func (f *Filter) indexedLogs(ctx context.Context, end uint64, logChan chan *types.Log) error {
   225  	// Create a matcher session and request servicing from the backend
   226  	matches := make(chan uint64, 64)
   227  
   228  	session, err := f.matcher.Start(ctx, uint64(f.begin), end, matches)
   229  	if err != nil {
   230  		return err
   231  	}
   232  	defer session.Close()
   233  
   234  	f.sys.backend.ServiceFilter(ctx, session)
   235  
   236  	for {
   237  		select {
   238  		case number, ok := <-matches:
   239  			// Abort if all matches have been fulfilled
   240  			if !ok {
   241  				err := session.Error()
   242  				if err == nil {
   243  					f.begin = int64(end) + 1
   244  				}
   245  				return err
   246  			}
   247  			f.begin = int64(number) + 1
   248  
   249  			// Retrieve the suggested block and pull any truly matching logs
   250  			header, err := f.sys.backend.HeaderByNumber(ctx, rpc.BlockNumber(number))
   251  			if header == nil || err != nil {
   252  				return err
   253  			}
   254  			found, err := f.checkMatches(ctx, header)
   255  			if err != nil {
   256  				return err
   257  			}
   258  			for _, log := range found {
   259  				logChan <- log
   260  			}
   261  
   262  		case <-ctx.Done():
   263  			return ctx.Err()
   264  		}
   265  	}
   266  }
   267  
   268  // unindexedLogs returns the logs matching the filter criteria based on raw block
   269  // iteration and bloom matching.
   270  func (f *Filter) unindexedLogs(ctx context.Context, end uint64, logChan chan *types.Log) error {
   271  	for ; f.begin <= int64(end); f.begin++ {
   272  		header, err := f.sys.backend.HeaderByNumber(ctx, rpc.BlockNumber(f.begin))
   273  		if header == nil || err != nil {
   274  			return err
   275  		}
   276  		found, err := f.blockLogs(ctx, header)
   277  		if err != nil {
   278  			return err
   279  		}
   280  		for _, log := range found {
   281  			select {
   282  			case logChan <- log:
   283  			case <-ctx.Done():
   284  				return ctx.Err()
   285  			}
   286  		}
   287  	}
   288  	return nil
   289  }
   290  
   291  // blockLogs returns the logs matching the filter criteria within a single block.
   292  func (f *Filter) blockLogs(ctx context.Context, header *types.Header) ([]*types.Log, error) {
   293  	if bloomFilter(header.Bloom, f.addresses, f.topics) {
   294  		return f.checkMatches(ctx, header)
   295  	}
   296  	return nil, nil
   297  }
   298  
   299  // checkMatches checks if the receipts belonging to the given header contain any log events that
   300  // match the filter criteria. This function is called when the bloom filter signals a potential match.
   301  // skipFilter signals all logs of the given block are requested.
   302  func (f *Filter) checkMatches(ctx context.Context, header *types.Header) ([]*types.Log, error) {
   303  	hash := header.Hash()
   304  	// Logs in cache are partially filled with context data
   305  	// such as tx index, block hash, etc.
   306  	// Notably tx hash is NOT filled in because it needs
   307  	// access to block body data.
   308  	cached, err := f.sys.cachedLogElem(ctx, hash, header.Number.Uint64())
   309  	if err != nil {
   310  		return nil, err
   311  	}
   312  	logs := filterLogs(cached.logs, nil, nil, f.addresses, f.topics)
   313  	if len(logs) == 0 {
   314  		return nil, nil
   315  	}
   316  	// Most backends will deliver un-derived logs, but check nevertheless.
   317  	if len(logs) > 0 && logs[0].TxHash != (common.Hash{}) {
   318  		return logs, nil
   319  	}
   320  
   321  	body, err := f.sys.cachedGetBody(ctx, cached, hash, header.Number.Uint64())
   322  	if err != nil {
   323  		return nil, err
   324  	}
   325  	for i, log := range logs {
   326  		// Copy log not to modify cache elements
   327  		logcopy := *log
   328  		logcopy.TxHash = body.Transactions[logcopy.TxIndex].Hash()
   329  		logs[i] = &logcopy
   330  	}
   331  	return logs, nil
   332  }
   333  
   334  // pendingLogs returns the logs matching the filter criteria within the pending block.
   335  func (f *Filter) pendingLogs() []*types.Log {
   336  	block, receipts := f.sys.backend.PendingBlockAndReceipts()
   337  	if block == nil || receipts == nil {
   338  		return nil
   339  	}
   340  	if bloomFilter(block.Bloom(), f.addresses, f.topics) {
   341  		var unfiltered []*types.Log
   342  		for _, r := range receipts {
   343  			unfiltered = append(unfiltered, r.Logs...)
   344  		}
   345  		return filterLogs(unfiltered, nil, nil, f.addresses, f.topics)
   346  	}
   347  	return nil
   348  }
   349  
   350  // includes returns true if the element is present in the list.
   351  func includes[T comparable](things []T, element T) bool {
   352  	for _, thing := range things {
   353  		if thing == element {
   354  			return true
   355  		}
   356  	}
   357  	return false
   358  }
   359  
   360  // filterLogs creates a slice of logs matching the given criteria.
   361  func filterLogs(logs []*types.Log, fromBlock, toBlock *big.Int, addresses []common.Address, topics [][]common.Hash) []*types.Log {
   362  	var check = func(log *types.Log) bool {
   363  		if fromBlock != nil && fromBlock.Int64() >= 0 && fromBlock.Uint64() > log.BlockNumber {
   364  			return false
   365  		}
   366  		if toBlock != nil && toBlock.Int64() >= 0 && toBlock.Uint64() < log.BlockNumber {
   367  			return false
   368  		}
   369  		if len(addresses) > 0 && !includes(addresses, log.Address) {
   370  			return false
   371  		}
   372  		// If the to filtered topics is greater than the amount of topics in logs, skip.
   373  		if len(topics) > len(log.Topics) {
   374  			return false
   375  		}
   376  		for i, sub := range topics {
   377  			if len(sub) == 0 {
   378  				continue // empty rule set == wildcard
   379  			}
   380  			if !includes(sub, log.Topics[i]) {
   381  				return false
   382  			}
   383  		}
   384  		return true
   385  	}
   386  	var ret []*types.Log
   387  	for _, log := range logs {
   388  		if check(log) {
   389  			ret = append(ret, log)
   390  		}
   391  	}
   392  	return ret
   393  }
   394  
   395  func bloomFilter(bloom types.Bloom, addresses []common.Address, topics [][]common.Hash) bool {
   396  	if len(addresses) > 0 {
   397  		var included bool
   398  		for _, addr := range addresses {
   399  			if types.BloomLookup(bloom, addr) {
   400  				included = true
   401  				break
   402  			}
   403  		}
   404  		if !included {
   405  			return false
   406  		}
   407  	}
   408  
   409  	for _, sub := range topics {
   410  		included := len(sub) == 0 // empty rule set == wildcard
   411  		for _, topic := range sub {
   412  			if types.BloomLookup(bloom, topic) {
   413  				included = true
   414  				break
   415  			}
   416  		}
   417  		if !included {
   418  			return false
   419  		}
   420  	}
   421  	return true
   422  }