github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/session/pingpong/hermes_status_checker.go (about) 1 /* 2 * Copyright (C) 2021 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 "sync" 23 "time" 24 25 "github.com/ethereum/go-ethereum/common" 26 "github.com/rs/zerolog/log" 27 ) 28 29 // HermesStatusChecker checks hermes activity and caches the results. 30 type HermesStatusChecker struct { 31 mbc mbc 32 observer observerApi 33 cacheDuration time.Duration 34 35 cachedValues map[string]HermesStatus 36 lock sync.Mutex 37 } 38 39 // NewHermesStatusChecker creates a new instance of hermes activity checker. 40 func NewHermesStatusChecker(bc mbc, observer observerApi, cacheDuration time.Duration) *HermesStatusChecker { 41 return &HermesStatusChecker{ 42 cachedValues: make(map[string]HermesStatus), 43 cacheDuration: cacheDuration, 44 mbc: bc, 45 observer: observer, 46 } 47 } 48 49 // HermesStatus represents the hermes status. 50 type HermesStatus struct { 51 HermesID common.Address 52 ChainID int64 53 IsActive bool 54 Fee uint16 55 ValidUntil time.Time 56 } 57 58 func (has HermesStatus) isValid() bool { 59 return time.Now().Before(has.ValidUntil) 60 } 61 62 type mbc interface { 63 IsHermesActive(chainID int64, hermesID common.Address) (bool, error) 64 IsHermesRegistered(chainID int64, registryAddress, hermesID common.Address) (bool, error) 65 GetHermesFee(chainID int64, hermesAddress common.Address) (uint16, error) 66 } 67 68 // GetHermesStatus determines if hermes is active or not. 69 func (hac *HermesStatusChecker) GetHermesStatus(chainID int64, registryAddress common.Address, hermesID common.Address) (HermesStatus, error) { 70 cached, ok := hac.getFromCache(chainID, hermesID) 71 if ok { 72 return cached, nil 73 } 74 75 status, err := hac.fetchHermesStatus(chainID, registryAddress, hermesID) 76 if err != nil { 77 return HermesStatus{}, err 78 } 79 80 status = hac.setInCache(chainID, hermesID, status.IsActive, status.Fee) 81 return status, nil 82 } 83 84 func (hac *HermesStatusChecker) formKey(chainID int64, hermesID common.Address) string { 85 return fmt.Sprintf("%v_%v", hermesID.Hex(), chainID) 86 } 87 88 func (hac *HermesStatusChecker) fetchHermesStatus(chainID int64, registryAddress common.Address, hermesID common.Address) (HermesStatus, error) { 89 // hermes is active if: is registered and is active. 90 isRegistered, err := hac.mbc.IsHermesRegistered(chainID, registryAddress, hermesID) 91 if err != nil { 92 log.Err(err).Msg("using observer as fallback") 93 return hac.fetchFallbackHermesStatus(chainID, hermesID) 94 } 95 96 isActive, err := hac.mbc.IsHermesActive(chainID, hermesID) 97 if err != nil { 98 return HermesStatus{}, fmt.Errorf("could not check if hermes(%v) is active on chain %v: %w", hermesID.Hex(), chainID, err) 99 } 100 101 fee, err := hac.mbc.GetHermesFee(chainID, hermesID) 102 if err != nil { 103 return HermesStatus{}, fmt.Errorf("could not check hermes(%v) fee on chain %v: %w", hermesID.Hex(), chainID, err) 104 } 105 106 status := HermesStatus{ 107 Fee: fee, 108 IsActive: isRegistered && isActive, 109 } 110 111 return status, nil 112 } 113 114 func (hac *HermesStatusChecker) fetchFallbackHermesStatus(chainID int64, hermesID common.Address) (HermesStatus, error) { 115 hermesData, err := hac.observer.GetHermesData(chainID, hermesID) 116 if err != nil { 117 return HermesStatus{}, fmt.Errorf("failed to get hermes data from observer: %w", err) 118 } 119 return HermesStatus{ 120 IsActive: hermesData.Approved, 121 Fee: uint16(hermesData.Fee), 122 }, nil 123 124 } 125 126 func (hac *HermesStatusChecker) setInCache(chainID int64, hermesID common.Address, isActive bool, fee uint16) HermesStatus { 127 hac.lock.Lock() 128 defer hac.lock.Unlock() 129 status := HermesStatus{ 130 HermesID: hermesID, 131 IsActive: isActive, 132 ChainID: chainID, 133 ValidUntil: time.Now().Add(hac.cacheDuration), 134 Fee: fee, 135 } 136 hac.cachedValues[hac.formKey(chainID, hermesID)] = status 137 return status 138 } 139 140 func (hac *HermesStatusChecker) getFromCache(chainID int64, hermesID common.Address) (HermesStatus, bool) { 141 hac.lock.Lock() 142 defer hac.lock.Unlock() 143 144 v, ok := hac.cachedValues[hac.formKey(chainID, hermesID)] 145 if !ok { 146 return HermesStatus{}, false 147 } 148 149 if v.isValid() { 150 return v, true 151 } 152 153 return HermesStatus{}, false 154 }