github.com/ava-labs/subnet-evm@v0.6.4/eth/filters/filter.go (about)

     1  // (c) 2019-2020, Ava Labs, Inc.
     2  //
     3  // This file is a derived work, based on the go-ethereum library whose original
     4  // notices appear below.
     5  //
     6  // It is distributed under a license compatible with the licensing terms of the
     7  // original code from which it is derived.
     8  //
     9  // Much love to the original authors for their work.
    10  // **********
    11  // Copyright 2014 The go-ethereum Authors
    12  // This file is part of the go-ethereum library.
    13  //
    14  // The go-ethereum library is free software: you can redistribute it and/or modify
    15  // it under the terms of the GNU Lesser General Public License as published by
    16  // the Free Software Foundation, either version 3 of the License, or
    17  // (at your option) any later version.
    18  //
    19  // The go-ethereum library is distributed in the hope that it will be useful,
    20  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    21  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    22  // GNU Lesser General Public License for more details.
    23  //
    24  // You should have received a copy of the GNU Lesser General Public License
    25  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    26  
    27  package filters
    28  
    29  import (
    30  	"context"
    31  	"errors"
    32  	"fmt"
    33  	"math/big"
    34  
    35  	"github.com/ava-labs/subnet-evm/core/bloombits"
    36  	"github.com/ava-labs/subnet-evm/core/types"
    37  	"github.com/ava-labs/subnet-evm/rpc"
    38  	"github.com/ethereum/go-ethereum/common"
    39  )
    40  
    41  // Filter can be used to retrieve and filter logs.
    42  type Filter struct {
    43  	sys *FilterSystem
    44  
    45  	addresses []common.Address
    46  	topics    [][]common.Hash
    47  
    48  	block      *common.Hash // Block hash if filtering a single block
    49  	begin, end int64        // Range interval if filtering multiple blocks
    50  
    51  	matcher *bloombits.Matcher
    52  }
    53  
    54  // NewRangeFilter creates a new filter which uses a bloom filter on blocks to
    55  // figure out whether a particular block is interesting or not.
    56  func (sys *FilterSystem) NewRangeFilter(begin, end int64, addresses []common.Address, topics [][]common.Hash) *Filter {
    57  	// Flatten the address and topic filter clauses into a single bloombits filter
    58  	// system. Since the bloombits are not positional, nil topics are permitted,
    59  	// which get flattened into a nil byte slice.
    60  	var filters [][][]byte
    61  	if len(addresses) > 0 {
    62  		filter := make([][]byte, len(addresses))
    63  		for i, address := range addresses {
    64  			filter[i] = address.Bytes()
    65  		}
    66  		filters = append(filters, filter)
    67  	}
    68  	for _, topicList := range topics {
    69  		filter := make([][]byte, len(topicList))
    70  		for i, topic := range topicList {
    71  			filter[i] = topic.Bytes()
    72  		}
    73  		filters = append(filters, filter)
    74  	}
    75  	size, _ := sys.backend.BloomStatus()
    76  
    77  	// Create a generic filter and convert it into a range filter
    78  	filter := newFilter(sys, addresses, topics)
    79  
    80  	filter.matcher = bloombits.NewMatcher(size, filters)
    81  	filter.begin = begin
    82  	filter.end = end
    83  
    84  	return filter
    85  }
    86  
    87  // NewBlockFilter creates a new filter which directly inspects the contents of
    88  // a block to figure out whether it is interesting or not.
    89  func (sys *FilterSystem) NewBlockFilter(block common.Hash, addresses []common.Address, topics [][]common.Hash) *Filter {
    90  	// Create a generic filter and convert it into a block filter
    91  	filter := newFilter(sys, addresses, topics)
    92  	filter.block = &block
    93  	return filter
    94  }
    95  
    96  // newFilter creates a generic filter that can either filter based on a block hash,
    97  // or based on range queries. The search criteria needs to be explicitly set.
    98  func newFilter(sys *FilterSystem, addresses []common.Address, topics [][]common.Hash) *Filter {
    99  	return &Filter{
   100  		sys:       sys,
   101  		addresses: addresses,
   102  		topics:    topics,
   103  	}
   104  }
   105  
   106  // Logs searches the blockchain for matching log entries, returning all from the
   107  // first block that contains matches, updating the start of the filter accordingly.
   108  func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) {
   109  	// If we're doing singleton block filtering, execute and return
   110  	if f.block != nil {
   111  		header, err := f.sys.backend.HeaderByHash(ctx, *f.block)
   112  		if err != nil {
   113  			return nil, err
   114  		}
   115  		if header == nil {
   116  			return nil, errors.New("unknown block")
   117  		}
   118  		return f.blockLogs(ctx, header)
   119  	}
   120  
   121  	// Disallow blocks past the last accepted block if the backend does not
   122  	// allow unfinalized queries.
   123  	allowUnfinalizedQueries := f.sys.backend.IsAllowUnfinalizedQueries()
   124  	acceptedBlock := f.sys.backend.LastAcceptedBlock()
   125  	if !allowUnfinalizedQueries && acceptedBlock != nil {
   126  		lastAccepted := acceptedBlock.Number().Int64()
   127  		if f.begin >= 0 && f.begin > lastAccepted {
   128  			return nil, fmt.Errorf("requested from block %d after last accepted block %d", f.begin, lastAccepted)
   129  		}
   130  		if f.end >= 0 && f.end > lastAccepted {
   131  			return nil, fmt.Errorf("requested to block %d after last accepted block %d", f.end, lastAccepted)
   132  		}
   133  	}
   134  
   135  	var (
   136  		beginPending = f.begin == rpc.PendingBlockNumber.Int64()
   137  		endPending   = f.end == rpc.PendingBlockNumber.Int64()
   138  		endSet       = f.end >= 0
   139  	)
   140  
   141  	// special case for pending logs
   142  	if beginPending && !endPending {
   143  		return nil, errors.New("invalid block range")
   144  	}
   145  
   146  	// Short-cut if all we care about is pending logs
   147  	if beginPending && endPending {
   148  		return nil, nil
   149  	}
   150  
   151  	resolveSpecial := func(number int64) (int64, error) {
   152  		var hdr *types.Header
   153  		switch number {
   154  		case rpc.LatestBlockNumber.Int64(), rpc.PendingBlockNumber.Int64():
   155  			// we should return head here since we've already captured
   156  			// that we need to get the pending logs in the pending boolean above
   157  			hdr, _ = f.sys.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
   158  			if hdr == nil {
   159  				return 0, errors.New("latest header not found")
   160  			}
   161  		case rpc.FinalizedBlockNumber.Int64():
   162  			hdr, _ = f.sys.backend.HeaderByNumber(ctx, rpc.FinalizedBlockNumber)
   163  			if hdr == nil {
   164  				return 0, errors.New("finalized header not found")
   165  			}
   166  		case rpc.SafeBlockNumber.Int64():
   167  			hdr, _ = f.sys.backend.HeaderByNumber(ctx, rpc.SafeBlockNumber)
   168  			if hdr == nil {
   169  				return 0, errors.New("safe header not found")
   170  			}
   171  		default:
   172  			return number, nil
   173  		}
   174  		return hdr.Number.Int64(), nil
   175  	}
   176  
   177  	var err error
   178  	// range query need to resolve the special begin/end block number
   179  	if f.begin, err = resolveSpecial(f.begin); err != nil {
   180  		return nil, err
   181  	}
   182  	if f.end, err = resolveSpecial(f.end); err != nil {
   183  		return nil, err
   184  	}
   185  
   186  	// When querying unfinalized data without a populated end block, it is
   187  	// possible that the begin will be greater than the end.
   188  	//
   189  	// We error in this case to prevent a bad UX where the caller thinks there
   190  	// are no logs from the specified beginning to end (when in reality there may
   191  	// be some).
   192  	if endSet && f.end < f.begin {
   193  		return nil, fmt.Errorf("begin block %d is greater than end block %d", f.begin, f.end)
   194  	}
   195  
   196  	// If the requested range of blocks exceeds the maximum number of blocks allowed by the backend
   197  	// return an error instead of searching for the logs.
   198  	if maxBlocks := f.sys.backend.GetMaxBlocksPerRequest(); f.end-f.begin >= maxBlocks && maxBlocks > 0 {
   199  		return nil, fmt.Errorf("requested too many blocks from %d to %d, maximum is set to %d", f.begin, f.end, maxBlocks)
   200  	}
   201  	// Gather all indexed logs, and finish with non indexed ones
   202  	logChan, errChan := f.rangeLogsAsync(ctx)
   203  	var logs []*types.Log
   204  	for {
   205  		select {
   206  		case log := <-logChan:
   207  			logs = append(logs, log)
   208  		case err := <-errChan:
   209  			if err != nil {
   210  				// if an error occurs during extraction, we do return the extracted data
   211  				return logs, err
   212  			}
   213  			return logs, nil
   214  		}
   215  	}
   216  }
   217  
   218  // rangeLogsAsync retrieves block-range logs that match the filter criteria asynchronously,
   219  // it creates and returns two channels: one for delivering log data, and one for reporting errors.
   220  func (f *Filter) rangeLogsAsync(ctx context.Context) (chan *types.Log, chan error) {
   221  	var (
   222  		logChan = make(chan *types.Log)
   223  		errChan = make(chan error)
   224  	)
   225  
   226  	go func() {
   227  		defer func() {
   228  			close(errChan)
   229  			close(logChan)
   230  		}()
   231  
   232  		// Gather all indexed logs, and finish with non indexed ones
   233  		var (
   234  			end            = uint64(f.end)
   235  			size, sections = f.sys.backend.BloomStatus()
   236  			err            error
   237  		)
   238  		if indexed := sections * size; indexed > uint64(f.begin) {
   239  			if indexed > end {
   240  				indexed = end + 1
   241  			}
   242  			if err = f.indexedLogs(ctx, indexed-1, logChan); err != nil {
   243  				errChan <- err
   244  				return
   245  			}
   246  		}
   247  
   248  		if err := f.unindexedLogs(ctx, end, logChan); err != nil {
   249  			errChan <- err
   250  			return
   251  		}
   252  
   253  		errChan <- nil
   254  	}()
   255  
   256  	return logChan, errChan
   257  }
   258  
   259  // indexedLogs returns the logs matching the filter criteria based on the bloom
   260  // bits indexed available locally or via the network.
   261  func (f *Filter) indexedLogs(ctx context.Context, end uint64, logChan chan *types.Log) error {
   262  	// Create a matcher session and request servicing from the backend
   263  	matches := make(chan uint64, 64)
   264  
   265  	session, err := f.matcher.Start(ctx, uint64(f.begin), end, matches)
   266  	if err != nil {
   267  		return err
   268  	}
   269  	defer session.Close()
   270  
   271  	f.sys.backend.ServiceFilter(ctx, session)
   272  
   273  	for {
   274  		select {
   275  		case number, ok := <-matches:
   276  			// Abort if all matches have been fulfilled
   277  			if !ok {
   278  				err := session.Error()
   279  				if err == nil {
   280  					f.begin = int64(end) + 1
   281  				}
   282  				return err
   283  			}
   284  			f.begin = int64(number) + 1
   285  
   286  			// Retrieve the suggested block and pull any truly matching logs
   287  			header, err := f.sys.backend.HeaderByNumber(ctx, rpc.BlockNumber(number))
   288  			if header == nil || err != nil {
   289  				return err
   290  			}
   291  			found, err := f.checkMatches(ctx, header)
   292  			if err != nil {
   293  				return err
   294  			}
   295  			for _, log := range found {
   296  				logChan <- log
   297  			}
   298  
   299  		case <-ctx.Done():
   300  			return ctx.Err()
   301  		}
   302  	}
   303  }
   304  
   305  // unindexedLogs returns the logs matching the filter criteria based on raw block
   306  // iteration and bloom matching.
   307  func (f *Filter) unindexedLogs(ctx context.Context, end uint64, logChan chan *types.Log) error {
   308  	for ; f.begin <= int64(end); f.begin++ {
   309  		header, err := f.sys.backend.HeaderByNumber(ctx, rpc.BlockNumber(f.begin))
   310  		if header == nil || err != nil {
   311  			return err
   312  		}
   313  		found, err := f.blockLogs(ctx, header)
   314  		if err != nil {
   315  			return err
   316  		}
   317  		for _, log := range found {
   318  			select {
   319  			case logChan <- log:
   320  			case <-ctx.Done():
   321  				return ctx.Err()
   322  			}
   323  		}
   324  	}
   325  	return nil
   326  }
   327  
   328  // blockLogs returns the logs matching the filter criteria within a single block.
   329  func (f *Filter) blockLogs(ctx context.Context, header *types.Header) ([]*types.Log, error) {
   330  	if bloomFilter(header.Bloom, f.addresses, f.topics) {
   331  		return f.checkMatches(ctx, header)
   332  	}
   333  	return nil, nil
   334  }
   335  
   336  // checkMatches checks if the receipts belonging to the given header contain any log events that
   337  // match the filter criteria. This function is called when the bloom filter signals a potential match.
   338  func (f *Filter) checkMatches(ctx context.Context, header *types.Header) ([]*types.Log, error) {
   339  	logsList, err := f.sys.getLogs(ctx, header.Hash(), header.Number.Uint64())
   340  	if err != nil {
   341  		return nil, err
   342  	}
   343  
   344  	unfiltered := types.FlattenLogs(logsList)
   345  	logs := filterLogs(unfiltered, nil, nil, f.addresses, f.topics)
   346  	if len(logs) == 0 {
   347  		return nil, nil
   348  	}
   349  	// Most backends will deliver un-derived logs, but check nevertheless.
   350  	if len(logs) > 0 && logs[0].TxHash != (common.Hash{}) {
   351  		return logs, nil
   352  	}
   353  	// We have matching logs, check if we need to resolve full logs via the light client
   354  	receipts, err := f.sys.backend.GetReceipts(ctx, header.Hash())
   355  	if err != nil {
   356  		return nil, err
   357  	}
   358  	unfiltered = unfiltered[:0]
   359  	for _, receipt := range receipts {
   360  		unfiltered = append(unfiltered, receipt.Logs...)
   361  	}
   362  	logs = filterLogs(unfiltered, nil, nil, f.addresses, f.topics)
   363  
   364  	return logs, nil
   365  }
   366  
   367  // includes returns true if the element is present in the list.
   368  func includes[T comparable](things []T, element T) bool {
   369  	for _, thing := range things {
   370  		if thing == element {
   371  			return true
   372  		}
   373  	}
   374  	return false
   375  }
   376  
   377  // filterLogs creates a slice of logs matching the given criteria.
   378  func filterLogs(logs []*types.Log, fromBlock, toBlock *big.Int, addresses []common.Address, topics [][]common.Hash) []*types.Log {
   379  	var check = func(log *types.Log) bool {
   380  		if fromBlock != nil && fromBlock.Int64() >= 0 && fromBlock.Uint64() > log.BlockNumber {
   381  			return false
   382  		}
   383  		if toBlock != nil && toBlock.Int64() >= 0 && toBlock.Uint64() < log.BlockNumber {
   384  			return false
   385  		}
   386  		if len(addresses) > 0 && !includes(addresses, log.Address) {
   387  			return false
   388  		}
   389  		// If the to filtered topics is greater than the amount of topics in logs, skip.
   390  		if len(topics) > len(log.Topics) {
   391  			return false
   392  		}
   393  		for i, sub := range topics {
   394  			if len(sub) == 0 {
   395  				continue // empty rule set == wildcard
   396  			}
   397  			if !includes(sub, log.Topics[i]) {
   398  				return false
   399  			}
   400  		}
   401  		return true
   402  	}
   403  	var ret []*types.Log
   404  	for _, log := range logs {
   405  		if check(log) {
   406  			ret = append(ret, log)
   407  		}
   408  	}
   409  	return ret
   410  }
   411  
   412  func bloomFilter(bloom types.Bloom, addresses []common.Address, topics [][]common.Hash) bool {
   413  	if len(addresses) > 0 {
   414  		var included bool
   415  		for _, addr := range addresses {
   416  			if types.BloomLookup(bloom, addr) {
   417  				included = true
   418  				break
   419  			}
   420  		}
   421  		if !included {
   422  			return false
   423  		}
   424  	}
   425  
   426  	for _, sub := range topics {
   427  		included := len(sub) == 0 // empty rule set == wildcard
   428  		for _, topic := range sub {
   429  			if types.BloomLookup(bloom, topic) {
   430  				included = true
   431  				break
   432  			}
   433  		}
   434  		if !included {
   435  			return false
   436  		}
   437  	}
   438  	return true
   439  }