github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/core/discovery/brokerdiscovery/storage.go (about)

     1  /*
     2   * Copyright (C) 2019 The "MysteriumNetwork/node" Authors.
     3   *
     4   * This program is free software: you can redistribute it and/or modify
     5   * it under the terms of the GNU 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   * This program 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 General Public License for more details.
    13   *
    14   * You should have received a copy of the GNU General Public License
    15   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16   */
    17  
    18  package brokerdiscovery
    19  
    20  import (
    21  	"fmt"
    22  	"sync"
    23  
    24  	"github.com/mysteriumnetwork/node/core/discovery"
    25  	"github.com/mysteriumnetwork/node/core/discovery/proposal"
    26  	"github.com/mysteriumnetwork/node/eventbus"
    27  	"github.com/mysteriumnetwork/node/market"
    28  )
    29  
    30  // ProposalReducer proposal match function
    31  type ProposalReducer func(proposal market.ServiceProposal) bool
    32  
    33  // NewStorage creates new instance of ProposalStorage
    34  func NewStorage(eventPublisher eventbus.Publisher) *ProposalStorage {
    35  	return &ProposalStorage{
    36  		eventPublisher: eventPublisher,
    37  		proposals:      make([]market.ServiceProposal, 0),
    38  	}
    39  }
    40  
    41  // ProposalStorage represents table of currently active proposals in Mysterium Discovery
    42  type ProposalStorage struct {
    43  	eventPublisher eventbus.Publisher
    44  
    45  	proposals []market.ServiceProposal
    46  	mutex     sync.RWMutex
    47  }
    48  
    49  // Proposals returns list of proposals in storage
    50  func (s *ProposalStorage) Proposals() []market.ServiceProposal {
    51  	s.mutex.Lock()
    52  	defer s.mutex.Unlock()
    53  
    54  	proposals := make([]market.ServiceProposal, 0)
    55  	proposals = append(proposals, s.proposals...)
    56  	return proposals
    57  }
    58  
    59  // MatchProposals fetches currently active service proposals from storage by match function
    60  func (s *ProposalStorage) MatchProposals(match ProposalReducer) ([]market.ServiceProposal, error) {
    61  	s.mutex.Lock()
    62  	defer s.mutex.Unlock()
    63  
    64  	proposals := make([]market.ServiceProposal, 0)
    65  	for _, p := range s.proposals {
    66  		if match(p) {
    67  			proposals = append(proposals, p)
    68  		}
    69  	}
    70  	return proposals, nil
    71  }
    72  
    73  // FindProposals fetches currently active service proposals from storage by given filter
    74  func (s *ProposalStorage) FindProposals(filter *proposal.Filter) ([]market.ServiceProposal, error) {
    75  	return s.MatchProposals(filter.Matches)
    76  }
    77  
    78  // Countries fetches currently active service proposals from storage by given filter
    79  func (s *ProposalStorage) Countries(filter *proposal.Filter) (map[string]int, error) {
    80  	proposals, err := s.MatchProposals(filter.Matches)
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  
    85  	countries := make(map[string]int, 0)
    86  	for _, p := range proposals {
    87  		countries[p.Location.Country]++
    88  	}
    89  
    90  	return countries, nil
    91  }
    92  
    93  // Set puts given list proposals to storage
    94  func (s *ProposalStorage) Set(proposals []market.ServiceProposal) {
    95  	s.mutex.Lock()
    96  	defer s.mutex.Unlock()
    97  
    98  	proposalsOld := s.proposals
    99  	for _, p := range proposals {
   100  		index, exist := s.getProposalIndex(proposalsOld, p.UniqueID())
   101  		if exist {
   102  			go s.eventPublisher.Publish(discovery.AppTopicProposalUpdated, p)
   103  			proposalsOld = append(proposalsOld[:index], proposalsOld[index+1:]...)
   104  		} else {
   105  			go s.eventPublisher.Publish(discovery.AppTopicProposalAdded, p)
   106  		}
   107  	}
   108  	for _, p := range proposalsOld {
   109  		go s.eventPublisher.Publish(discovery.AppTopicProposalRemoved, p)
   110  	}
   111  	s.proposals = proposals
   112  }
   113  
   114  // HasProposal checks if proposal exists in storage
   115  func (s *ProposalStorage) HasProposal(id market.ProposalID) bool {
   116  	s.mutex.Lock()
   117  	defer s.mutex.Unlock()
   118  
   119  	_, exist := s.getProposalIndex(s.proposals, id)
   120  	return exist
   121  }
   122  
   123  // GetProposal returns proposal from storage
   124  func (s *ProposalStorage) GetProposal(id market.ProposalID) (*market.ServiceProposal, error) {
   125  	s.mutex.Lock()
   126  	defer s.mutex.Unlock()
   127  
   128  	index, exist := s.getProposalIndex(s.proposals, id)
   129  	if !exist {
   130  		return nil, fmt.Errorf(`proposal does not exist: %v`, id)
   131  	}
   132  	return &s.proposals[index], nil
   133  }
   134  
   135  // AddProposal appends given proposal to storage
   136  func (s *ProposalStorage) AddProposal(proposals ...market.ServiceProposal) {
   137  	s.mutex.Lock()
   138  	defer s.mutex.Unlock()
   139  
   140  	for _, p := range proposals {
   141  		if index, exist := s.getProposalIndex(s.proposals, p.UniqueID()); !exist {
   142  			s.eventPublisher.Publish(discovery.AppTopicProposalAdded, p)
   143  			s.proposals = append(s.proposals, p)
   144  		} else {
   145  			s.eventPublisher.Publish(discovery.AppTopicProposalUpdated, p)
   146  			s.proposals[index] = p
   147  		}
   148  	}
   149  }
   150  
   151  // RemoveProposal removes proposal from storage
   152  func (s *ProposalStorage) RemoveProposal(id market.ProposalID) {
   153  	s.mutex.Lock()
   154  	defer s.mutex.Unlock()
   155  
   156  	if index, exist := s.getProposalIndex(s.proposals, id); exist {
   157  		go s.eventPublisher.Publish(discovery.AppTopicProposalRemoved, s.proposals[index])
   158  		s.proposals = append(s.proposals[:index], s.proposals[index+1:]...)
   159  	}
   160  }
   161  
   162  func (s *ProposalStorage) getProposalIndex(proposals []market.ServiceProposal, id market.ProposalID) (int, bool) {
   163  	for index, p := range proposals {
   164  		if p.UniqueID() == id {
   165  			return index, true
   166  		}
   167  	}
   168  
   169  	return 0, false
   170  }