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

     1  package jsonrpc
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"sync"
     7  	"time"
     8  
     9  	"github.com/0xPolygon/supernets2-node/hex"
    10  	"github.com/google/uuid"
    11  	"github.com/gorilla/websocket"
    12  )
    13  
    14  // ErrNotFound represent a not found error.
    15  var ErrNotFound = errors.New("object not found")
    16  
    17  // ErrFilterInvalidPayload indicates there is an invalid payload when creating a filter
    18  var ErrFilterInvalidPayload = errors.New("invalid argument 0: cannot specify both BlockHash and FromBlock/ToBlock, choose one or the other")
    19  
    20  // Storage uses memory to store the data
    21  // related to the json rpc server
    22  type Storage struct {
    23  	filters sync.Map
    24  }
    25  
    26  // NewStorage creates and initializes an instance of Storage
    27  func NewStorage() *Storage {
    28  	return &Storage{
    29  		filters: sync.Map{},
    30  	}
    31  }
    32  
    33  // NewLogFilter persists a new log filter
    34  func (s *Storage) NewLogFilter(wsConn *websocket.Conn, filter LogFilter) (string, error) {
    35  	if filter.BlockHash != nil && (filter.FromBlock != nil || filter.ToBlock != nil) {
    36  		return "", ErrFilterInvalidPayload
    37  	}
    38  
    39  	return s.createFilter(FilterTypeLog, filter, wsConn)
    40  }
    41  
    42  // NewBlockFilter persists a new block log filter
    43  func (s *Storage) NewBlockFilter(wsConn *websocket.Conn) (string, error) {
    44  	return s.createFilter(FilterTypeBlock, nil, wsConn)
    45  }
    46  
    47  // NewPendingTransactionFilter persists a new pending transaction filter
    48  func (s *Storage) NewPendingTransactionFilter(wsConn *websocket.Conn) (string, error) {
    49  	return s.createFilter(FilterTypePendingTx, nil, wsConn)
    50  }
    51  
    52  // create persists the filter to the memory and provides the filter id
    53  func (s *Storage) createFilter(t FilterType, parameters interface{}, wsConn *websocket.Conn) (string, error) {
    54  	lastPoll := time.Now().UTC()
    55  	id, err := s.generateFilterID()
    56  	if err != nil {
    57  		return "", fmt.Errorf("failed to generate filter ID: %w", err)
    58  	}
    59  	s.filters.Store(id, &Filter{
    60  		ID:         id,
    61  		Type:       t,
    62  		Parameters: parameters,
    63  		LastPoll:   lastPoll,
    64  		WsConn:     wsConn,
    65  	})
    66  
    67  	return id, nil
    68  }
    69  
    70  func (s *Storage) generateFilterID() (string, error) {
    71  	r, err := uuid.NewRandom()
    72  	if err != nil {
    73  		return "", err
    74  	}
    75  
    76  	b, err := r.MarshalBinary()
    77  	if err != nil {
    78  		return "", err
    79  	}
    80  
    81  	id := hex.EncodeToHex(b)
    82  	return id, nil
    83  }
    84  
    85  // GetAllBlockFiltersWithWSConn returns an array with all filter that have
    86  // a web socket connection and are filtering by new blocks
    87  func (s *Storage) GetAllBlockFiltersWithWSConn() ([]*Filter, error) {
    88  	filtersWithWSConn := []*Filter{}
    89  	s.filters.Range(func(key, value any) bool {
    90  		filter := value.(*Filter)
    91  		if filter.WsConn == nil || filter.Type != FilterTypeBlock {
    92  			return true
    93  		}
    94  
    95  		f := filter
    96  		filtersWithWSConn = append(filtersWithWSConn, f)
    97  		return true
    98  	})
    99  
   100  	return filtersWithWSConn, nil
   101  }
   102  
   103  // GetAllLogFiltersWithWSConn returns an array with all filter that have
   104  // a web socket connection and are filtering by new logs
   105  func (s *Storage) GetAllLogFiltersWithWSConn() ([]*Filter, error) {
   106  	filtersWithWSConn := []*Filter{}
   107  	s.filters.Range(func(key, value any) bool {
   108  		filter := value.(*Filter)
   109  		if filter.WsConn == nil || filter.Type != FilterTypeLog {
   110  			return true
   111  		}
   112  
   113  		f := filter
   114  		filtersWithWSConn = append(filtersWithWSConn, f)
   115  		return true
   116  	})
   117  
   118  	return filtersWithWSConn, nil
   119  }
   120  
   121  // GetFilter gets a filter by its id
   122  func (s *Storage) GetFilter(filterID string) (*Filter, error) {
   123  	filter, found := s.filters.Load(filterID)
   124  	if !found {
   125  		return nil, ErrNotFound
   126  	}
   127  
   128  	return filter.(*Filter), nil
   129  }
   130  
   131  // UpdateFilterLastPoll updates the last poll to now
   132  func (s *Storage) UpdateFilterLastPoll(filterID string) error {
   133  	filterValue, found := s.filters.Load(filterID)
   134  	if !found {
   135  		return ErrNotFound
   136  	}
   137  	filter := filterValue.(*Filter)
   138  	filter.LastPoll = time.Now().UTC()
   139  	s.filters.Store(filterID, filter)
   140  	return nil
   141  }
   142  
   143  // UninstallFilter deletes a filter by its id
   144  func (s *Storage) UninstallFilter(filterID string) error {
   145  	_, found := s.filters.Load(filterID)
   146  	if !found {
   147  		return ErrNotFound
   148  	}
   149  	s.filters.Delete(filterID)
   150  	return nil
   151  }
   152  
   153  // UninstallFilterByWSConn deletes all filters connected to the provided web socket connection
   154  func (s *Storage) UninstallFilterByWSConn(wsConn *websocket.Conn) error {
   155  	filterIDsToDelete := []string{}
   156  	s.filters.Range(func(key, value any) bool {
   157  		id := key.(string)
   158  		filter := value.(*Filter)
   159  		if filter.WsConn == wsConn {
   160  			filterIDsToDelete = append(filterIDsToDelete, id)
   161  		}
   162  		return true
   163  	})
   164  
   165  	for _, filterID := range filterIDsToDelete {
   166  		s.filters.Delete(filterID)
   167  	}
   168  
   169  	return nil
   170  }