diff --git a/Makefile b/Makefile index 946e3a35..266bde6c 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,9 @@ INTEGRATION_TESTS_DIR:=$(ROOT_DIR)/integration-tests RELAYER_DIR:=$(ROOT_DIR)/relayer BUILD_DIR?=$(ROOT_DIR)/build GIT_TAG:=$(shell git describe --tags --exact-match 2>/dev/null) +ifeq ($(GIT_TAG),) +GIT_TAG:=devel +endif GIT_SHA:=$(shell git rev-parse HEAD) DOCKER_PUSH_TAG?=$(shell git describe --tags --exact-match 2>/dev/null || git rev-parse HEAD) LD_FLAGS:="-extldflags=-static \ diff --git a/infra/composer/grafana/dashboards/relayer.json b/infra/composer/grafana/dashboards/relayer.json index b1bbe581..882515b5 100644 --- a/infra/composer/grafana/dashboards/relayer.json +++ b/infra/composer/grafana/dashboards/relayer.json @@ -1609,9 +1609,107 @@ ], "title": "Malicious behaviour", "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unitScale": true + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 40 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "10.3.3", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "relayer_version{relayer_coreum_address=~\"$relayer_coreum_address\"}", + "format": "time_series", + "instant": false, + "legendFormat": "{{relayer_coreum_address}} | {{version}} ", + "range": true, + "refId": "A" + } + ], + "title": "Relayer version", + "type": "timeseries" } ], - "refresh": "auto", + "refresh": "5s", "schemaVersion": 39, "tags": [], "templating": { @@ -1715,7 +1813,7 @@ ] }, "time": { - "from": "now-1h", + "from": "now-5m", "to": "now" }, "timepicker": {}, diff --git a/integration-tests/processes/metrics_test.go b/integration-tests/processes/metrics_test.go index 7a35b806..1a7f96da 100644 --- a/integration-tests/processes/metrics_test.go +++ b/integration-tests/processes/metrics_test.go @@ -24,6 +24,7 @@ import ( coreumintegration "github.com/CoreumFoundation/coreum/v4/testutil/integration" integrationtests "github.com/CoreumFoundation/coreumbridge-xrpl/integration-tests" + "github.com/CoreumFoundation/coreumbridge-xrpl/relayer/buildinfo" "github.com/CoreumFoundation/coreumbridge-xrpl/relayer/coreum" "github.com/CoreumFoundation/coreumbridge-xrpl/relayer/metrics" "github.com/CoreumFoundation/coreumbridge-xrpl/relayer/runner" @@ -761,7 +762,7 @@ func TestRelayerActivityMetrics(t *testing.T) { runnerEnv.RunnerComponents[0].MetricsRegistry.RelayerActivityGaugeVec, map[string]string{ metrics.RelayerCoremAddressLabel: relayer.CoreumAddress, - metrics.ContractActionLabel: "save_evidence", + metrics.ActionLabel: "save_evidence", }, 2, ) @@ -772,7 +773,59 @@ func TestRelayerActivityMetrics(t *testing.T) { runnerEnv.RunnerComponents[0].MetricsRegistry.RelayerActivityGaugeVec, map[string]string{ metrics.RelayerCoremAddressLabel: relayer.CoreumAddress, - metrics.ContractActionLabel: "save_signature", + metrics.ActionLabel: "save_signature", + }, + 1, + ) + } +} + +func TestRelayerVersionMetrics(t *testing.T) { + t.Parallel() + + ctx, chains := integrationtests.NewTestingContext(t) + + envCfg := DefaultRunnerEnvConfig() + envCfg.RelayersCount = 2 + envCfg.SigningThreshold = 2 + runnerEnv := NewRunnerEnv(ctx, t, envCfg, chains) + runnerEnv.StartAllRunnerProcesses() + runnerEnv.StartAllRunnerPeriodicMetricCollectors() + runnerEnv.AllocateTickets(ctx, t, uint32(200)) + + coreumSenderAddress := chains.Coreum.GenAccount() + chains.Coreum.FundAccountWithOptions(ctx, t, coreumSenderAddress, coreumintegration.BalancesOptions{ + Amount: sdkmath.NewIntWithDecimal(1, 6), + }) + + xrplSenderAddressAddress := chains.XRPL.GenAccount(ctx, t, 10) + + valueToSendFromXRPLToCoreum, err := rippledata.NewValue("1.0", true) + require.NoError(t, err) + + amountToSendFromXRPLtoCoreum := rippledata.Amount{ + Value: valueToSendFromXRPLToCoreum, + Currency: xrpl.XRPTokenCurrency, + Issuer: xrpl.XRPTokenIssuer, + } + // save evidence to generate the tx with memo + runnerEnv.SendFromXRPLToCoreum( + ctx, + t, + xrplSenderAddressAddress.String(), + amountToSendFromXRPLtoCoreum, + coreumSenderAddress, + ) + + for _, relayer := range runnerEnv.BootstrappingConfig.Relayers { + awaitGaugeVecMetricState( + ctx, + t, + runnerEnv, + runnerEnv.RunnerComponents[0].MetricsRegistry.RelayerVersion, + map[string]string{ + metrics.RelayerCoremAddressLabel: relayer.CoreumAddress, + metrics.VersionLabel: buildinfo.VersionTag, }, 1, ) diff --git a/relayer/coreum/contract.go b/relayer/coreum/contract.go index 4a3e0715..b0791ff2 100644 --- a/relayer/coreum/contract.go +++ b/relayer/coreum/contract.go @@ -32,6 +32,8 @@ import ( const ( contractLabel = "coreumbridge-xrpl" + // RelayerCoreumMemoPrefix is memo prefix for the relayer transaction. + RelayerCoreumMemoPrefix = "Coreum XRPL bridge relayer version:" eventAttributeAction = "action" eventAttributeHash = "hash" @@ -1800,7 +1802,7 @@ func (c *ContractClient) getTxFactory() client.Factory { WithKeybase(c.clientCtx.Keyring()). WithChainID(c.clientCtx.ChainID()). WithTxConfig(c.clientCtx.TxConfig()). - WithMemo(fmt.Sprintf("Coreum XRPL bridge relayer version: %s", buildinfo.VersionTag)). + WithMemo(fmt.Sprintf("%s %s", RelayerCoreumMemoPrefix, buildinfo.VersionTag)). WithSimulateAndExecute(true) } diff --git a/relayer/metrics/collector.go b/relayer/metrics/collector.go index 90e7e6a4..b1258f44 100644 --- a/relayer/metrics/collector.go +++ b/relayer/metrics/collector.go @@ -82,6 +82,7 @@ type PeriodicCollector struct { registry *Registry xrplRPCClient XRPLRPCClient contractClient ContractClient + clientContext client.Context feemodelClient feemodeltypes.QueryClient coreumBankClient banktypes.QueryClient cometServiceClient sdktxtypes.ServiceClient @@ -90,6 +91,7 @@ type PeriodicCollector struct { transactionEvidencesCachedKeys map[string]struct{} relayersBalancesCachedKeys map[string]struct{} relayerActivityCachedKeys map[string]struct{} + relayerVersionCachedKeys map[string]struct{} cacheMu sync.Mutex } @@ -113,6 +115,7 @@ func NewPeriodicCollector( registry: registry, xrplRPCClient: xrplRPCClient, contractClient: contractClient, + clientContext: clientContext, feemodelClient: feemodeltypes.NewQueryClient(clientContext), coreumBankClient: banktypes.NewQueryClient(clientContext), cometServiceClient: sdktxtypes.NewServiceClient(clientContext), @@ -121,6 +124,7 @@ func NewPeriodicCollector( transactionEvidencesCachedKeys: make(map[string]struct{}), relayersBalancesCachedKeys: make(map[string]struct{}), relayerActivityCachedKeys: make(map[string]struct{}), + relayerVersionCachedKeys: make(map[string]struct{}), cacheMu: sync.Mutex{}, } } @@ -136,10 +140,10 @@ func (c *PeriodicCollector) Start(ctx context.Context) error { transactionEvidencesMetricName: c.collectTransactionEvidences, relayerBalancesMetricName: c.collectRelayerBalances, fmt.Sprintf("%s/%s", freeContractTicketsMetricName, freeXRPLTicketsMetricName): c.collectFreeTickets, - bridgeStateMetricName: c.collectBridgeState, - relayerActivityMetricName: c.collectRelayerActivity, - xrplTokensCoreumSupplyMetricName: c.collectXRPLTokensCoreumSupply, - xrplBridgeAccountReservesMetricName: c.collectXRPLBridgeAccountReserves, + bridgeStateMetricName: c.collectBridgeState, + fmt.Sprintf("%s/%s", relayerActivityMetricName, relayerVersionMetricName): c.collectRelayerActivityAndVersion, + xrplTokensCoreumSupplyMetricName: c.collectXRPLTokensCoreumSupply, + xrplBridgeAccountReservesMetricName: c.collectXRPLBridgeAccountReserves, } return parallel.Run(ctx, func(ctx context.Context, spawn parallel.SpawnFn) error { for name, collector := range periodicCollectors { @@ -411,12 +415,13 @@ func (c *PeriodicCollector) collectBridgeState(ctx context.Context) error { return nil } -func (c *PeriodicCollector) collectRelayerActivity(ctx context.Context) error { +func (c *PeriodicCollector) collectRelayerActivityAndVersion(ctx context.Context) error { contractCfg, err := c.contractClient.GetContractConfig(ctx) if err != nil { return errors.Wrap(err, "failed to get contract config") } - currentValues := make(map[string]gaugeVecValue, 0) + currentRelayerActivityValues := make(map[string]gaugeVecValue, 0) + currentRelayerVersionValues := make(map[string]gaugeVecValue, 0) // get sequentially to prevent rate limit for _, relayer := range contractCfg.Relayers { relayerAddress := relayer.CoreumAddress @@ -438,14 +443,48 @@ func (c *PeriodicCollector) collectRelayerActivity(ctx context.Context) error { } // fill the metric with the data for action, count := range actionCount { - currentValues[strings.Join([]string{relayerAddress.String(), action}, "-")] = gaugeVecValue{ + currentRelayerActivityValues[strings.Join([]string{relayerAddress.String(), action}, "-")] = gaugeVecValue{ keys: []string{relayerAddress.String(), action}, value: float64(count), } } + + // fill relayer version + for _, txRes := range txResponses { + var sdkTx sdk.Tx + if err := c.clientContext.Codec().UnpackAny(txRes.Tx, &sdkTx); err != nil { + return errors.Errorf("failed to unpack sdk.Tx, tx:%v", sdkTx) + } + tx, ok := sdkTx.(*sdktxtypes.Tx) + if !ok { + continue + } + if !strings.HasPrefix(tx.Body.Memo, coreum.RelayerCoreumMemoPrefix) { + continue + } + version := strings.TrimSpace(strings.TrimPrefix(tx.Body.Memo, coreum.RelayerCoreumMemoPrefix)) + if version == "" { + continue + } + + currentRelayerVersionValues[strings.Join([]string{relayerAddress.String(), version}, "-")] = gaugeVecValue{ + keys: []string{relayerAddress.String(), version}, + value: float64(1), + } + break + } } - c.updateGaugeVecAndCachedValues(currentValues, c.relayerActivityCachedKeys, c.registry.RelayerActivityGaugeVec) + c.updateGaugeVecAndCachedValues( + currentRelayerActivityValues, + c.relayerActivityCachedKeys, + c.registry.RelayerActivityGaugeVec, + ) + c.updateGaugeVecAndCachedValues( + currentRelayerVersionValues, + c.relayerVersionCachedKeys, + c.registry.RelayerVersion, + ) return nil } diff --git a/relayer/metrics/registry.go b/relayer/metrics/registry.go index b781ffaf..b8e9bf35 100644 --- a/relayer/metrics/registry.go +++ b/relayer/metrics/registry.go @@ -23,6 +23,7 @@ const ( relayerActivityMetricName = "relayer_activity" xrplTokensCoreumSupplyMetricName = "xrpl_tokens_coreum_supply" xrplBridgeAccountReservesMetricName = "xrpl_bridge_account_reserves" + relayerVersionMetricName = "relayer_version" xrplRPCDecodingErrorCounterMetricName = "xrpl_rpc_decoding_errors_total" // XRPLCurrencyIssuerLabel is XRPL currency issuer label. @@ -37,8 +38,10 @@ const ( RelayerCoremAddressLabel = "relayer_coreum_address" // MaliciousBehaviourKeyLabel malicious behaviour key label. MaliciousBehaviourKeyLabel = "malicious_behaviour_key" - // ContractActionLabel is contract action label. - ContractActionLabel = "action" + // ActionLabel is action label. + ActionLabel = "action" + // VersionLabel is version label. + VersionLabel = "version" ) // Registry contains metrics. @@ -58,6 +61,7 @@ type Registry struct { BridgeStateGauge prometheus.Gauge MaliciousBehaviourGaugeVec *prometheus.GaugeVec RelayerActivityGaugeVec *prometheus.GaugeVec + RelayerVersion *prometheus.GaugeVec XRPLTokensCoreumSupplyGaugeVec *prometheus.GaugeVec XRPLBridgeAccountReservesGauge prometheus.Gauge XRPLRPCDecodingErrorCounter prometheus.Counter @@ -153,7 +157,16 @@ func NewRegistry() *Registry { }, []string{ RelayerCoremAddressLabel, - ContractActionLabel, + ActionLabel, + }, + ), + RelayerVersion: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: relayerVersionMetricName, + Help: "Relayer version", + }, + []string{ + RelayerCoremAddressLabel, + VersionLabel, }, ), XRPLTokensCoreumSupplyGaugeVec: prometheus.NewGaugeVec(prometheus.GaugeOpts{ @@ -194,6 +207,7 @@ func (m *Registry) Register(registry prometheus.Registerer) error { m.BridgeStateGauge, m.MaliciousBehaviourGaugeVec, m.RelayerActivityGaugeVec, + m.RelayerVersion, m.XRPLTokensCoreumSupplyGaugeVec, m.XRPLBridgeAccountReservesGauge, m.XRPLRPCDecodingErrorCounter,