github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/session/pingpong/hermes_url_getter.go (about)

     1  /*
     2   * Copyright (C) 2020 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 pingpong
    19  
    20  import (
    21  	"fmt"
    22  	"net/url"
    23  	"sync"
    24  
    25  	"github.com/ethereum/go-ethereum/common"
    26  	"github.com/mysteriumnetwork/payments/observer"
    27  	"github.com/rs/zerolog/log"
    28  )
    29  
    30  // HermesURLGetter allows for fetching and storing of hermes urls.
    31  type HermesURLGetter struct {
    32  	addressProvider      addressProvider
    33  	bc                   bc
    34  	loadedAddresses      map[int64]map[common.Address]string
    35  	loaddedAddressesLock sync.Mutex
    36  	observer             observerApi
    37  }
    38  
    39  type addressProvider interface {
    40  	GetActiveChannelAddress(chainID int64, id common.Address) (common.Address, error)
    41  	GetArbitraryChannelAddress(hermes, registry, channel common.Address, id common.Address) (common.Address, error)
    42  	GetActiveChannelImplementation(chainID int64) (common.Address, error)
    43  	GetChannelImplementationForHermes(chainID int64, hermes common.Address) (common.Address, error)
    44  	GetMystAddress(chainID int64) (common.Address, error)
    45  	GetActiveHermes(chainID int64) (common.Address, error)
    46  	GetRegistryAddress(chainID int64) (common.Address, error)
    47  	GetKnownHermeses(chainID int64) ([]common.Address, error)
    48  	GetHermesChannelAddress(chainID int64, id, hermesAddr common.Address) (common.Address, error)
    49  }
    50  
    51  type observerApi interface {
    52  	GetHermeses(f *observer.HermesFilter) (observer.HermesesResponse, error)
    53  	GetHermesData(chainId int64, hermesAddress common.Address) (*observer.HermesResponse, error)
    54  }
    55  
    56  // NewHermesURLGetter creates a new instance of hermes url getter.
    57  func NewHermesURLGetter(
    58  	bc bc,
    59  	addressProvider addressProvider,
    60  	observer observerApi,
    61  ) *HermesURLGetter {
    62  	return &HermesURLGetter{
    63  		loadedAddresses: make(map[int64]map[common.Address]string),
    64  		addressProvider: addressProvider,
    65  		bc:              bc,
    66  		observer:        observer,
    67  	}
    68  }
    69  
    70  type bc interface {
    71  	GetHermesURL(chainID int64, registryID, hermesID common.Address) (string, error)
    72  }
    73  
    74  const suffix = "api/v2"
    75  
    76  func (hug *HermesURLGetter) normalizeAddress(address string) (string, error) {
    77  	u, err := url.ParseRequestURI(address)
    78  	if err != nil {
    79  		return "", fmt.Errorf("could not parse hermes URL: %w", err)
    80  	}
    81  	return fmt.Sprintf("%v://%v/%v", u.Scheme, u.Host, suffix), nil
    82  }
    83  
    84  // GetHermesURL fetches the hermes url from blockchain, observer or local cache if it has already been loaded.
    85  func (hug *HermesURLGetter) GetHermesURL(chainID int64, address common.Address) (string, error) {
    86  	hug.loaddedAddressesLock.Lock()
    87  	defer hug.loaddedAddressesLock.Unlock()
    88  
    89  	addresses, ok := hug.loadedAddresses[chainID]
    90  	if ok {
    91  		v, ok := addresses[address]
    92  		if ok {
    93  			return v, nil
    94  		}
    95  	} else {
    96  		hug.loadedAddresses[chainID] = make(map[common.Address]string, 0)
    97  	}
    98  
    99  	url, err := hug.getHermesURLBC(chainID, address)
   100  	if err != nil {
   101  		log.Err(err).Fields(map[string]any{
   102  			"chain_id":  chainID,
   103  			"hermes_id": address.Hex(),
   104  		}).Msg("failed to get hermes url from blockchain, using fallback")
   105  		url, err = hug.getHermesURLObserver(chainID, address)
   106  		if err != nil {
   107  			return "", err
   108  		}
   109  	}
   110  	url, err = hug.normalizeAddress(url)
   111  	if err != nil {
   112  		return "", err
   113  	}
   114  	hug.loadedAddresses[chainID][address] = url
   115  	return url, nil
   116  }
   117  
   118  func (hug *HermesURLGetter) getHermesURLBC(chainID int64, address common.Address) (string, error) {
   119  	registry, err := hug.addressProvider.GetRegistryAddress(chainID)
   120  	if err != nil {
   121  		return "", err
   122  	}
   123  	return hug.bc.GetHermesURL(chainID, registry, address)
   124  }
   125  
   126  func (hug *HermesURLGetter) getHermesURLObserver(chainID int64, address common.Address) (string, error) {
   127  	hermesData, err := hug.observer.GetHermesData(chainID, address)
   128  	if err != nil {
   129  		return "", err
   130  	}
   131  	return hug.normalizeAddress(hermesData.URL)
   132  }