github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/core/quality/morqa_transport.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 quality 19 20 import ( 21 "errors" 22 "strings" 23 "time" 24 25 "github.com/mysteriumnetwork/metrics" 26 "github.com/mysteriumnetwork/node/config" 27 "github.com/mysteriumnetwork/node/core/location/locationstate" 28 "github.com/mysteriumnetwork/node/market" 29 ) 30 31 var errEventNotImplemented = errors.New("event not implemented") 32 33 type locationProvider interface { 34 GetOrigin() locationstate.Location 35 } 36 37 // NewMORQATransport creates transport allowing to send events to Mysterium Quality Oracle - MORQA. 38 func NewMORQATransport(morqaClient *MysteriumMORQA, lp locationProvider) *morqaTransport { 39 return &morqaTransport{ 40 morqaClient: morqaClient, 41 lp: lp, 42 } 43 } 44 45 type morqaTransport struct { 46 morqaClient *MysteriumMORQA 47 lp locationProvider 48 } 49 50 func (t *morqaTransport) SendEvent(event Event) error { 51 if id, metric := mapEventToMetric(event); metric != nil { 52 metric.Version = &metrics.VersionPayload{ 53 Version: event.Application.Version, 54 Os: event.Application.OS, 55 Arch: event.Application.Arch, 56 LauncherVersion: event.Application.LauncherVersion, 57 HostOs: event.Application.HostOS, 58 } 59 metric.Country = t.lp.GetOrigin().Country 60 return t.morqaClient.SendMetric(id, metric) 61 } 62 63 return errEventNotImplemented 64 } 65 66 func mapEventToMetric(event Event) (string, *metrics.Event) { 67 switch event.EventName { 68 case pingEventName: 69 return pingEventToMetricsEvent(event.Context.(pingEventContext)) 70 case unlockEventName: 71 return identityUnlockToMetricsEvent(event.Context.(string)) 72 case sessionEventName: 73 return sessionEventToMetricsEvent(event.Context.(sessionEventContext)) 74 case sessionDataName: 75 return sessionDataToMetricsEvent(event.Context.(sessionDataContext)) 76 case sessionTokensName: 77 return sessionTokensToMetricsEvent(event.Context.(sessionTokensContext)) 78 case proposalEventName: 79 return proposalEventToMetricsEvent(event.Context.(market.ServiceProposal)) 80 case traceEventName: 81 return traceEventToMetricsEvent(event.Context.(sessionTraceContext)) 82 case registerIdentity: 83 return identityRegistrationEvent(event.Context.(registrationEvent)) 84 case natMappingEventName: 85 return natMappingEvent(event.Context.(natMappingContext)) 86 case connectionEvent: 87 return connectionEventToMetricsEvent(event.Context.(ConnectionEvent)) 88 case residentCountryEventName: 89 return residentCountryToMetricsEvent(event.Context.(residentCountryEvent)) 90 case stunDetectionEvent: 91 return natTypeToMetricsEvent(event.Context.(natTypeEvent)) 92 case natTypeDetectionEvent: 93 return natTypeToMetricsEvent(event.Context.(natTypeEvent)) 94 case natTraversalMethod: 95 return natTraversalMethodToMetricsEvent(event.Context.(natMethodEvent)) 96 } 97 98 return "", nil 99 } 100 101 func natTraversalMethodToMetricsEvent(event natMethodEvent) (string, *metrics.Event) { 102 return event.ID, &metrics.Event{ 103 IsProvider: true, 104 Metric: &metrics.Event_NatMethod{ 105 NatMethod: &metrics.NatMethodResult{ 106 Method: event.NATMethod, 107 Success: event.Success, 108 }, 109 }, 110 } 111 } 112 113 func natTypeToMetricsEvent(event natTypeEvent) (string, *metrics.Event) { 114 return event.ID, &metrics.Event{ 115 Metric: &metrics.Event_StunDetection{ 116 StunDetection: &metrics.STUNDetectionPayload{ 117 Type: event.NATType, 118 }, 119 }, 120 } 121 } 122 123 func residentCountryToMetricsEvent(event residentCountryEvent) (string, *metrics.Event) { 124 return event.ID, &metrics.Event{ 125 Metric: &metrics.Event_ResidentCountry{ 126 ResidentCountry: &metrics.ResidentCountryPayload{ 127 Country: event.Country, 128 }, 129 }, 130 } 131 } 132 133 func pingEventToMetricsEvent(context pingEventContext) (string, *metrics.Event) { 134 sender, target, country := context.Consumer, context.Provider, context.ProviderCountry 135 if context.IsProvider { 136 sender, target, country = context.Provider, context.Consumer, context.ConsumerCountry 137 } 138 139 return sender, &metrics.Event{ 140 IsProvider: context.IsProvider, 141 TargetId: target, 142 Metric: &metrics.Event_PingEvent{ 143 PingEvent: &metrics.PingPayload{ 144 SessionId: context.ID, 145 RemoteCountry: country, 146 Duration: uint64(context.Duration), 147 }, 148 }, 149 } 150 } 151 152 func connectionEventToMetricsEvent(context ConnectionEvent) (string, *metrics.Event) { 153 return context.ConsumerID, &metrics.Event{ 154 IsProvider: false, 155 TargetId: context.ProviderID, 156 Metric: &metrics.Event_ConnectionEvent{ 157 ConnectionEvent: &metrics.ConnectionEvent{ 158 ServiceType: context.ServiceType, 159 HermesId: context.HermesID, 160 Stage: context.Stage, 161 Error: context.Error, 162 }, 163 }, 164 } 165 } 166 167 func natMappingEvent(context natMappingContext) (string, *metrics.Event) { 168 var errMsg string 169 if context.ErrorMessage != nil { 170 errMsg = *context.ErrorMessage 171 } 172 173 return context.ID, &metrics.Event{ 174 IsProvider: true, 175 Metric: &metrics.Event_NatMappingPayload{ 176 NatMappingPayload: &metrics.NatMappingPayload{ 177 Stage: context.Stage, 178 Successful: context.Successful, 179 Err: errMsg, 180 }, 181 }, 182 } 183 } 184 185 func identityRegistrationEvent(data registrationEvent) (string, *metrics.Event) { 186 return data.Identity, &metrics.Event{ 187 Metric: &metrics.Event_RegistrationPayload{ 188 RegistrationPayload: &metrics.RegistrationPayload{ 189 Status: data.Status, 190 }, 191 }, 192 } 193 } 194 195 func identityUnlockToMetricsEvent(id string) (string, *metrics.Event) { 196 return id, &metrics.Event{} 197 } 198 199 func sessionEventToMetricsEvent(ctx sessionEventContext) (string, *metrics.Event) { 200 sender, target, country := ctx.Consumer, ctx.Provider, ctx.ProviderCountry 201 if ctx.IsProvider { 202 sender, target, country = ctx.Provider, ctx.Consumer, ctx.ConsumerCountry 203 } 204 205 return sender, &metrics.Event{ 206 TargetId: target, 207 IsProvider: ctx.IsProvider, 208 Metric: &metrics.Event_SessionEventPayload{ 209 SessionEventPayload: &metrics.SessionEventPayload{ 210 Event: ctx.Event, 211 Session: &metrics.SessionPayload{ 212 Id: ctx.ID, 213 ServiceType: ctx.ServiceType, 214 RemoteCountry: country, 215 HermesId: ctx.AccountantID, 216 }, 217 }, 218 }, 219 } 220 } 221 222 func sessionDataToMetricsEvent(ctx sessionDataContext) (string, *metrics.Event) { 223 sender, target, country := ctx.Consumer, ctx.Provider, ctx.ProviderCountry 224 if ctx.IsProvider { 225 sender, target, country = ctx.Provider, ctx.Consumer, ctx.ConsumerCountry 226 } 227 228 return sender, &metrics.Event{ 229 TargetId: target, 230 IsProvider: ctx.IsProvider, 231 Metric: &metrics.Event_SessionStatisticsPayload{ 232 SessionStatisticsPayload: &metrics.SessionStatisticsPayload{ 233 BytesSent: ctx.Tx, 234 BytesReceived: ctx.Rx, 235 Duration: duration(ctx.StartedAt), 236 Session: &metrics.SessionPayload{ 237 Id: ctx.ID, 238 ServiceType: ctx.ServiceType, 239 RemoteCountry: country, 240 HermesId: ctx.AccountantID, 241 }, 242 }, 243 }, 244 } 245 } 246 247 func sessionTokensToMetricsEvent(ctx sessionTokensContext) (string, *metrics.Event) { 248 return ctx.Consumer, &metrics.Event{ 249 TargetId: ctx.Provider, 250 IsProvider: false, 251 Metric: &metrics.Event_SessionTokensPayload{ 252 SessionTokensPayload: &metrics.SessionTokensPayload{ 253 Tokens: ctx.Tokens.Text(10), 254 Session: &metrics.SessionPayload{ 255 Id: ctx.ID, 256 ServiceType: ctx.ServiceType, 257 RemoteCountry: ctx.ProviderCountry, 258 HermesId: ctx.AccountantID, 259 }, 260 }, 261 }, 262 } 263 } 264 265 func proposalEventToMetricsEvent(ctx market.ServiceProposal) (string, *metrics.Event) { 266 return ctx.ProviderID, &metrics.Event{ 267 IsProvider: true, 268 Metric: &metrics.Event_ProposalPayload{ 269 ProposalPayload: &metrics.ProposalPayload{ 270 ServiceType: ctx.ServiceType, 271 NodeType: ctx.Location.IPType, 272 VendorId: config.GetString(config.FlagVendorID), 273 }, 274 }, 275 } 276 } 277 278 func traceEventToMetricsEvent(ctx sessionTraceContext) (string, *metrics.Event) { 279 sender, target, isProvider, country := ctx.Consumer, ctx.Provider, false, ctx.ProviderCountry 280 // TODO Remove this workaround by generating&signing&publishing `metrics.Event` in same place 281 if strings.HasPrefix(ctx.Stage, "Provider") { 282 sender, target, isProvider, country = ctx.Provider, ctx.Consumer, true, ctx.ConsumerCountry 283 } 284 285 return sender, &metrics.Event{ 286 TargetId: target, 287 IsProvider: isProvider, 288 Metric: &metrics.Event_SessionTracePayload{ 289 SessionTracePayload: &metrics.SessionTracePayload{ 290 Duration: uint64(ctx.Duration.Nanoseconds()), 291 Stage: ctx.Stage, 292 Session: &metrics.SessionPayload{ 293 Id: ctx.ID, 294 ServiceType: ctx.ServiceType, 295 RemoteCountry: country, 296 HermesId: ctx.AccountantID, 297 }, 298 }, 299 }, 300 } 301 } 302 303 func duration(startedAt time.Time) uint64 { 304 if startedAt.IsZero() { 305 return 0 306 } 307 308 return uint64(time.Since(startedAt).Seconds()) 309 }