github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/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  	"context"
    21  	"errors"
    22  	"math/big"
    23  
    24  	"github.com/kisexp/xdchain/common"
    25  	"github.com/kisexp/xdchain/core"
    26  	"github.com/kisexp/xdchain/core/bloombits"
    27  	"github.com/kisexp/xdchain/core/mps"
    28  	"github.com/kisexp/xdchain/core/rawdb"
    29  	"github.com/kisexp/xdchain/core/types"
    30  	"github.com/kisexp/xdchain/core/vm"
    31  	"github.com/kisexp/xdchain/ethdb"
    32  	"github.com/kisexp/xdchain/event"
    33  	"github.com/kisexp/xdchain/rpc"
    34  )
    35  
    36  type Backend interface {
    37  	ChainDb() ethdb.Database
    38  	HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error)
    39  	HeaderByHash(ctx context.Context, blockHash common.Hash) (*types.Header, error)
    40  	GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error)
    41  	GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error)
    42  
    43  	SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription
    44  	SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription
    45  	SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription
    46  	SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription
    47  	SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription
    48  
    49  	BloomStatus() (uint64, uint64)
    50  	ServiceFilter(ctx context.Context, session *bloombits.MatcherSession)
    51  
    52  	// AccountExtraDataStateGetterByNumber returns state getter at a given block height
    53  	AccountExtraDataStateGetterByNumber(ctx context.Context, number rpc.BlockNumber) (vm.AccountExtraDataStateGetter, error)
    54  	PSMR() mps.PrivateStateMetadataResolver
    55  }
    56  
    57  // Filter can be used to retrieve and filter logs.
    58  type Filter struct {
    59  	backend Backend
    60  
    61  	db        ethdb.Database
    62  	addresses []common.Address
    63  	topics    [][]common.Hash
    64  	psi       types.PrivateStateIdentifier
    65  
    66  	block      common.Hash // Block hash if filtering a single block
    67  	begin, end int64       // Range interval if filtering multiple blocks
    68  
    69  	matcher *bloombits.Matcher
    70  }
    71  
    72  // NewRangeFilter creates a new filter which uses a bloom filter on blocks to
    73  // figure out whether a particular block is interesting or not.
    74  func NewRangeFilter(backend Backend, begin, end int64, addresses []common.Address, topics [][]common.Hash, psi types.PrivateStateIdentifier) *Filter {
    75  	// Flatten the address and topic filter clauses into a single bloombits filter
    76  	// system. Since the bloombits are not positional, nil topics are permitted,
    77  	// which get flattened into a nil byte slice.
    78  	var filters [][][]byte
    79  	if len(addresses) > 0 {
    80  		filter := make([][]byte, len(addresses))
    81  		for i, address := range addresses {
    82  			filter[i] = address.Bytes()
    83  		}
    84  		filters = append(filters, filter)
    85  	}
    86  	for _, topicList := range topics {
    87  		filter := make([][]byte, len(topicList))
    88  		for i, topic := range topicList {
    89  			filter[i] = topic.Bytes()
    90  		}
    91  		filters = append(filters, filter)
    92  	}
    93  	size, _ := backend.BloomStatus()
    94  
    95  	// Create a generic filter and convert it into a range filter
    96  	filter := newFilter(backend, addresses, topics, psi)
    97  
    98  	filter.matcher = bloombits.NewMatcher(size, filters)
    99  	filter.begin = begin
   100  	filter.end = end
   101  
   102  	return filter
   103  }
   104  
   105  // NewBlockFilter creates a new filter which directly inspects the contents of
   106  // a block to figure out whether it is interesting or not.
   107  func NewBlockFilter(backend Backend, block common.Hash, addresses []common.Address, topics [][]common.Hash, psi types.PrivateStateIdentifier) *Filter {
   108  	// Create a generic filter and convert it into a block filter
   109  	filter := newFilter(backend, addresses, topics, psi)
   110  	filter.block = block
   111  	return filter
   112  }
   113  
   114  // newFilter creates a generic filter that can either filter based on a block hash,
   115  // or based on range queries. The search criteria needs to be explicitly set.
   116  func newFilter(backend Backend, addresses []common.Address, topics [][]common.Hash, psi types.PrivateStateIdentifier) *Filter {
   117  	return &Filter{
   118  		backend:   backend,
   119  		addresses: addresses,
   120  		topics:    topics,
   121  		psi:       psi,
   122  		db:        backend.ChainDb(),
   123  	}
   124  }
   125  
   126  // Logs searches the blockchain for matching log entries, returning all from the
   127  // first block that contains matches, updating the start of the filter accordingly.
   128  func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) {
   129  	// If we're doing singleton block filtering, execute and return
   130  	if f.block != (common.Hash{}) {
   131  		header, err := f.backend.HeaderByHash(ctx, f.block)
   132  		if err != nil {
   133  			return nil, err
   134  		}
   135  		if header == nil {
   136  			return nil, errors.New("unknown block")
   137  		}
   138  		return f.blockLogs(ctx, header)
   139  	}
   140  	// Figure out the limits of the filter range
   141  	header, _ := f.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
   142  	if header == nil {
   143  		return nil, nil
   144  	}
   145  	head := header.Number.Uint64()
   146  
   147  	if f.begin == -1 {
   148  		f.begin = int64(head)
   149  	}
   150  	end := uint64(f.end)
   151  	if f.end == -1 {
   152  		end = head
   153  	}
   154  	// Gather all indexed logs, and finish with non indexed ones
   155  	var (
   156  		logs []*types.Log
   157  		err  error
   158  	)
   159  	size, sections := f.backend.BloomStatus()
   160  	if indexed := sections * size; indexed > uint64(f.begin) {
   161  		if indexed > end {
   162  			logs, err = f.indexedLogs(ctx, end)
   163  		} else {
   164  			logs, err = f.indexedLogs(ctx, indexed-1)
   165  		}
   166  		if err != nil {
   167  			return logs, err
   168  		}
   169  	}
   170  	rest, err := f.unindexedLogs(ctx, end)
   171  	logs = append(logs, rest...)
   172  	return logs, err
   173  }
   174  
   175  // indexedLogs returns the logs matching the filter criteria based on the bloom
   176  // bits indexed available locally or via the network.
   177  func (f *Filter) indexedLogs(ctx context.Context, end uint64) ([]*types.Log, error) {
   178  	// Create a matcher session and request servicing from the backend
   179  	matches := make(chan uint64, 64)
   180  
   181  	session, err := f.matcher.Start(ctx, uint64(f.begin), end, matches)
   182  	if err != nil {
   183  		return nil, err
   184  	}
   185  	defer session.Close()
   186  
   187  	f.backend.ServiceFilter(ctx, session)
   188  
   189  	// Iterate over the matches until exhausted or context closed
   190  	var logs []*types.Log
   191  
   192  	for {
   193  		select {
   194  		case number, ok := <-matches:
   195  			// Abort if all matches have been fulfilled
   196  			if !ok {
   197  				err := session.Error()
   198  				if err == nil {
   199  					f.begin = int64(end) + 1
   200  				}
   201  				return logs, err
   202  			}
   203  			f.begin = int64(number) + 1
   204  
   205  			// Retrieve the suggested block and pull any truly matching logs
   206  			header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(number))
   207  			if header == nil || err != nil {
   208  				return logs, err
   209  			}
   210  			found, err := f.checkMatches(ctx, header)
   211  			if err != nil {
   212  				return logs, err
   213  			}
   214  			logs = append(logs, found...)
   215  
   216  		case <-ctx.Done():
   217  			return logs, ctx.Err()
   218  		}
   219  	}
   220  }
   221  
   222  // unindexedLogs returns the logs matching the filter criteria based on raw block
   223  // iteration and bloom matching.
   224  func (f *Filter) unindexedLogs(ctx context.Context, end uint64) ([]*types.Log, error) {
   225  	var logs []*types.Log
   226  
   227  	for ; f.begin <= int64(end); f.begin++ {
   228  		header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(f.begin))
   229  		if header == nil || err != nil {
   230  			return logs, err
   231  		}
   232  		found, err := f.blockLogs(ctx, header)
   233  		if err != nil {
   234  			return logs, err
   235  		}
   236  		logs = append(logs, found...)
   237  	}
   238  	return logs, nil
   239  }
   240  
   241  // blockLogs returns the logs matching the filter criteria within a single block.
   242  func (f *Filter) blockLogs(ctx context.Context, header *types.Header) (logs []*types.Log, err error) {
   243  	// Quorum
   244  	// Apply bloom filter for both public bloom and private bloom
   245  	bloomMatches := bloomFilter(header.Bloom, f.addresses, f.topics) ||
   246  		bloomFilter(rawdb.GetPrivateBlockBloom(f.db, header.Number.Uint64()), f.addresses, f.topics)
   247  	if bloomMatches {
   248  		found, err := f.checkMatches(ctx, header)
   249  		if err != nil {
   250  			return logs, err
   251  		}
   252  		logs = append(logs, found...)
   253  	}
   254  	return logs, nil
   255  }
   256  
   257  // checkMatches checks if the receipts belonging to the given header contain any log events that
   258  // match the filter criteria. This function is called when the bloom filter signals a potential match.
   259  func (f *Filter) checkMatches(ctx context.Context, header *types.Header) (logs []*types.Log, err error) {
   260  	// Get the logs of the block
   261  	logsList, err := f.backend.GetLogs(ctx, header.Hash())
   262  	if err != nil {
   263  		return nil, err
   264  	}
   265  	var unfiltered []*types.Log
   266  	for _, logs := range logsList {
   267  		unfiltered = append(unfiltered, logs...)
   268  	}
   269  	logs = filterLogs(unfiltered, nil, nil, f.addresses, f.topics, f.psi)
   270  	if len(logs) > 0 {
   271  		// We have matching logs, check if we need to resolve full logs via the light client
   272  		if logs[0].TxHash == (common.Hash{}) {
   273  			receipts, err := f.backend.GetReceipts(ctx, header.Hash())
   274  			if err != nil {
   275  				return nil, err
   276  			}
   277  			unfiltered = unfiltered[:0]
   278  			for _, receipt := range receipts {
   279  				unfiltered = append(unfiltered, receipt.Logs...)
   280  			}
   281  			logs = filterLogs(unfiltered, nil, nil, f.addresses, f.topics, f.psi)
   282  		}
   283  		return logs, nil
   284  	}
   285  	return nil, nil
   286  }
   287  
   288  func includes(addresses []common.Address, a common.Address) bool {
   289  	for _, addr := range addresses {
   290  		if addr == a {
   291  			return true
   292  		}
   293  	}
   294  
   295  	return false
   296  }
   297  
   298  // filterLogs creates a slice of logs matching the given criteria.
   299  func filterLogs(logs []*types.Log, fromBlock, toBlock *big.Int, addresses []common.Address, topics [][]common.Hash, psi types.PrivateStateIdentifier) []*types.Log {
   300  	var ret []*types.Log
   301  Logs:
   302  	for _, log := range logs {
   303  		if fromBlock != nil && fromBlock.Int64() >= 0 && fromBlock.Uint64() > log.BlockNumber {
   304  			continue
   305  		}
   306  		if toBlock != nil && toBlock.Int64() >= 0 && toBlock.Uint64() < log.BlockNumber {
   307  			continue
   308  		}
   309  
   310  		if len(addresses) > 0 && !includes(addresses, log.Address) {
   311  			continue
   312  		}
   313  
   314  		if len(log.PSI) > 0 && log.PSI != psi {
   315  			continue
   316  		}
   317  		// If the to filtered topics is greater than the amount of topics in logs, skip.
   318  		if len(topics) > len(log.Topics) {
   319  			continue Logs
   320  		}
   321  		for i, sub := range topics {
   322  			match := len(sub) == 0 // empty rule set == wildcard
   323  			for _, topic := range sub {
   324  				if log.Topics[i] == topic {
   325  					match = true
   326  					break
   327  				}
   328  			}
   329  			if !match {
   330  				continue Logs
   331  			}
   332  		}
   333  		ret = append(ret, log)
   334  	}
   335  	return ret
   336  }
   337  
   338  func bloomFilter(bloom types.Bloom, addresses []common.Address, topics [][]common.Hash) bool {
   339  	if len(addresses) > 0 {
   340  		var included bool
   341  		for _, addr := range addresses {
   342  			if types.BloomLookup(bloom, addr) {
   343  				included = true
   344  				break
   345  			}
   346  		}
   347  		if !included {
   348  			return false
   349  		}
   350  	}
   351  
   352  	for _, sub := range topics {
   353  		included := len(sub) == 0 // empty rule set == wildcard
   354  		for _, topic := range sub {
   355  			if types.BloomLookup(bloom, topic) {
   356  				included = true
   357  				break
   358  			}
   359  		}
   360  		if !included {
   361  			return false
   362  		}
   363  	}
   364  	return true
   365  }