github.com/0xPolygon/supernets2-node@v0.0.0-20230711153321-2fe574524eaa/jsonrpc/query.go (about)

     1  package jsonrpc
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"time"
     7  
     8  	"github.com/0xPolygon/supernets2-node/hex"
     9  	"github.com/0xPolygon/supernets2-node/jsonrpc/types"
    10  	"github.com/ethereum/go-ethereum/common"
    11  	"github.com/gorilla/websocket"
    12  )
    13  
    14  const (
    15  	// FilterTypeLog represents a filter of type log.
    16  	FilterTypeLog = "log"
    17  	// FilterTypeBlock represents a filter of type block.
    18  	FilterTypeBlock = "block"
    19  	// FilterTypePendingTx represent a filter of type pending Tx.
    20  	FilterTypePendingTx = "pendingTx"
    21  )
    22  
    23  // Filter represents a filter.
    24  type Filter struct {
    25  	ID         string
    26  	Type       FilterType
    27  	Parameters interface{}
    28  	LastPoll   time.Time
    29  	WsConn     *websocket.Conn
    30  }
    31  
    32  // FilterType express the type of the filter, block, logs, pending transactions
    33  type FilterType string
    34  
    35  // LogFilter is a filter for logs
    36  type LogFilter struct {
    37  	BlockHash *common.Hash
    38  	FromBlock *types.BlockNumber
    39  	ToBlock   *types.BlockNumber
    40  	Addresses []common.Address
    41  	Topics    [][]common.Hash
    42  	Since     *time.Time
    43  }
    44  
    45  // addTopic adds specific topics to the log filter topics
    46  func (f *LogFilter) addTopic(topics ...string) error {
    47  	if f.Topics == nil {
    48  		f.Topics = [][]common.Hash{}
    49  	}
    50  
    51  	topicsHashes := []common.Hash{}
    52  
    53  	for _, topic := range topics {
    54  		topicHash := common.Hash{}
    55  		if err := topicHash.UnmarshalText([]byte(topic)); err != nil {
    56  			return err
    57  		}
    58  
    59  		topicsHashes = append(topicsHashes, topicHash)
    60  	}
    61  
    62  	f.Topics = append(f.Topics, topicsHashes)
    63  
    64  	return nil
    65  }
    66  
    67  // addAddress Adds the address to the log filter
    68  func (f *LogFilter) addAddress(raw string) error {
    69  	if f.Addresses == nil {
    70  		f.Addresses = []common.Address{}
    71  	}
    72  
    73  	addr := common.Address{}
    74  
    75  	if err := addr.UnmarshalText([]byte(raw)); err != nil {
    76  		return err
    77  	}
    78  
    79  	f.Addresses = append(f.Addresses, addr)
    80  
    81  	return nil
    82  }
    83  
    84  // MarshalJSON allows to customize the JSON representation.
    85  func (f *LogFilter) MarshalJSON() ([]byte, error) {
    86  	var obj types.LogFilterRequest
    87  
    88  	obj.BlockHash = f.BlockHash
    89  
    90  	if f.FromBlock != nil && (*f.FromBlock == types.LatestBlockNumber) {
    91  		fromblock := ""
    92  		obj.FromBlock = &fromblock
    93  	} else if f.FromBlock != nil {
    94  		fromblock := hex.EncodeUint64(uint64(*f.FromBlock))
    95  		obj.FromBlock = &fromblock
    96  	}
    97  
    98  	if f.ToBlock != nil && (*f.ToBlock == types.LatestBlockNumber) {
    99  		toblock := ""
   100  		obj.ToBlock = &toblock
   101  	} else if f.ToBlock != nil {
   102  		toblock := hex.EncodeUint64(uint64(*f.ToBlock))
   103  		obj.ToBlock = &toblock
   104  	}
   105  
   106  	if f.Addresses != nil {
   107  		if len(f.Addresses) == 1 {
   108  			obj.Address = f.Addresses[0].Hex()
   109  		} else {
   110  			obj.Address = f.Addresses
   111  		}
   112  	}
   113  
   114  	obj.Topics = make([]interface{}, 0, len(f.Topics))
   115  	for _, topic := range f.Topics {
   116  		if len(topic) == 0 {
   117  			obj.Topics = append(obj.Topics, nil)
   118  		} else if len(topic) == 1 {
   119  			obj.Topics = append(obj.Topics, topic[0])
   120  		} else {
   121  			obj.Topics = append(obj.Topics, topic)
   122  		}
   123  	}
   124  
   125  	return json.Marshal(obj)
   126  }
   127  
   128  // UnmarshalJSON decodes a json object
   129  func (f *LogFilter) UnmarshalJSON(data []byte) error {
   130  	var obj types.LogFilterRequest
   131  
   132  	err := json.Unmarshal(data, &obj)
   133  
   134  	if err != nil {
   135  		return err
   136  	}
   137  
   138  	f.BlockHash = obj.BlockHash
   139  	lbb := types.LatestBlockNumber
   140  
   141  	if obj.FromBlock != nil && *obj.FromBlock == "" {
   142  		f.FromBlock = &lbb
   143  	} else if obj.FromBlock != nil {
   144  		bn, err := types.StringToBlockNumber(*obj.FromBlock)
   145  		if err != nil {
   146  			return err
   147  		}
   148  		f.FromBlock = &bn
   149  	}
   150  
   151  	if obj.ToBlock != nil && *obj.ToBlock == "" {
   152  		f.ToBlock = &lbb
   153  	} else if obj.ToBlock != nil {
   154  		bn, err := types.StringToBlockNumber(*obj.ToBlock)
   155  		if err != nil {
   156  			return err
   157  		}
   158  		f.ToBlock = &bn
   159  	}
   160  
   161  	if obj.Address != nil {
   162  		// decode address, either "" or [""]
   163  		switch raw := obj.Address.(type) {
   164  		case string:
   165  			// ""
   166  			if err := f.addAddress(raw); err != nil {
   167  				return err
   168  			}
   169  
   170  		case []interface{}:
   171  			// ["", ""]
   172  			for _, addr := range raw {
   173  				if item, ok := addr.(string); ok {
   174  					if err := f.addAddress(item); err != nil {
   175  						return err
   176  					}
   177  				} else {
   178  					return fmt.Errorf("address expected")
   179  				}
   180  			}
   181  
   182  		default:
   183  			return fmt.Errorf("failed to decode address. Expected either '' or ['', '']")
   184  		}
   185  	}
   186  
   187  	if obj.Topics != nil {
   188  		// decode topics, either "" or ["", ""] or null
   189  		for _, item := range obj.Topics {
   190  			switch raw := item.(type) {
   191  			case string:
   192  				// ""
   193  				if err := f.addTopic(raw); err != nil {
   194  					return err
   195  				}
   196  
   197  			case []interface{}:
   198  				// ["", ""]
   199  				res := []string{}
   200  
   201  				for _, i := range raw {
   202  					if item, ok := i.(string); ok {
   203  						res = append(res, item)
   204  					} else {
   205  						return fmt.Errorf("hash expected")
   206  					}
   207  				}
   208  
   209  				if err := f.addTopic(res...); err != nil {
   210  					return err
   211  				}
   212  
   213  			case nil:
   214  				// null
   215  				if err := f.addTopic(); err != nil {
   216  					return err
   217  				}
   218  
   219  			default:
   220  				return fmt.Errorf("failed to decode topics. Expected '' or [''] or null")
   221  			}
   222  		}
   223  	}
   224  
   225  	// decode topics
   226  	return nil
   227  }
   228  
   229  // Match returns whether the receipt includes topics for this filter
   230  func (f *LogFilter) Match(log *types.Log) bool {
   231  	// check addresses
   232  	if len(f.Addresses) > 0 {
   233  		match := false
   234  
   235  		for _, addr := range f.Addresses {
   236  			if addr == log.Address {
   237  				match = true
   238  			}
   239  		}
   240  
   241  		if !match {
   242  			return false
   243  		}
   244  	}
   245  	// check topics
   246  	if len(f.Topics) > len(log.Topics) {
   247  		return false
   248  	}
   249  
   250  	for i, sub := range f.Topics {
   251  		match := len(sub) == 0
   252  
   253  		for _, topic := range sub {
   254  			if log.Topics[i] == topic {
   255  				match = true
   256  
   257  				break
   258  			}
   259  		}
   260  
   261  		if !match {
   262  			return false
   263  		}
   264  	}
   265  
   266  	return true
   267  }