github.com/badrootd/celestia-core@v0.0.0-20240305091328-aa4207a4b25d/p2p/trust/store.go (about) 1 package trust 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "time" 7 8 dbm "github.com/cometbft/cometbft-db" 9 10 "github.com/badrootd/celestia-core/libs/service" 11 cmtsync "github.com/badrootd/celestia-core/libs/sync" 12 ) 13 14 const defaultStorePeriodicSaveInterval = 1 * time.Minute 15 16 var trustMetricKey = []byte("trustMetricStore") 17 18 // MetricStore - Manages all trust metrics for peers 19 type MetricStore struct { 20 service.BaseService 21 22 // Maps a Peer.Key to that peer's TrustMetric 23 peerMetrics map[string]*Metric 24 25 // Mutex that protects the map and history data file 26 mtx cmtsync.Mutex 27 28 // The db where peer trust metric history data will be stored 29 db dbm.DB 30 31 // This configuration will be used when creating new TrustMetrics 32 config MetricConfig 33 } 34 35 // NewTrustMetricStore returns a store that saves data to the DB 36 // and uses the config when creating new trust metrics. 37 // Use Start to to initialize the trust metric store 38 func NewTrustMetricStore(db dbm.DB, tmc MetricConfig) *MetricStore { 39 tms := &MetricStore{ 40 peerMetrics: make(map[string]*Metric), 41 db: db, 42 config: tmc, 43 } 44 45 tms.BaseService = *service.NewBaseService(nil, "MetricStore", tms) 46 return tms 47 } 48 49 // OnStart implements Service 50 func (tms *MetricStore) OnStart() error { 51 if err := tms.BaseService.OnStart(); err != nil { 52 return err 53 } 54 55 tms.mtx.Lock() 56 defer tms.mtx.Unlock() 57 58 tms.loadFromDB() 59 go tms.saveRoutine() 60 return nil 61 } 62 63 // OnStop implements Service 64 func (tms *MetricStore) OnStop() { 65 tms.BaseService.OnStop() 66 67 tms.mtx.Lock() 68 defer tms.mtx.Unlock() 69 70 // Stop all trust metric go-routines 71 for _, tm := range tms.peerMetrics { 72 if err := tm.Stop(); err != nil { 73 tms.Logger.Error("unable to stop metric store", "error", err) 74 } 75 } 76 77 // Make the final trust history data save 78 tms.saveToDB() 79 } 80 81 // Size returns the number of entries in the trust metric store 82 func (tms *MetricStore) Size() int { 83 tms.mtx.Lock() 84 defer tms.mtx.Unlock() 85 86 return tms.size() 87 } 88 89 // AddPeerTrustMetric takes an existing trust metric and associates it with a peer key. 90 // The caller is expected to call Start on the TrustMetric being added 91 func (tms *MetricStore) AddPeerTrustMetric(key string, tm *Metric) { 92 tms.mtx.Lock() 93 defer tms.mtx.Unlock() 94 95 if key == "" || tm == nil { 96 return 97 } 98 tms.peerMetrics[key] = tm 99 } 100 101 // GetPeerTrustMetric returns a trust metric by peer key 102 func (tms *MetricStore) GetPeerTrustMetric(key string) *Metric { 103 tms.mtx.Lock() 104 defer tms.mtx.Unlock() 105 106 tm, ok := tms.peerMetrics[key] 107 if !ok { 108 // If the metric is not available, we will create it 109 tm = NewMetricWithConfig(tms.config) 110 if err := tm.Start(); err != nil { 111 tms.Logger.Error("unable to start metric store", "error", err) 112 } 113 // The metric needs to be in the map 114 tms.peerMetrics[key] = tm 115 } 116 return tm 117 } 118 119 // PeerDisconnected pauses the trust metric associated with the peer identified by the key 120 func (tms *MetricStore) PeerDisconnected(key string) { 121 tms.mtx.Lock() 122 defer tms.mtx.Unlock() 123 124 // If the Peer that disconnected has a metric, pause it 125 if tm, ok := tms.peerMetrics[key]; ok { 126 tm.Pause() 127 } 128 } 129 130 // Saves the history data for all peers to the store DB. 131 // This public method acquires the trust metric store lock 132 func (tms *MetricStore) SaveToDB() { 133 tms.mtx.Lock() 134 defer tms.mtx.Unlock() 135 136 tms.saveToDB() 137 } 138 139 /* Private methods */ 140 141 // size returns the number of entries in the store without acquiring the mutex 142 func (tms *MetricStore) size() int { 143 return len(tms.peerMetrics) 144 } 145 146 /* Loading & Saving */ 147 /* Both loadFromDB and savetoDB assume the mutex has been acquired */ 148 149 // Loads the history data for all peers from the store DB 150 // cmn.Panics if file is corrupt 151 func (tms *MetricStore) loadFromDB() bool { 152 // Obtain the history data we have so far 153 bytes, err := tms.db.Get(trustMetricKey) 154 if err != nil { 155 panic(err) 156 } 157 if bytes == nil { 158 return false 159 } 160 161 peers := make(map[string]MetricHistoryJSON) 162 err = json.Unmarshal(bytes, &peers) 163 if err != nil { 164 panic(fmt.Sprintf("Could not unmarshal Trust Metric Store DB data: %v", err)) 165 } 166 167 // If history data exists in the file, 168 // load it into trust metric 169 for key, p := range peers { 170 tm := NewMetricWithConfig(tms.config) 171 172 if err := tm.Start(); err != nil { 173 tms.Logger.Error("unable to start metric", "error", err) 174 } 175 tm.Init(p) 176 // Load the peer trust metric into the store 177 tms.peerMetrics[key] = tm 178 } 179 return true 180 } 181 182 // Saves the history data for all peers to the store DB 183 func (tms *MetricStore) saveToDB() { 184 tms.Logger.Debug("Saving TrustHistory to DB", "size", tms.size()) 185 186 peers := make(map[string]MetricHistoryJSON) 187 188 for key, tm := range tms.peerMetrics { 189 // Add an entry for the peer identified by key 190 peers[key] = tm.HistoryJSON() 191 } 192 193 // Write all the data back to the DB 194 bytes, err := json.Marshal(peers) 195 if err != nil { 196 tms.Logger.Error("Failed to encode the TrustHistory", "err", err) 197 return 198 } 199 if err := tms.db.SetSync(trustMetricKey, bytes); err != nil { 200 tms.Logger.Error("failed to flush data to disk", "error", err) 201 } 202 } 203 204 // Periodically saves the trust history data to the DB 205 func (tms *MetricStore) saveRoutine() { 206 t := time.NewTicker(defaultStorePeriodicSaveInterval) 207 defer t.Stop() 208 loop: 209 for { 210 select { 211 case <-t.C: 212 tms.SaveToDB() 213 case <-tms.Quit(): 214 break loop 215 } 216 } 217 }