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  }