github.com/MetalBlockchain/subnet-evm@v0.4.9/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/MetalBlockchain/subnet-evm/core/bloombits"
    36  	"github.com/MetalBlockchain/subnet-evm/core/types"
    37  	"github.com/MetalBlockchain/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, error) {
    57  	allowUnfinalizedQueries := sys.backend.GetVMConfig().AllowUnfinalizedQueries
    58  	acceptedBlock := sys.backend.LastAcceptedBlock()
    59  
    60  	// Flatten the address and topic filter clauses into a single bloombits filter
    61  	// system. Since the bloombits are not positional, nil topics are permitted,
    62  	// which get flattened into a nil byte slice.
    63  	var filters [][][]byte
    64  	if len(addresses) > 0 {
    65  		filter := make([][]byte, len(addresses))
    66  		for i, address := range addresses {
    67  			filter[i] = address.Bytes()
    68  		}
    69  		filters = append(filters, filter)
    70  	}
    71  	for _, topicList := range topics {
    72  		filter := make([][]byte, len(topicList))
    73  		for i, topic := range topicList {
    74  			filter[i] = topic.Bytes()
    75  		}
    76  		filters = append(filters, filter)
    77  	}
    78  	size, _ := sys.backend.BloomStatus()
    79  
    80  	if !allowUnfinalizedQueries && acceptedBlock != nil {
    81  		lastAccepted := acceptedBlock.Number().Int64()
    82  		if begin >= 0 && begin > lastAccepted {
    83  			return nil, fmt.Errorf("requested from block %d after last accepted block %d", begin, lastAccepted)
    84  		}
    85  		if end >= 0 && end > lastAccepted {
    86  			return nil, fmt.Errorf("requested to block %d after last accepted block %d", end, lastAccepted)
    87  		}
    88  	}
    89  
    90  	// Create a generic filter and convert it into a range filter
    91  	filter := newFilter(sys, addresses, topics)
    92  
    93  	filter.matcher = bloombits.NewMatcher(size, filters)
    94  	filter.begin = begin
    95  	filter.end = end
    96  
    97  	return filter, nil
    98  }
    99  
   100  // NewBlockFilter creates a new filter which directly inspects the contents of
   101  // a block to figure out whether it is interesting or not.
   102  func (sys *FilterSystem) NewBlockFilter(block common.Hash, addresses []common.Address, topics [][]common.Hash) *Filter {
   103  	// Create a generic filter and convert it into a block filter
   104  	filter := newFilter(sys, addresses, topics)
   105  	filter.block = &block
   106  	return filter
   107  }
   108  
   109  // newFilter creates a generic filter that can either filter based on a block hash,
   110  // or based on range queries. The search criteria needs to be explicitly set.
   111  func newFilter(sys *FilterSystem, addresses []common.Address, topics [][]common.Hash) *Filter {
   112  	return &Filter{
   113  		sys:       sys,
   114  		addresses: addresses,
   115  		topics:    topics,
   116  	}
   117  }
   118  
   119  // Logs searches the blockchain for matching log entries, returning all from the
   120  // first block that contains matches, updating the start of the filter accordingly.
   121  func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) {
   122  	// If we're doing singleton block filtering, execute and return
   123  	if f.block != nil {
   124  		header, err := f.sys.backend.HeaderByHash(ctx, *f.block)
   125  		if err != nil {
   126  			return nil, err
   127  		}
   128  		if header == nil {
   129  			return nil, errors.New("unknown block")
   130  		}
   131  		return f.blockLogs(ctx, header, false)
   132  	}
   133  	// Short-cut if all we care about is pending logs
   134  	if f.begin == rpc.PendingBlockNumber.Int64() {
   135  		if f.end != rpc.PendingBlockNumber.Int64() {
   136  			return nil, errors.New("invalid block range")
   137  		}
   138  		// There is no pending block, if the request specifies only the pending block, then return nil.
   139  		return nil, nil
   140  	}
   141  	// Figure out the limits of the filter range
   142  	// LatestBlockNumber is transformed into the last accepted block in HeaderByNumber
   143  	// so it is left in place here.
   144  	header, err := f.sys.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
   145  	if err != nil {
   146  		return nil, err
   147  	}
   148  	if header == nil {
   149  		return nil, nil
   150  	}
   151  	var (
   152  		head = header.Number.Uint64()
   153  		end  = uint64(f.end)
   154  	)
   155  	if f.begin < 0 {
   156  		f.begin = int64(head)
   157  	}
   158  	if f.end < 0 {
   159  		end = head
   160  	}
   161  
   162  	// When querying unfinalized data without a populated end block, it is
   163  	// possible that the begin will be greater than the end.
   164  	//
   165  	// We error in this case to prevent a bad UX where the caller thinks there
   166  	// are no logs from the specified beginning to end (when in reality there may
   167  	// be some).
   168  	if end < uint64(f.begin) {
   169  		return nil, fmt.Errorf("begin block %d is greater than end block %d", f.begin, end)
   170  	}
   171  
   172  	// If the requested range of blocks exceeds the maximum number of blocks allowed by the backend
   173  	// return an error instead of searching for the logs.
   174  	if maxBlocks := f.sys.backend.GetMaxBlocksPerRequest(); int64(end)-f.begin > maxBlocks && maxBlocks > 0 {
   175  		return nil, fmt.Errorf("requested too many blocks from %d to %d, maximum is set to %d", f.begin, int64(end), maxBlocks)
   176  	}
   177  	// Gather all indexed logs, and finish with non indexed ones
   178  	var (
   179  		logs           []*types.Log
   180  		size, sections = f.sys.backend.BloomStatus()
   181  	)
   182  	if indexed := sections * size; indexed > uint64(f.begin) {
   183  		if indexed > end {
   184  			logs, err = f.indexedLogs(ctx, end)
   185  		} else {
   186  			logs, err = f.indexedLogs(ctx, indexed-1)
   187  		}
   188  		if err != nil {
   189  			return logs, err
   190  		}
   191  	}
   192  	rest, err := f.unindexedLogs(ctx, end)
   193  	logs = append(logs, rest...)
   194  	return logs, err
   195  }
   196  
   197  // indexedLogs returns the logs matching the filter criteria based on the bloom
   198  // bits indexed available locally or via the network.
   199  func (f *Filter) indexedLogs(ctx context.Context, end uint64) ([]*types.Log, error) {
   200  	// Create a matcher session and request servicing from the backend
   201  	matches := make(chan uint64, 64)
   202  
   203  	session, err := f.matcher.Start(ctx, uint64(f.begin), end, matches)
   204  	if err != nil {
   205  		return nil, err
   206  	}
   207  	defer session.Close()
   208  
   209  	f.sys.backend.ServiceFilter(ctx, session)
   210  
   211  	// Iterate over the matches until exhausted or context closed
   212  	var logs []*types.Log
   213  
   214  	for {
   215  		select {
   216  		case number, ok := <-matches:
   217  			// Abort if all matches have been fulfilled
   218  			if !ok {
   219  				err := session.Error()
   220  				if err == nil {
   221  					f.begin = int64(end) + 1
   222  				}
   223  				return logs, err
   224  			}
   225  			f.begin = int64(number) + 1
   226  
   227  			// Retrieve the suggested block and pull any truly matching logs
   228  			header, err := f.sys.backend.HeaderByNumber(ctx, rpc.BlockNumber(number))
   229  			if header == nil || err != nil {
   230  				return logs, err
   231  			}
   232  			found, err := f.blockLogs(ctx, header, true)
   233  			if err != nil {
   234  				return logs, err
   235  			}
   236  			logs = append(logs, found...)
   237  
   238  		case <-ctx.Done():
   239  			return logs, ctx.Err()
   240  		}
   241  	}
   242  }
   243  
   244  // unindexedLogs returns the logs matching the filter criteria based on raw block
   245  // iteration and bloom matching.
   246  func (f *Filter) unindexedLogs(ctx context.Context, end uint64) ([]*types.Log, error) {
   247  	var logs []*types.Log
   248  
   249  	for ; f.begin <= int64(end); f.begin++ {
   250  		header, err := f.sys.backend.HeaderByNumber(ctx, rpc.BlockNumber(f.begin))
   251  		if header == nil || err != nil {
   252  			return logs, err
   253  		}
   254  		found, err := f.blockLogs(ctx, header, false)
   255  		if err != nil {
   256  			return logs, err
   257  		}
   258  		logs = append(logs, found...)
   259  	}
   260  	return logs, nil
   261  }
   262  
   263  // blockLogs returns the logs matching the filter criteria within a single block.
   264  func (f *Filter) blockLogs(ctx context.Context, header *types.Header, skipBloom bool) ([]*types.Log, error) {
   265  	// Fast track: no filtering criteria
   266  	if len(f.addresses) == 0 && len(f.topics) == 0 {
   267  		list, err := f.sys.getLogs(ctx, header.Hash(), header.Number.Uint64())
   268  		if err != nil {
   269  			return nil, err
   270  		}
   271  		return types.FlattenLogs(list), nil
   272  	} else if skipBloom || bloomFilter(header.Bloom, f.addresses, f.topics) {
   273  		return f.checkMatches(ctx, header)
   274  	}
   275  	return nil, nil
   276  }
   277  
   278  // checkMatches checks if the receipts belonging to the given header contain any log events that
   279  // match the filter criteria. This function is called when the bloom filter signals a potential match.
   280  func (f *Filter) checkMatches(ctx context.Context, header *types.Header) ([]*types.Log, error) {
   281  	logsList, err := f.sys.getLogs(ctx, header.Hash(), header.Number.Uint64())
   282  	if err != nil {
   283  		return nil, err
   284  	}
   285  
   286  	unfiltered := types.FlattenLogs(logsList)
   287  	logs := filterLogs(unfiltered, nil, nil, f.addresses, f.topics)
   288  	if len(logs) > 0 {
   289  		// We have matching logs, check if we need to resolve full logs via the light client
   290  		if logs[0].TxHash == (common.Hash{}) {
   291  			receipts, err := f.sys.backend.GetReceipts(ctx, header.Hash())
   292  			if err != nil {
   293  				return nil, err
   294  			}
   295  			unfiltered = unfiltered[:0]
   296  			for _, receipt := range receipts {
   297  				unfiltered = append(unfiltered, receipt.Logs...)
   298  			}
   299  			logs = filterLogs(unfiltered, nil, nil, f.addresses, f.topics)
   300  		}
   301  		return logs, nil
   302  	}
   303  	return nil, nil
   304  }
   305  
   306  func includes(addresses []common.Address, a common.Address) bool {
   307  	for _, addr := range addresses {
   308  		if addr == a {
   309  			return true
   310  		}
   311  	}
   312  
   313  	return false
   314  }
   315  
   316  // filterLogs creates a slice of logs matching the given criteria.
   317  func filterLogs(logs []*types.Log, fromBlock, toBlock *big.Int, addresses []common.Address, topics [][]common.Hash) []*types.Log {
   318  	var ret []*types.Log
   319  Logs:
   320  	for _, log := range logs {
   321  		if fromBlock != nil && fromBlock.Int64() >= 0 && fromBlock.Uint64() > log.BlockNumber {
   322  			continue
   323  		}
   324  		if toBlock != nil && toBlock.Int64() >= 0 && toBlock.Uint64() < log.BlockNumber {
   325  			continue
   326  		}
   327  
   328  		if len(addresses) > 0 && !includes(addresses, log.Address) {
   329  			continue
   330  		}
   331  		// If the to filtered topics is greater than the amount of topics in logs, skip.
   332  		if len(topics) > len(log.Topics) {
   333  			continue
   334  		}
   335  		for i, sub := range topics {
   336  			match := len(sub) == 0 // empty rule set == wildcard
   337  			for _, topic := range sub {
   338  				if log.Topics[i] == topic {
   339  					match = true
   340  					break
   341  				}
   342  			}
   343  			if !match {
   344  				continue Logs
   345  			}
   346  		}
   347  		ret = append(ret, log)
   348  	}
   349  	return ret
   350  }
   351  
   352  func bloomFilter(bloom types.Bloom, addresses []common.Address, topics [][]common.Hash) bool {
   353  	if len(addresses) > 0 {
   354  		var included bool
   355  		for _, addr := range addresses {
   356  			if types.BloomLookup(bloom, addr) {
   357  				included = true
   358  				break
   359  			}
   360  		}
   361  		if !included {
   362  			return false
   363  		}
   364  	}
   365  
   366  	for _, sub := range topics {
   367  		included := len(sub) == 0 // empty rule set == wildcard
   368  		for _, topic := range sub {
   369  			if types.BloomLookup(bloom, topic) {
   370  				included = true
   371  				break
   372  			}
   373  		}
   374  		if !included {
   375  			return false
   376  		}
   377  	}
   378  	return true
   379  }