github.com/neatio-net/neatio@v1.7.3-0.20231114194659-f4d7a2226baa/neatptc/filters/filter.go (about)

     1  package filters
     2  
     3  import (
     4  	"context"
     5  	"math/big"
     6  
     7  	"github.com/neatio-net/neatio/chain/core"
     8  	"github.com/neatio-net/neatio/chain/core/bloombits"
     9  	"github.com/neatio-net/neatio/chain/core/types"
    10  	"github.com/neatio-net/neatio/neatdb"
    11  	"github.com/neatio-net/neatio/network/rpc"
    12  	"github.com/neatio-net/neatio/utilities/common"
    13  	"github.com/neatio-net/neatio/utilities/event"
    14  )
    15  
    16  type Backend interface {
    17  	ChainDb() neatdb.Database
    18  	EventMux() *event.TypeMux
    19  	HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error)
    20  	GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error)
    21  	GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error)
    22  
    23  	SubscribeTxPreEvent(chan<- core.TxPreEvent) event.Subscription
    24  	SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription
    25  	SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription
    26  	SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription
    27  
    28  	BloomStatus() (uint64, uint64)
    29  	ServiceFilter(ctx context.Context, session *bloombits.MatcherSession)
    30  }
    31  
    32  type Filter struct {
    33  	backend Backend
    34  
    35  	db         neatdb.Database
    36  	begin, end int64
    37  	addresses  []common.Address
    38  	topics     [][]common.Hash
    39  
    40  	matcher *bloombits.Matcher
    41  }
    42  
    43  func New(backend Backend, begin, end int64, addresses []common.Address, topics [][]common.Hash) *Filter {
    44  
    45  	var filters [][][]byte
    46  	if len(addresses) > 0 {
    47  		filter := make([][]byte, len(addresses))
    48  		for i, address := range addresses {
    49  			filter[i] = address.Bytes()
    50  		}
    51  		filters = append(filters, filter)
    52  	}
    53  	for _, topicList := range topics {
    54  		filter := make([][]byte, len(topicList))
    55  		for i, topic := range topicList {
    56  			filter[i] = topic.Bytes()
    57  		}
    58  		filters = append(filters, filter)
    59  	}
    60  
    61  	size, _ := backend.BloomStatus()
    62  
    63  	return &Filter{
    64  		backend:   backend,
    65  		begin:     begin,
    66  		end:       end,
    67  		addresses: addresses,
    68  		topics:    topics,
    69  		db:        backend.ChainDb(),
    70  		matcher:   bloombits.NewMatcher(size, filters),
    71  	}
    72  }
    73  
    74  func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) {
    75  
    76  	header, _ := f.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
    77  	if header == nil {
    78  		return nil, nil
    79  	}
    80  	head := header.Number.Uint64()
    81  
    82  	if f.begin == -1 {
    83  		f.begin = int64(head)
    84  	}
    85  	end := uint64(f.end)
    86  	if f.end == -1 {
    87  		end = head
    88  	}
    89  
    90  	var (
    91  		logs []*types.Log
    92  		err  error
    93  	)
    94  	size, sections := f.backend.BloomStatus()
    95  	if indexed := sections * size; indexed > uint64(f.begin) {
    96  		if indexed > end {
    97  			logs, err = f.indexedLogs(ctx, end)
    98  		} else {
    99  			logs, err = f.indexedLogs(ctx, indexed-1)
   100  		}
   101  		if err != nil {
   102  			return logs, err
   103  		}
   104  	}
   105  	rest, err := f.unindexedLogs(ctx, end)
   106  	logs = append(logs, rest...)
   107  	return logs, err
   108  }
   109  
   110  func (f *Filter) indexedLogs(ctx context.Context, end uint64) ([]*types.Log, error) {
   111  
   112  	matches := make(chan uint64, 64)
   113  
   114  	session, err := f.matcher.Start(ctx, uint64(f.begin), end, matches)
   115  	if err != nil {
   116  		return nil, err
   117  	}
   118  	defer session.Close()
   119  
   120  	f.backend.ServiceFilter(ctx, session)
   121  
   122  	var logs []*types.Log
   123  
   124  	for {
   125  		select {
   126  		case number, ok := <-matches:
   127  
   128  			if !ok {
   129  				err := session.Error()
   130  				if err == nil {
   131  					f.begin = int64(end) + 1
   132  				}
   133  				return logs, err
   134  			}
   135  			f.begin = int64(number) + 1
   136  
   137  			header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(number))
   138  			if header == nil || err != nil {
   139  				return logs, err
   140  			}
   141  			found, err := f.checkMatches(ctx, header)
   142  			if err != nil {
   143  				return logs, err
   144  			}
   145  			logs = append(logs, found...)
   146  
   147  		case <-ctx.Done():
   148  			return logs, ctx.Err()
   149  		}
   150  	}
   151  }
   152  
   153  func (f *Filter) unindexedLogs(ctx context.Context, end uint64) ([]*types.Log, error) {
   154  	var logs []*types.Log
   155  
   156  	for ; f.begin <= int64(end); f.begin++ {
   157  		header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(f.begin))
   158  		if header == nil || err != nil {
   159  			return logs, err
   160  		}
   161  		if bloomFilter(header.Bloom, f.addresses, f.topics) {
   162  			found, err := f.checkMatches(ctx, header)
   163  			if err != nil {
   164  				return logs, err
   165  			}
   166  			logs = append(logs, found...)
   167  		}
   168  	}
   169  	return logs, nil
   170  }
   171  
   172  func (f *Filter) checkMatches(ctx context.Context, header *types.Header) (logs []*types.Log, err error) {
   173  
   174  	logsList, err := f.backend.GetLogs(ctx, header.Hash())
   175  	if err != nil {
   176  		return nil, err
   177  	}
   178  	var unfiltered []*types.Log
   179  	for _, logs := range logsList {
   180  		unfiltered = append(unfiltered, logs...)
   181  	}
   182  	logs = filterLogs(unfiltered, nil, nil, f.addresses, f.topics)
   183  	if len(logs) > 0 {
   184  
   185  		if logs[0].TxHash == (common.Hash{}) {
   186  			receipts, err := f.backend.GetReceipts(ctx, header.Hash())
   187  			if err != nil {
   188  				return nil, err
   189  			}
   190  			unfiltered = unfiltered[:0]
   191  			for _, receipt := range receipts {
   192  				unfiltered = append(unfiltered, receipt.Logs...)
   193  			}
   194  			logs = filterLogs(unfiltered, nil, nil, f.addresses, f.topics)
   195  		}
   196  		return logs, nil
   197  	}
   198  	return nil, nil
   199  }
   200  
   201  func includes(addresses []common.Address, a common.Address) bool {
   202  	for _, addr := range addresses {
   203  		if addr == a {
   204  			return true
   205  		}
   206  	}
   207  
   208  	return false
   209  }
   210  
   211  func filterLogs(logs []*types.Log, fromBlock, toBlock *big.Int, addresses []common.Address, topics [][]common.Hash) []*types.Log {
   212  	var ret []*types.Log
   213  Logs:
   214  	for _, log := range logs {
   215  		if fromBlock != nil && fromBlock.Int64() >= 0 && fromBlock.Uint64() > log.BlockNumber {
   216  			continue
   217  		}
   218  		if toBlock != nil && toBlock.Int64() >= 0 && toBlock.Uint64() < log.BlockNumber {
   219  			continue
   220  		}
   221  
   222  		if len(addresses) > 0 && !includes(addresses, log.Address) {
   223  			continue
   224  		}
   225  
   226  		if len(topics) > len(log.Topics) {
   227  			continue Logs
   228  		}
   229  		for i, topics := range topics {
   230  			match := len(topics) == 0
   231  			for _, topic := range topics {
   232  				if log.Topics[i] == topic {
   233  					match = true
   234  					break
   235  				}
   236  			}
   237  			if !match {
   238  				continue Logs
   239  			}
   240  		}
   241  		ret = append(ret, log)
   242  	}
   243  	return ret
   244  }
   245  
   246  func bloomFilter(bloom types.Bloom, addresses []common.Address, topics [][]common.Hash) bool {
   247  	if len(addresses) > 0 {
   248  		var included bool
   249  		for _, addr := range addresses {
   250  			if types.BloomLookup(bloom, addr) {
   251  				included = true
   252  				break
   253  			}
   254  		}
   255  		if !included {
   256  			return false
   257  		}
   258  	}
   259  
   260  	for _, sub := range topics {
   261  		included := len(sub) == 0
   262  		for _, topic := range sub {
   263  			if types.BloomLookup(bloom, topic) {
   264  				included = true
   265  				break
   266  			}
   267  		}
   268  		if !included {
   269  			return false
   270  		}
   271  	}
   272  	return true
   273  }