github.com/amazechain/amc@v0.1.3/internal/api/filters/filter.go (about)

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