From 9f119ec61d3342e3bd8e0effda6acd7ffed71a78 Mon Sep 17 00:00:00 2001 From: vgorkavenko Date: Mon, 26 Jun 2023 15:53:43 +0400 Subject: [PATCH 1/4] feat: keys-api interface --- .../alerts/CriticalMissedAttestations.ts | 6 +- .../alerts/CriticalMissedProposes.ts | 6 +- .../alerts/CriticalNegativeDelta.ts | 4 +- .../alertmanager/alerts/CriticalSlashing.ts | 4 +- src/common/config/env.validation.ts | 20 ++ src/common/prometheus/prometheus.constants.ts | 2 + src/common/prometheus/prometheus.service.ts | 124 ++++++++---- .../file-source/file-source.service.ts | 29 +-- .../keysapi-source/index.ts | 2 + .../keysapi-source/keysapi-source.client.ts | 182 ++++++++++++++++++ .../keysapi-source/keysapi-source.module.ts | 21 ++ .../keysapi-source/keysapi-source.service.ts | 107 ++++++++++ .../lido-source/lido-source.service.ts | 13 +- .../registry-source.interface.ts | 5 +- .../validators-registry/registry.module.ts | 19 +- .../validators-registry/registry.service.ts | 2 +- src/duty/state/state.metrics.ts | 102 +++++----- src/duty/state/state.service.ts | 7 +- src/duty/summary/summary.metrics.ts | 2 +- src/duty/summary/summary.service.ts | 1 + src/duty/sync/sync.metrics.ts | 5 +- .../clickhouse/clickhouse.constants.ts | 156 +++++++++------ src/storage/clickhouse/clickhouse.service.ts | 45 +++-- src/storage/clickhouse/clickhouse.types.ts | 17 ++ .../migrations/migration_000007_module_id.ts | 7 + 25 files changed, 691 insertions(+), 197 deletions(-) create mode 100644 src/common/validators-registry/keysapi-source/index.ts create mode 100644 src/common/validators-registry/keysapi-source/keysapi-source.client.ts create mode 100644 src/common/validators-registry/keysapi-source/keysapi-source.module.ts create mode 100644 src/common/validators-registry/keysapi-source/keysapi-source.service.ts create mode 100644 src/storage/clickhouse/migrations/migration_000007_module_id.ts diff --git a/src/common/alertmanager/alerts/CriticalMissedAttestations.ts b/src/common/alertmanager/alerts/CriticalMissedAttestations.ts index 55bc607b..9b9109ed 100644 --- a/src/common/alertmanager/alerts/CriticalMissedAttestations.ts +++ b/src/common/alertmanager/alerts/CriticalMissedAttestations.ts @@ -20,8 +20,10 @@ export class CriticalMissedAttestations extends Alert { const nosStats = await this.storage.getUserNodeOperatorsStats(epoch); const missedAttValidatorsCount = await this.storage.getValidatorCountWithMissedAttestationsLastNEpoch(epoch); for (const noStats of nosStats.filter((o) => o.active_ongoing > this.config.get('CRITICAL_ALERTS_MIN_VAL_COUNT'))) { - const operator = this.operators.find((o) => +noStats.val_nos_id == o.index); - const missedAtt = missedAttValidatorsCount.find((a) => a.val_nos_id != null && +a.val_nos_id == operator.index); + const operator = this.operators.find((o) => +noStats.val_nos_module_id == o.module && +noStats.val_nos_id == o.index); + const missedAtt = missedAttValidatorsCount.find( + (a) => a.val_nos_id != null && +a.val_nos_module_id == operator.module && +a.val_nos_id == operator.index, + ); if (!missedAtt) continue; if (missedAtt.amount > noStats.active_ongoing * VALIDATORS_WITH_MISSED_ATTESTATION_COUNT_THRESHOLD) { result[operator.name] = { ongoing: noStats.active_ongoing, missedAtt: missedAtt.amount }; diff --git a/src/common/alertmanager/alerts/CriticalMissedProposes.ts b/src/common/alertmanager/alerts/CriticalMissedProposes.ts index fdbd60f3..a2d98993 100644 --- a/src/common/alertmanager/alerts/CriticalMissedProposes.ts +++ b/src/common/alertmanager/alerts/CriticalMissedProposes.ts @@ -20,8 +20,10 @@ export class CriticalMissedProposes extends Alert { const nosStats = await this.storage.getUserNodeOperatorsStats(epoch); const proposes = await this.storage.getUserNodeOperatorsProposesStats(epoch); // ~12h range for (const noStats of nosStats.filter((o) => o.active_ongoing > this.config.get('CRITICAL_ALERTS_MIN_VAL_COUNT'))) { - const operator = this.operators.find((o) => +noStats.val_nos_id == o.index); - const proposeStats = proposes.find((a) => a.val_nos_id != null && +a.val_nos_id == operator.index); + const operator = this.operators.find((o) => +noStats.val_nos_module_id == o.module && +noStats.val_nos_id == o.index); + const proposeStats = proposes.find( + (a) => a.val_nos_id != null && +a.val_nos_module_id == operator.module && +a.val_nos_id == operator.index, + ); if (!proposeStats) continue; if (proposeStats.missed > proposeStats.all * VALIDATORS_WITH_MISSED_PROPOSALS_COUNT_THRESHOLD) { result[operator.name] = { all: proposeStats.all, missed: proposeStats.missed }; diff --git a/src/common/alertmanager/alerts/CriticalNegativeDelta.ts b/src/common/alertmanager/alerts/CriticalNegativeDelta.ts index 366b796d..50a82a87 100644 --- a/src/common/alertmanager/alerts/CriticalNegativeDelta.ts +++ b/src/common/alertmanager/alerts/CriticalNegativeDelta.ts @@ -20,8 +20,8 @@ export class CriticalNegativeDelta extends Alert { const nosStats = await this.storage.getUserNodeOperatorsStats(epoch); const negativeValidatorsCount = await this.storage.getValidatorsCountWithNegativeDelta(epoch); for (const noStats of nosStats.filter((o) => o.active_ongoing > this.config.get('CRITICAL_ALERTS_MIN_VAL_COUNT'))) { - const operator = this.operators.find((o) => +noStats.val_nos_id == o.index); - const negDelta = negativeValidatorsCount.find((a) => +a.val_nos_id == operator.index); + const operator = this.operators.find((o) => +noStats.val_nos_module_id == o.module && +noStats.val_nos_id == o.index); + const negDelta = negativeValidatorsCount.find((a) => +a.val_nos_module_id == operator.module && +a.val_nos_id == operator.index); if (!negDelta) continue; if (negDelta.amount > noStats.active_ongoing * VALIDATORS_WITH_NEGATIVE_DELTA_COUNT_THRESHOLD) { result[operator.name] = { ongoing: noStats.active_ongoing, negDelta: negDelta.amount }; diff --git a/src/common/alertmanager/alerts/CriticalSlashing.ts b/src/common/alertmanager/alerts/CriticalSlashing.ts index 895421b3..a4732d31 100644 --- a/src/common/alertmanager/alerts/CriticalSlashing.ts +++ b/src/common/alertmanager/alerts/CriticalSlashing.ts @@ -17,8 +17,8 @@ export class CriticalSlashing extends Alert { const currOperators = await this.storage.getUserNodeOperatorsStats(epoch); const prevOperators = await this.storage.getUserNodeOperatorsStats(epoch - 1); // compare with previous epoch for (const currOperator of currOperators) { - const operator = this.operators.find((o) => +currOperator.val_nos_id == o.index); - const prevOperator = prevOperators.find((a) => a.val_nos_id == currOperator.val_nos_id); + const operator = this.operators.find((o) => +currOperator.val_nos_module_id == o.module && +currOperator.val_nos_id == o.index); + const prevOperator = prevOperators.find((a) => +a.val_nos_module_id == operator.module && +a.val_nos_id == operator.index); // if count of slashed validators increased, we should alert about it const prevSlashed = prevOperator ? prevOperator.slashed : 0; if (currOperator.slashed > prevSlashed) { diff --git a/src/common/config/env.validation.ts b/src/common/config/env.validation.ts index 89cfb019..23be4068 100644 --- a/src/common/config/env.validation.ts +++ b/src/common/config/env.validation.ts @@ -28,6 +28,7 @@ export enum Network { export enum ValidatorRegistrySource { Lido = 'lido', File = 'file', + KeysAPI = 'keysapi', } const toBoolean = (value: any): boolean => { @@ -180,6 +181,25 @@ export class EnvironmentVariables { @IsString() public VALIDATOR_REGISTRY_LIDO_SOURCE_SQLITE_CACHE_PATH = './docker/validators/lido_mainnet.db'; + @IsArray() + @ArrayMinSize(1) + @Transform(({ value }) => value.split(',')) + @ValidateIf((vars) => vars.VALIDATOR_REGISTRY_SOURCE == ValidatorRegistrySource.KeysAPI && vars.NODE_ENV != Environment.test) + public VALIDATOR_REGISTRY_KEYSAPI_SOURCE_URLS = []; + + @IsInt() + @Transform(({ value }) => parseInt(value, 10), { toClassOnly: true }) + public VALIDATOR_REGISTRY_KEYSAPI_SOURCE_RETRY_DELAY_MS = 500; + + @IsNumber() + @Min(5000) + @Transform(({ value }) => parseInt(value, 10), { toClassOnly: true }) + public VALIDATOR_REGISTRY_KEYSAPI_SOURCE_RESPONSE_TIMEOUT = 30000; + + @IsNumber() + @Transform(({ value }) => parseInt(value, 10), { toClassOnly: true }) + public VALIDATOR_REGISTRY_KEYSAPI_SOURCE_MAX_RETRIES = 2; + /** * Use a file with list of validators that are stuck and should be excluded from the monitoring metrics */ diff --git a/src/common/prometheus/prometheus.constants.ts b/src/common/prometheus/prometheus.constants.ts index c00b5bfd..e5ab7330 100644 --- a/src/common/prometheus/prometheus.constants.ts +++ b/src/common/prometheus/prometheus.constants.ts @@ -9,6 +9,8 @@ export const METRIC_OUTGOING_EL_REQUESTS_DURATION_SECONDS = `outgoing_el_request export const METRIC_OUTGOING_EL_REQUESTS_COUNT = `outgoing_el_requests_count`; export const METRIC_OUTGOING_CL_REQUESTS_DURATION_SECONDS = `outgoing_cl_requests_duration_seconds`; export const METRIC_OUTGOING_CL_REQUESTS_COUNT = `outgoing_cl_requests_count`; +export const METRIC_OUTGOING_KEYSAPI_REQUESTS_DURATION_SECONDS = `outgoing_keysapi_requests_duration_seconds`; +export const METRIC_OUTGOING_KEYSAPI_REQUESTS_COUNT = `outgoing_keysapi_requests_count`; export const METRIC_TASK_DURATION_SECONDS = `task_duration_seconds`; export const METRIC_TASK_RESULT_COUNT = `task_result_count`; export const METRIC_DATA_ACTUALITY = `data_actuality`; diff --git a/src/common/prometheus/prometheus.service.ts b/src/common/prometheus/prometheus.service.ts index 6b351889..81e7beb4 100644 --- a/src/common/prometheus/prometheus.service.ts +++ b/src/common/prometheus/prometheus.service.ts @@ -46,6 +46,8 @@ import { METRIC_OUTGOING_CL_REQUESTS_DURATION_SECONDS, METRIC_OUTGOING_EL_REQUESTS_COUNT, METRIC_OUTGOING_EL_REQUESTS_DURATION_SECONDS, + METRIC_OUTGOING_KEYSAPI_REQUESTS_COUNT, + METRIC_OUTGOING_KEYSAPI_REQUESTS_DURATION_SECONDS, METRIC_STETH_BUFFERED_ETHER_TOTAL, METRIC_SYNC_PARTICIPATION_DISTANCE_DOWN_FROM_CHAIN_AVG, METRIC_TASK_DURATION_SECONDS, @@ -196,6 +198,19 @@ export class PrometheusService implements OnApplicationBootstrap { labelNames: ['name', 'target', 'status', 'code'] as const, }); + public outgoingKeysAPIRequestsDuration = this.getOrCreateMetric('Histogram', { + name: METRIC_OUTGOING_KEYSAPI_REQUESTS_DURATION_SECONDS, + help: 'Duration of outgoing KeysAPI requests', + buckets: [0.01, 0.1, 0.5, 1, 2, 5, 15, 30, 60], + labelNames: ['name', 'target'] as const, + }); + + public outgoingKeysAPIRequestsCount = this.getOrCreateMetric('Gauge', { + name: METRIC_OUTGOING_KEYSAPI_REQUESTS_COUNT, + help: 'Count of outgoing KeysAPI requests', + labelNames: ['name', 'target', 'status', 'code'] as const, + }); + public taskDuration = this.getOrCreateMetric('Histogram', { name: METRIC_TASK_DURATION_SECONDS, help: 'Duration of task execution', @@ -212,55 +227,55 @@ export class PrometheusService implements OnApplicationBootstrap { public operatorsIdentifies = this.getOrCreateMetric('Gauge', { name: METRIC_USER_OPERATORS_IDENTIFIES, help: 'Operators identifies', - labelNames: ['nos_id', 'nos_name'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name'], }); public validators = this.getOrCreateMetric('Gauge', { name: METRIC_VALIDATORS, help: 'Validators number', - labelNames: ['owner', 'status'], + labelNames: ['owner', 'nos_module_id', 'status'], }); public userValidators = this.getOrCreateMetric('Gauge', { name: METRIC_USER_VALIDATORS, help: 'User validators number', - labelNames: ['nos_name', 'status'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name', 'status'], }); public avgValidatorBalanceDelta = this.getOrCreateMetric('Gauge', { name: METRIC_VALIDATOR_BALANCES_DELTA, help: 'average validator balances delta (6 epochs delta)', - labelNames: ['nos_name'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name'], }); public operatorRealBalanceDelta = this.getOrCreateMetric('Gauge', { name: METRIC_OPERATOR_REAL_BALANCE_DELTA, help: 'operator real balance delta (according to state)', - labelNames: ['nos_name'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name'], }); public operatorCalculatedBalanceDelta = this.getOrCreateMetric('Gauge', { name: METRIC_OPERATOR_CALCULATED_BALANCE_DELTA, help: 'operator calculated balance delta (according to calculated rewards and penalties)', - labelNames: ['nos_name'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name'], }); public operatorCalculatedBalanceCalculationError = this.getOrCreateMetric('Gauge', { name: METRIC_OPERATOR_CALCULATED_BALANCE_CALCULATION_ERROR, help: 'operator calculated balance delta calculation error by real balance change', - labelNames: ['nos_name'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name'], }); public validatorQuantile001BalanceDelta = this.getOrCreateMetric('Gauge', { name: METRIC_VALIDATOR_QUANTILE_001_BALANCES_DELTA, help: 'validator 0.1% quantile balances delta (6 epochs delta)', - labelNames: ['nos_name'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name'], }); public validatorsCountWithNegativeBalanceDelta = this.getOrCreateMetric('Gauge', { name: METRIC_VALIDATOR_COUNT_WITH_NEGATIVE_BALANCES_DELTA, help: 'number of validators with negative balances delta', - labelNames: ['nos_name'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name'], }); public otherValidatorsCountWithGoodSyncParticipation = this.getOrCreateMetric('Gauge', { @@ -272,7 +287,7 @@ export class PrometheusService implements OnApplicationBootstrap { public validatorsCountWithGoodSyncParticipation = this.getOrCreateMetric('Gauge', { name: METRIC_VALIDATOR_COUNT_WITH_GOOD_SYNC_PARTICIPATION, help: 'number of validators with good sync committee participation', - labelNames: ['nos_name'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name'], }); public otherValidatorsCountWithSyncParticipationLessAvg = this.getOrCreateMetric('Gauge', { @@ -284,7 +299,7 @@ export class PrometheusService implements OnApplicationBootstrap { public validatorsCountWithSyncParticipationLessAvg = this.getOrCreateMetric('Gauge', { name: METRIC_VALIDATOR_COUNT_WITH_SYNC_PARTICIPATION_LESS_AVG, help: 'number of validators with sync committee participation less avg', - labelNames: ['nos_name'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name'], }); public otherValidatorsCountPerfectAttestation = this.getOrCreateMetric('Gauge', { @@ -296,7 +311,7 @@ export class PrometheusService implements OnApplicationBootstrap { public validatorsCountPerfectAttestation = this.getOrCreateMetric('Gauge', { name: METRIC_VALIDATOR_COUNT_PERFECT_ATTESTATION, help: 'number of validators with perfect attestation', - labelNames: ['nos_name'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name'], }); public otherValidatorsCountMissAttestation = this.getOrCreateMetric('Gauge', { @@ -308,7 +323,7 @@ export class PrometheusService implements OnApplicationBootstrap { public validatorsCountMissAttestation = this.getOrCreateMetric('Gauge', { name: METRIC_VALIDATOR_COUNT_MISS_ATTESTATION, help: 'number of validators miss attestation', - labelNames: ['nos_name'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name'], }); public otherValidatorsCountInvalidAttestation = this.getOrCreateMetric('Gauge', { @@ -320,55 +335,55 @@ export class PrometheusService implements OnApplicationBootstrap { public validatorsCountInvalidAttestation = this.getOrCreateMetric('Gauge', { name: METRIC_VALIDATOR_COUNT_INVALID_ATTESTATION, help: 'number of validators with invalid properties or high inc. delay in attestation', - labelNames: ['nos_name', 'reason'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name', 'reason'], }); public validatorsCountMissAttestationLastNEpoch = this.getOrCreateMetric('Gauge', { name: METRIC_VALIDATOR_COUNT_MISS_ATTESTATION_LAST_N_EPOCH, help: 'number of validators miss attestation last N epoch', - labelNames: ['nos_name', 'epoch_interval'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name', 'epoch_interval'], }); public validatorsCountInvalidAttestationLastNEpoch = this.getOrCreateMetric('Gauge', { name: METRIC_VALIDATOR_COUNT_INVALID_ATTESTATION_LAST_N_EPOCH, help: 'number of validators with invalid properties or high inc. delay in attestation last N epoch', - labelNames: ['nos_name', 'reason', 'epoch_interval'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name', 'reason', 'epoch_interval'], }); public validatorsCountHighAvgIncDelayAttestationOfNEpoch = this.getOrCreateMetric('Gauge', { name: METRIC_VALIDATOR_COUNT_HIGH_AVG_INC_DELAY_ATTESTATION_OF_N_EPOCH, help: 'number of validators with high avg inc. delay of N epochs', - labelNames: ['nos_name', 'epoch_interval'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name', 'epoch_interval'], }); public validatorsCountHighIncDelayAttestationLastNEpoch = this.getOrCreateMetric('Gauge', { name: METRIC_VALIDATOR_COUNT_HIGH_INC_DELAY_ATTESTATION_LAST_N_EPOCH, help: 'number of validators with high inc. delay last N epochs', - labelNames: ['nos_name', 'epoch_interval'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name', 'epoch_interval'], }); public validatorsCountInvalidAttestationPropertyLastNEpoch = this.getOrCreateMetric('Gauge', { name: METRIC_VALIDATOR_COUNT_INVALID_ATTESTATION_PROPERTY_LAST_N_EPOCH, help: 'number of validators with two invalid attestation property (head, target, source) last N epochs', - labelNames: ['nos_name', 'epoch_interval'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name', 'epoch_interval'], }); public highRewardValidatorsCountMissAttestationLastNEpoch = this.getOrCreateMetric('Gauge', { name: METRIC_HIGH_REWARD_VALIDATOR_COUNT_MISS_ATTESTATION_LAST_N_EPOCH, help: 'number of validators miss attestation last N epoch (with possible high reward in the future)', - labelNames: ['nos_name', 'epoch_interval'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name', 'epoch_interval'], }); public validatorsCountWithSyncParticipationLessAvgLastNEpoch = this.getOrCreateMetric('Gauge', { name: METRIC_VALIDATOR_COUNT_WITH_SYNC_PARTICIPATION_LESS_AVG_LAST_N_EPOCH, help: 'number of validators with sync participation less than avg last N epoch', - labelNames: ['nos_name', 'epoch_interval'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name', 'epoch_interval'], }); public highRewardValidatorsCountWithSyncParticipationLessAvgLastNEpoch = this.getOrCreateMetric('Gauge', { name: METRIC_HIGH_REWARD_VALIDATOR_COUNT_WITH_SYNC_PARTICIPATION_LESS_AVG_LAST_N_EPOCH, help: 'number of validators with sync participation less than avg last N epoch (with possible high reward in the future)', - labelNames: ['nos_name', 'epoch_interval'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name', 'epoch_interval'], }); public otherValidatorsCountGoodPropose = this.getOrCreateMetric('Gauge', { @@ -380,7 +395,7 @@ export class PrometheusService implements OnApplicationBootstrap { public validatorsCountGoodPropose = this.getOrCreateMetric('Gauge', { name: METRIC_VALIDATOR_COUNT_GOOD_PROPOSE, help: 'number of validators good propose', - labelNames: ['nos_name'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name'], }); public otherValidatorsCountMissPropose = this.getOrCreateMetric('Gauge', { @@ -392,25 +407,25 @@ export class PrometheusService implements OnApplicationBootstrap { public validatorsCountMissPropose = this.getOrCreateMetric('Gauge', { name: METRIC_VALIDATOR_COUNT_MISS_PROPOSE, help: 'number of validators miss propose', - labelNames: ['nos_name'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name'], }); public highRewardValidatorsCountMissPropose = this.getOrCreateMetric('Gauge', { name: METRIC_HIGH_REWARD_VALIDATOR_COUNT_MISS_PROPOSE, help: 'number of validators miss propose (with possible high reward in the future)', - labelNames: ['nos_name'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name'], }); public userSyncParticipationAvgPercent = this.getOrCreateMetric('Gauge', { name: METRIC_USER_SYNC_PARTICIPATION_AVG_PERCENT, help: 'User sync committee validators participation avg percent', - labelNames: [], + labelNames: ['nos_module_id'], }); public operatorSyncParticipationAvgPercent = this.getOrCreateMetric('Gauge', { name: METRIC_OPERATOR_SYNC_PARTICIPATION_AVG_PERCENT, help: 'Operator sync committee validators participation avg percent', - labelNames: ['nos_name'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name'], }); public otherSyncParticipationAvgPercent = this.getOrCreateMetric('Gauge', { @@ -440,7 +455,7 @@ export class PrometheusService implements OnApplicationBootstrap { public operatorBalance24hDifference = this.getOrCreateMetric('Gauge', { name: METRIC_OPERATOR_BALANCE_24H_DIFFERENCE, help: 'Operator balance difference (24 hours)', - labelNames: ['nos_name'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name'], }); public avgChainReward = this.getOrCreateMetric('Gauge', { @@ -452,7 +467,7 @@ export class PrometheusService implements OnApplicationBootstrap { public operatorReward = this.getOrCreateMetric('Gauge', { name: METRIC_OPERATOR_REWARD, help: 'rewards for each duty for each operator', - labelNames: ['nos_name', 'duty'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name', 'duty'], }); public avgChainMissedReward = this.getOrCreateMetric('Gauge', { @@ -464,7 +479,7 @@ export class PrometheusService implements OnApplicationBootstrap { public operatorMissedReward = this.getOrCreateMetric('Gauge', { name: METRIC_OPERATOR_MISSED_REWARD, help: 'missed rewards for each duty for each operator', - labelNames: ['nos_name', 'duty'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name', 'duty'], }); public avgChainPenalty = this.getOrCreateMetric('Gauge', { @@ -476,13 +491,13 @@ export class PrometheusService implements OnApplicationBootstrap { public operatorPenalty = this.getOrCreateMetric('Gauge', { name: METRIC_OPERATOR_PENALTY, help: 'operator penalty for each duty', - labelNames: ['nos_name', 'duty'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name', 'duty'], }); public operatorWithdrawalsSum = this.getOrCreateMetric('Gauge', { name: METRIC_OPERATOR_WITHDRAWALS_SUM, help: 'operator withdrawals sum', - labelNames: ['nos_name', 'type'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name', 'type'], }); public otherChainWithdrawalsSum = this.getOrCreateMetric('Gauge', { @@ -494,7 +509,7 @@ export class PrometheusService implements OnApplicationBootstrap { public operatorWithdrawalsCount = this.getOrCreateMetric('Gauge', { name: METRIC_OPERATOR_WITHDRAWALS_COUNT, help: 'operator withdrawals count', - labelNames: ['nos_name', 'type'], + labelNames: ['nos_module_id', 'nos_id', 'nos_name', 'type'], }); public otherChainWithdrawalsCount = this.getOrCreateMetric('Gauge', { @@ -524,8 +539,13 @@ export const setUserOperatorsMetric = ( value: (dataItem: any) => number = (dataItem) => dataItem.amount, ) => { operators.forEach((operator) => { - const _labels = typeof labels == 'function' ? labels(operator) : { nos_name: operator.name, ...labels }; - const operatorResult = data.find((p) => p.val_nos_id != null && +p.val_nos_id == operator.index); + const _labels = + typeof labels == 'function' + ? labels(operator) + : { nos_module_id: operator.module, nos_id: operator.index, nos_name: operator.name, ...labels }; + const operatorResult = data.find( + (p) => p.val_nos_id != null && +p.val_nos_module_id == operator.module && +p.val_nos_id == operator.index, + ); if (operatorResult) metric.set(_labels, value(operatorResult)); else metric.set(_labels, 0); }); @@ -576,6 +596,40 @@ export function TrackCLRequest(target: any, propertyKey: string, descriptor: Pro }; } +export function TrackKeysAPIRequest(target: any, propertyKey: string, descriptor: PropertyDescriptor) { + const originalValue = descriptor.value; + descriptor.value = function (...args) { + if (!this.prometheus) throw Error(`'${this.constructor.name}' class object must contain 'prometheus' property`); + const [apiUrl, subUrl] = args; + const [targetName, reqName] = requestLabels(apiUrl, subUrl); + const stop = this.prometheus.outgoingKeysAPIRequestsDuration.startTimer({ + name: reqName, + target: targetName, + }); + return originalValue + .apply(this, args) + .then((r: any) => { + this.prometheus.outgoingKeysAPIRequestsCount.inc({ + name: reqName, + target: targetName, + status: RequestStatus.COMPLETE, + code: 200, + }); + return r; + }) + .catch((e: any) => { + this.prometheus.outgoingKeysAPIRequestsCount.inc({ + name: reqName, + target: targetName, + status: RequestStatus.ERROR, + code: e.$httpCode, + }); + throw e; + }) + .finally(() => stop()); + }; +} + export function TrackTask(name: string) { return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => { const originalValue = descriptor.value; diff --git a/src/common/validators-registry/file-source/file-source.service.ts b/src/common/validators-registry/file-source/file-source.service.ts index bc4323c1..1fbe79d0 100644 --- a/src/common/validators-registry/file-source/file-source.service.ts +++ b/src/common/validators-registry/file-source/file-source.service.ts @@ -30,7 +30,7 @@ export class FileSourceService implements RegistrySource { protected data: FileContent; protected lastSuccessDataReadTimestamp: number; - protected operatorsMap = new Map(); + protected operatorsMap = new Map(); protected keysMap = new Map(); @@ -39,9 +39,9 @@ export class FileSourceService implements RegistrySource { const data = load(fileContent); if (!isValid(data)) throw new Error('Error when parsing validators registry file source'); this.logger.log( - `Successful reading validators registry file source. Keys count per operator - ${data.operators - .map((o) => `${o.name}: [${o.keys.length}]`) - .join(', ')}`, + `Successful reading validators registry file source.\n${Object.values(data) + .map((m, index) => `Module: ${index} | ` + m.map((o) => `${o.name}: [${o.keys.length}]`)) + .join('\n')}`, ); this.lastSuccessDataReadTimestamp = Date.now(); this.data = data; @@ -62,17 +62,24 @@ export class FileSourceService implements RegistrySource { } protected updateOperatorsMap() { - this.operatorsMap = new Map(this.data?.operators?.map((o, index) => [index, { index, name: o.name }])); + this.operatorsMap = new Map(); + Object.values(this.data).forEach((m, moduleIndex) => { + m.forEach((o, operatorIndex) => { + this.operatorsMap.set(`${moduleIndex + 1}_${operatorIndex}`, { index: operatorIndex, module: moduleIndex + 1, name: o.name }); + }); + }); } protected updateKeysMap() { this.keysMap = new Map(); - this.data?.operators?.forEach((o, operatorIndex) => { - if (o.keys) { - o.keys.forEach((key, index) => { - this.keysMap.set(key, { index, operatorIndex, key }); - }); - } + Object.values(this.data).forEach((m, moduleIndex) => { + m.forEach((o, operatorIndex) => { + if (o.keys) { + o.keys.forEach((key) => { + this.keysMap.set(key, { moduleIndex: moduleIndex + 1, operatorIndex, key }); + }); + } + }); }); } } diff --git a/src/common/validators-registry/keysapi-source/index.ts b/src/common/validators-registry/keysapi-source/index.ts new file mode 100644 index 00000000..9eda9171 --- /dev/null +++ b/src/common/validators-registry/keysapi-source/index.ts @@ -0,0 +1,2 @@ +export * from './keysapi-source.service'; +export * from './keysapi-source.module'; diff --git a/src/common/validators-registry/keysapi-source/keysapi-source.client.ts b/src/common/validators-registry/keysapi-source/keysapi-source.client.ts new file mode 100644 index 00000000..5f43d7e7 --- /dev/null +++ b/src/common/validators-registry/keysapi-source/keysapi-source.client.ts @@ -0,0 +1,182 @@ +import { LOGGER_PROVIDER } from '@lido-nestjs/logger'; +import { Inject, Injectable, LoggerService } from '@nestjs/common'; +import { HTTPError, Response, got } from 'got-cjs'; + +import { ConfigService } from 'common/config'; +import { ResponseError, errCommon, errRequest } from 'common/eth-providers'; +import { rejectDelay } from 'common/functions/rejectDelay'; +import { retrier } from 'common/functions/retrier'; +import { urljoin } from 'common/functions/urljoin'; +import { PrometheusService, TrackKeysAPIRequest } from 'common/prometheus'; + +interface RequestRetryOptions { + maxRetries?: number; + dataOnly?: boolean; + useFallbackOnRejected?: (last_error: any, current_error: any) => boolean; + useFallbackOnResolved?: (r: any) => boolean; +} + +interface StatusResponse { + appVersion: string; + chainId: number; + elBlockSnapshot: any; +} + +interface ModulesResponse { + nonce: number; + type: string; + id: 1; + stakingModuleAddress: string; + name: string; +} + +const REQUEST_TIMEOUT_POLICY_MS = { + // Starts when a socket is assigned. + // Ends when the hostname has been resolved. + lookup: undefined, + // Starts when lookup completes. + // Ends when the socket is fully connected. + // If lookup does not apply to the request, this event starts when the socket is assigned and ends when the socket is connected. + connect: 1000, + // Starts when connect completes. + // Ends when the handshake process completes. + secureConnect: undefined, + // Starts when the socket is connected. + // Resets when new data is transferred. + socket: undefined, + // Starts when the socket is connected. + // Ends when all data have been written to the socket. + send: undefined, + // Starts when request has been flushed. + // Ends when the headers are received. + // Will be redefined by `CL_API_GET_RESPONSE_TIMEOUT` + response: 1000, +}; + +@Injectable() +export class KeysapiSourceClient { + protected apiUrls: string[]; + + protected endpoints = { + usedKeys: 'v1/keys?used=true', + operators: 'v1/operators', + modules: 'v1/modules', + status: 'v1/status', + }; + + public constructor( + @Inject(LOGGER_PROVIDER) protected readonly logger: LoggerService, + protected readonly config: ConfigService, + protected readonly prometheus: PrometheusService, + ) { + this.apiUrls = this.config.get('VALIDATOR_REGISTRY_KEYSAPI_SOURCE_URLS'); + } + + public async getStatus(): Promise { + return await this.retryRequest(async (apiURL: string) => this.apiGet(apiURL, this.endpoints.status), { + dataOnly: false, + }); + } + + public async getModules(): Promise { + return await this.retryRequest(async (apiURL: string) => this.apiGet(apiURL, this.endpoints.modules)); + } + + public async getOperators(): Promise { + return await this.retryRequest(async (apiURL: string) => await this.apiGetStream(apiURL, this.endpoints.operators), { + dataOnly: false, + }); + } + + public async getUsedKeys(): Promise { + return await this.retryRequest(async (apiURL: string) => await this.apiGetStream(apiURL, this.endpoints.usedKeys), { + dataOnly: false, + }); + } + + protected async retryRequest(callback: (apiURL: string) => Promise, options?: RequestRetryOptions): Promise { + options = { + maxRetries: options?.maxRetries ?? this.config.get('VALIDATOR_REGISTRY_KEYSAPI_SOURCE_MAX_RETRIES'), + dataOnly: options?.dataOnly ?? true, + useFallbackOnRejected: options?.useFallbackOnRejected ?? (() => true), // use fallback on error as default + useFallbackOnResolved: options?.useFallbackOnResolved ?? (() => false), // do NOT use fallback on success as default + }; + const retry = retrier(this.logger, options.maxRetries, 100, 10000, true); + let res; + let err; + for (let i = 0; i < this.apiUrls.length; i++) { + if (res) break; + res = await callback(this.apiUrls[i]) + .catch(rejectDelay(this.config.get('VALIDATOR_REGISTRY_KEYSAPI_SOURCE_RETRY_DELAY_MS'))) + .catch(() => retry(() => callback(this.apiUrls[i]))) + .then((r: any) => { + if (options.useFallbackOnResolved(r)) { + err = Error('Unresolved data on a successful KeysAPI response'); + return undefined; + } + return r; + }) + .catch((current_error: any) => { + if (options.useFallbackOnRejected(err, current_error)) { + err = current_error; + return undefined; + } + throw current_error; + }); + if (i == this.apiUrls.length - 1 && !res) { + err.message = `Error while doing KeysAPI request on all passed URLs. ${err.message}`; + throw err; + } + if (!res) { + this.logger.warn(`${err.message}. Error while doing KeysAPI request. Will try to switch to another API URL`); + } + } + + if (options.dataOnly) return res.data; + else return res; + } + + @TrackKeysAPIRequest + protected async apiGet(apiURL: string, subUrl: string): Promise { + const res = await got + .get(urljoin(apiURL, subUrl), { + timeout: { ...REQUEST_TIMEOUT_POLICY_MS, response: this.config.get('VALIDATOR_REGISTRY_KEYSAPI_SOURCE_RESPONSE_TIMEOUT') }, + }) + .catch((e) => { + if (e.response) { + throw new ResponseError(errRequest(e.response.body, subUrl, apiURL), e.response.statusCode); + } + throw new ResponseError(errCommon(e.message, subUrl, apiURL)); + }); + if (res.statusCode !== 200) { + throw new ResponseError(errRequest(res.body, subUrl, apiURL), res.statusCode); + } + try { + return JSON.parse(res.body); + } catch (e) { + throw new ResponseError(`Error converting response body to JSON. Body: ${res.body}`); + } + } + + @TrackKeysAPIRequest + protected async apiGetStream(apiURL: string, subUrl: string): Promise { + const readStream = got.stream.get(urljoin(apiURL, subUrl), { + timeout: { ...REQUEST_TIMEOUT_POLICY_MS, response: this.config.get('VALIDATOR_REGISTRY_KEYSAPI_SOURCE_RESPONSE_TIMEOUT') }, + }); + + return new Promise((resolve, reject) => { + readStream.on('response', (r: Response) => { + if (r.statusCode != 200) reject(new HTTPError(r)); + resolve(readStream); + }); + readStream.on('error', (e) => reject(e)); + }) + .then((r: Request) => r) + .catch((e) => { + if (e instanceof HTTPError) { + throw new ResponseError(errRequest(e.response.body, subUrl, apiURL), e.response.statusCode); + } + throw new ResponseError(errCommon(e.message, subUrl, apiURL)); + }); + } +} diff --git a/src/common/validators-registry/keysapi-source/keysapi-source.module.ts b/src/common/validators-registry/keysapi-source/keysapi-source.module.ts new file mode 100644 index 00000000..c3683367 --- /dev/null +++ b/src/common/validators-registry/keysapi-source/keysapi-source.module.ts @@ -0,0 +1,21 @@ +import { ValidatorRegistryModule } from '@lido-nestjs/registry'; +import { Module } from '@nestjs/common'; + +import { ExecutionProvider } from 'common/eth-providers'; + +import { KeysapiSourceClient } from './keysapi-source.client'; +import { KeysapiSourceService } from './keysapi-source.service'; + +@Module({ + imports: [ + ValidatorRegistryModule.forFeatureAsync({ + async useFactory(provider: ExecutionProvider) { + return { provider }; + }, + inject: [ExecutionProvider], + }), + ], + providers: [KeysapiSourceService, KeysapiSourceClient], + exports: [KeysapiSourceService], +}) +export class KeysapiSourceModule {} diff --git a/src/common/validators-registry/keysapi-source/keysapi-source.service.ts b/src/common/validators-registry/keysapi-source/keysapi-source.service.ts new file mode 100644 index 00000000..0eeab04f --- /dev/null +++ b/src/common/validators-registry/keysapi-source/keysapi-source.service.ts @@ -0,0 +1,107 @@ +import { Injectable } from '@nestjs/common'; +import { chain } from 'stream-chain'; +import { parser } from 'stream-json'; +import { pick } from 'stream-json/filters/Pick'; +import { streamArray } from 'stream-json/streamers/StreamArray'; +import { batch } from 'stream-json/utils/Batch'; + +import { unblock } from '../../functions/unblock'; +import { RegistrySource, RegistrySourceKey, RegistrySourceOperator } from '../registry-source.interface'; +import { KeysapiSourceClient } from './keysapi-source.client'; + +@Injectable() +export class KeysapiSourceService implements RegistrySource { + constructor(protected readonly client: KeysapiSourceClient) {} + + protected modules = new Map(); + protected operatorsMap = new Map(); + protected keysMap = new Map(); + protected keysOpIndex = 0; + + protected status = undefined; + + public async update() { + const nonce = await this.getModulesNonceSum(); + if (this.keysOpIndex == 0 || nonce > this.keysOpIndex) { + await this.updateOperatorsMap(); + await this.updateKeysMap(); + this.keysOpIndex = nonce; + } + } + + public getOperatorsMap(): Map { + return this.operatorsMap; + } + + public getOperatorKey(pubKey: string) { + return this.keysMap.get(pubKey); + } + + public async sourceTimestamp() { + const status = await this.client.getStatus(); + return status.elBlockSnapshot.timestamp; + } + + protected async getModulesNonceSum() { + const modules = await this.client.getModules(); + return modules.reduce((acc, m) => acc + m.nonce, 0); + } + + protected async updateOperatorsMap() { + const operators = await this.client.getOperators(); + const pipeline = chain([ + operators, + parser(), + pick({ filter: 'data' }), + streamArray(), + batch({ batchSize: 100 }), + async (batch) => { + await unblock(); + for (const data of batch) { + for (const operator of data.value.operators) { + this.operatorsMap.set(`${data.value.module.id}_${operator.index}`, { + index: operator.index, + module: data.value.module.id, + name: operator.name, + }); + if (!this.modules.has(data.value.module.stakingModuleAddress)) { + this.modules.set(data.value.module.stakingModuleAddress, +data.value.module.id); + } + } + } + }, + ]); + pipeline.on('data', (data) => data); + await new Promise((resolve, reject) => { + pipeline.on('error', (error) => reject(error)); + pipeline.on('end', () => resolve(true)); + }).finally(() => pipeline.destroy()); + } + + protected async updateKeysMap() { + this.keysMap = new Map(); + const keys = await this.client.getUsedKeys(); + const pipeline = chain([ + keys, + parser(), + pick({ filter: 'data' }), + streamArray(), + batch({ batchSize: 100 }), + async (batch) => { + await unblock(); + for (const data of batch) { + this.keysMap.set(data.value.key, { + key: data.value.key, + operatorIndex: +data.value.operatorIndex, + moduleIndex: this.modules.get(data.value.moduleAddress), + }); + } + }, + ]); + pipeline.on('data', (data) => data); + await new Promise((resolve, reject) => { + pipeline.on('error', (error) => reject(error)); + pipeline.on('end', () => resolve(true)); + }).finally(() => pipeline.destroy()); + } +} diff --git a/src/common/validators-registry/lido-source/lido-source.service.ts b/src/common/validators-registry/lido-source/lido-source.service.ts index 359e9d05..a1b6c896 100644 --- a/src/common/validators-registry/lido-source/lido-source.service.ts +++ b/src/common/validators-registry/lido-source/lido-source.service.ts @@ -21,15 +21,16 @@ export class LidoSourceService implements RegistrySource { protected readonly operatorStorageService: RegistryOperatorStorageService, ) {} - protected operatorsMap = new Map(); + protected registryModuleId = 1; // stub for now + protected operatorsMap = new Map(); protected keysMap = new Map(); protected keysOpIndex = 0; public async update() { await this.validatorService.update('latest'); - await this.updateOperatorsMap(); const storageKeysOpIndex = (await this.metaStorageService.get())?.keysOpIndex; if (this.keysOpIndex == 0 || storageKeysOpIndex > this.keysOpIndex) { + await this.updateOperatorsMap(); await this.updateKeysMap(); this.keysOpIndex = storageKeysOpIndex; } @@ -50,15 +51,15 @@ export class LidoSourceService implements RegistrySource { protected async updateOperatorsMap() { const operators = await this.operatorStorageService.findAll(); - this.operatorsMap = new Map(operators.map((o) => [o.index, o])); + this.operatorsMap = new Map(operators.map((o) => [`${this.registryModuleId}_${o.index}`, { ...o, module: this.registryModuleId }])); } protected async updateKeysMap() { this.keysMap = new Map(); - for (const index of this.operatorsMap.keys()) { - const operatorKeys = await this.keyStorageService.findByOperatorIndex(index); + for (const operator of this.operatorsMap.values()) { + const operatorKeys = await this.keyStorageService.findByOperatorIndex(operator.index); for (const key of operatorKeys) { - if (key.used) this.keysMap.set(key.key, key); + if (key.used) this.keysMap.set(key.key, { key: key.key, operatorIndex: key.operatorIndex, moduleIndex: this.registryModuleId }); } await unblock(); } diff --git a/src/common/validators-registry/registry-source.interface.ts b/src/common/validators-registry/registry-source.interface.ts index 3d8e884f..f0e9b5e8 100644 --- a/src/common/validators-registry/registry-source.interface.ts +++ b/src/common/validators-registry/registry-source.interface.ts @@ -1,7 +1,7 @@ export const REGISTRY_SOURCE = 'validatorsRegistrySourceToken'; export interface RegistrySourceKey { - index: number; + moduleIndex: number; operatorIndex: number; key: string; } @@ -12,12 +12,13 @@ export interface RegistrySourceKeyWithOperatorName extends RegistrySourceKey { export interface RegistrySourceOperator { index: number; + module: number; name: string; } export interface RegistrySource { update(...args): Promise; - getOperatorsMap(): Map; + getOperatorsMap(): Map; getOperatorKey(pubKey: string): RegistrySourceKey | null; sourceTimestamp(): Promise; } diff --git a/src/common/validators-registry/registry.module.ts b/src/common/validators-registry/registry.module.ts index 23e68b55..acd1b8c0 100644 --- a/src/common/validators-registry/registry.module.ts +++ b/src/common/validators-registry/registry.module.ts @@ -1,27 +1,38 @@ -import { Module } from '@nestjs/common'; +import { LOGGER_PROVIDER } from '@lido-nestjs/logger'; +import { LoggerService, Module } from '@nestjs/common'; import { ConfigService, ValidatorRegistrySource } from 'common/config'; import { FileSourceModule, FileSourceService } from './file-source'; +import { KeysapiSourceModule, KeysapiSourceService } from './keysapi-source'; import { LidoSourceModule, LidoSourceService } from './lido-source'; import { REGISTRY_SOURCE, RegistrySource } from './registry-source.interface'; import { RegistryService } from './registry.service'; @Module({ - imports: [LidoSourceModule, FileSourceModule], + imports: [LidoSourceModule, FileSourceModule, KeysapiSourceModule], providers: [ RegistryService, { provide: REGISTRY_SOURCE, - useFactory: async (config: ConfigService, lido: LidoSourceService, file: FileSourceService): Promise => { + useFactory: async ( + logger: LoggerService, + config: ConfigService, + lido: LidoSourceService, + file: FileSourceService, + keysapi: KeysapiSourceService, + ): Promise => { switch (config.get('VALIDATOR_REGISTRY_SOURCE')) { case ValidatorRegistrySource.Lido: + logger.warn('DEPRECATED: VALIDATOR_REGISTRY_SOURCE=lido. Use VALIDATOR_REGISTRY_SOURCE=keysapi instead'); return lido; case ValidatorRegistrySource.File: return file; + case ValidatorRegistrySource.KeysAPI: + return keysapi; } }, - inject: [ConfigService, LidoSourceService, FileSourceService], + inject: [LOGGER_PROVIDER, ConfigService, LidoSourceService, FileSourceService, KeysapiSourceService], }, ], exports: [RegistryService], diff --git a/src/common/validators-registry/registry.service.ts b/src/common/validators-registry/registry.service.ts index d53a721e..7e6382d3 100644 --- a/src/common/validators-registry/registry.service.ts +++ b/src/common/validators-registry/registry.service.ts @@ -45,7 +45,7 @@ export class RegistryService { public getOperatorKey(pubKey: string): RegistrySourceKeyWithOperatorName { const key = this.source.getOperatorKey(pubKey); if (!key) return null; - const operator = this.source.getOperatorsMap().get(key.operatorIndex); + const operator = this.source.getOperatorsMap().get(`${key.moduleIndex}_${key.operatorIndex}`); return { ...key, operatorName: operator.name }; } diff --git a/src/duty/state/state.metrics.ts b/src/duty/state/state.metrics.ts index 0f50e1a9..ff7d9d04 100644 --- a/src/duty/state/state.metrics.ts +++ b/src/duty/state/state.metrics.ts @@ -29,7 +29,7 @@ export class StateMetrics { public async calculate(epoch: Epoch) { this.logger.log('Calculating state metrics'); this.processedEpoch = epoch; - this.operators = await this.registryService.getOperators(); + this.operators = this.registryService.getOperators(); await allSettled([ this.operatorsIdentifies(), this.nosStats(), @@ -49,7 +49,7 @@ export class StateMetrics { this.prometheus.operatorsIdentifies, this.operators.map((operator) => ({ val_nos_id: operator.index, amount: 1 })), this.operators, - (o) => ({ nos_id: o.index, nos_name: o.name }), + (o) => ({ nos_module_id: o.module, nos_id: o.index, nos_name: o.name }), ); } @@ -114,48 +114,56 @@ export class StateMetrics { private async userValidatorsStats() { const result = await this.storage.getUserValidatorsSummaryStats(this.processedEpoch); this.logger.debug(`User stats: ${JSON.stringify(result)}`); - this.prometheus.validators.set( - { - owner: Owner.USER, - status: PrometheusValStatus.Slashed, - }, - result.slashed, - ); - this.prometheus.validators.set( - { - owner: Owner.USER, - status: PrometheusValStatus.Ongoing, - }, - result.active_ongoing, - ); - this.prometheus.validators.set( - { - owner: Owner.USER, - status: PrometheusValStatus.Pending, - }, - result.pending, - ); - this.prometheus.validators.set( - { - owner: Owner.USER, - status: PrometheusValStatus.WithdrawalPending, - }, - result.withdraw_pending, - ); - this.prometheus.validators.set( - { - owner: Owner.USER, - status: PrometheusValStatus.WithdrawalDone, - }, - result.withdrawn, - ); - this.prometheus.validators.set( - { - owner: Owner.USER, - status: PrometheusValStatus.Stuck, - }, - result.stuck, - ); + result.map((r) => { + this.prometheus.validators.set( + { + owner: Owner.USER, + nos_module_id: r.val_nos_module_id, + status: PrometheusValStatus.Slashed, + }, + r.slashed, + ); + this.prometheus.validators.set( + { + owner: Owner.USER, + nos_module_id: r.val_nos_module_id, + status: PrometheusValStatus.Ongoing, + }, + r.active_ongoing, + ); + this.prometheus.validators.set( + { + owner: Owner.USER, + nos_module_id: r.val_nos_module_id, + status: PrometheusValStatus.Pending, + }, + r.pending, + ); + this.prometheus.validators.set( + { + owner: Owner.USER, + nos_module_id: r.val_nos_module_id, + status: PrometheusValStatus.WithdrawalPending, + }, + r.withdraw_pending, + ); + this.prometheus.validators.set( + { + owner: Owner.USER, + nos_module_id: r.val_nos_module_id, + status: PrometheusValStatus.WithdrawalDone, + }, + r.withdrawn, + ); + this.prometheus.validators.set( + { + owner: Owner.USER, + nos_module_id: r.val_nos_module_id, + status: PrometheusValStatus.Stuck, + }, + r.stuck, + ); + }); } private async otherValidatorsStats() { @@ -215,7 +223,7 @@ export class StateMetrics { private async totalBalance24hDifference() { const result = await this.storage.getTotalBalance24hDifference(this.processedEpoch); - if (result) this.prometheus.totalBalance24hDifference.set(result); + result.forEach((r) => this.prometheus.totalBalance24hDifference.set({ nos_module_id: r.val_nos_module_id }, r.amount)); } private async operatorBalance24hDifference() { @@ -227,11 +235,11 @@ export class StateMetrics { if (!(this.registryService.source instanceof LidoSourceService)) return; this.prometheus.contractKeysTotal.set( { type: 'total' }, - this.operators.reduce((sum, o: RegistryOperator) => sum + o.totalSigningKeys, 0), + (this.operators as any as RegistryOperator[]).reduce((sum, o: RegistryOperator) => sum + o.totalSigningKeys, 0), ); this.prometheus.contractKeysTotal.set( { type: 'used' }, - this.operators.reduce((sum, o: RegistryOperator) => sum + o.usedSigningKeys, 0), + (this.operators as any as RegistryOperator[]).reduce((sum, o: RegistryOperator) => sum + o.usedSigningKeys, 0), ); const bufferedEther = (await this.registryService.source.contract.getBufferedEther()).div(GWEI_WEI_RATIO).div(ETH_GWEI_RATIO); this.prometheus.bufferedEther.set(bufferedEther.toNumber()); diff --git a/src/duty/state/state.service.ts b/src/duty/state/state.service.ts index d03077f1..b422e851 100644 --- a/src/duty/state/state.service.ts +++ b/src/duty/state/state.service.ts @@ -33,9 +33,11 @@ export class StateService { @TrackTask('check-state-duties') public async check(epoch: Epoch, stateSlot: Slot): Promise { const slotTime = await this.clClient.getSlotTime(epoch * this.config.get('FETCH_INTERVAL_SLOTS')); - await this.registry.updateKeysRegistry(Number(slotTime)); this.logger.log('Getting all validators state'); - const readStream = await this.clClient.getValidatorsState(stateSlot); + const [readStream, _] = await Promise.all([ + this.clClient.getValidatorsState(stateSlot), + this.registry.updateKeysRegistry(Number(slotTime)), + ]); this.logger.log('Processing all validators state'); let activeValidatorsCount = 0; let activeValidatorsEffectiveBalance = 0n; @@ -56,6 +58,7 @@ export class StateService { epoch, val_id: index, val_pubkey: state.validator.pubkey, + val_nos_module_id: operator?.moduleIndex, val_nos_id: operator?.operatorIndex, val_nos_name: operator?.operatorName, val_slashed: state.validator.slashed, diff --git a/src/duty/summary/summary.metrics.ts b/src/duty/summary/summary.metrics.ts index 4cfc7474..83fbf4c4 100644 --- a/src/duty/summary/summary.metrics.ts +++ b/src/duty/summary/summary.metrics.ts @@ -32,7 +32,7 @@ export class SummaryMetrics { public async calculate(epoch: Epoch) { this.logger.log('Calculating propose metrics'); this.processedEpoch = epoch; - this.operators = await this.registryService.getOperators(); + this.operators = this.registryService.getOperators(); await allSettled([this.userRewards(), this.avgChainRewards(), this.common()]); } diff --git a/src/duty/summary/summary.service.ts b/src/duty/summary/summary.service.ts index 75720701..b1e0fa51 100644 --- a/src/duty/summary/summary.service.ts +++ b/src/duty/summary/summary.service.ts @@ -13,6 +13,7 @@ export interface ValidatorDutySummary { /// val_id: number; val_pubkey?: string; + val_nos_module_id?: number; val_nos_id?: number; val_nos_name?: string; val_slashed?: boolean; diff --git a/src/duty/sync/sync.metrics.ts b/src/duty/sync/sync.metrics.ts index 320d1687..1def0d2b 100644 --- a/src/duty/sync/sync.metrics.ts +++ b/src/duty/sync/sync.metrics.ts @@ -27,7 +27,7 @@ export class SyncMetrics { public async calculate(epoch: Epoch, possibleHighRewardValidators: string[]) { this.logger.log('Calculating sync committee metrics'); this.processedEpoch = epoch; - this.operators = await this.registryService.getOperators(); + this.operators = this.registryService.getOperators(); await allSettled([ this.userAvgSyncPercent(), @@ -39,7 +39,8 @@ export class SyncMetrics { private async userAvgSyncPercent() { const result = await this.storage.getUserSyncParticipationAvgPercent(this.processedEpoch); - if (result) this.prometheus.userSyncParticipationAvgPercent.set(result.amount); + if (result) + result.forEach((r) => this.prometheus.userSyncParticipationAvgPercent.set({ nos_module_id: r.val_nos_module_id }, r.amount)); else this.prometheus.userSyncParticipationAvgPercent.remove(); } diff --git a/src/storage/clickhouse/clickhouse.constants.ts b/src/storage/clickhouse/clickhouse.constants.ts index 86be9418..f8ae8422 100644 --- a/src/storage/clickhouse/clickhouse.constants.ts +++ b/src/storage/clickhouse/clickhouse.constants.ts @@ -3,10 +3,11 @@ import { Epoch } from 'common/eth-providers/consensus-provider/types'; export const avgValidatorBalanceDelta = (epoch: Epoch): string => ` SELECT + current.val_nos_module_id as val_nos_module_id, current.val_nos_id as val_nos_id, avg(current.val_balance - previous.val_balance + ifNull(withdrawals.withdrawn, 0)) AS amount FROM ( - SELECT val_balance, val_id, val_nos_id + SELECT val_balance, val_id, val_nos_module_id, val_nos_id FROM validators_summary WHERE val_status != '${ValStatus.PendingQueued}' AND @@ -29,9 +30,9 @@ export const avgValidatorBalanceDelta = (epoch: Epoch): string => ` previous.val_id = current.val_id LEFT JOIN ( SELECT - sum(val_balance_withdrawn) as withdrawn, val_id, val_nos_id + sum(val_balance_withdrawn) as withdrawn, val_id, val_nos_id, val_nos_module_id FROM ( - SELECT val_balance_withdrawn, val_id, val_nos_id + SELECT val_balance_withdrawn, val_id, val_nos_module_id, val_nos_id FROM validators_summary WHERE val_nos_id IS NOT NULL AND @@ -40,19 +41,20 @@ export const avgValidatorBalanceDelta = (epoch: Epoch): string => ` epoch > (${epoch} - 6) AND epoch <= ${epoch} LIMIT 1 BY epoch, val_id ) - GROUP BY val_id, val_nos_id + GROUP BY val_id, val_nos_id, val_nos_module_id ) AS withdrawals ON withdrawals.val_id = current.val_id - GROUP BY current.val_nos_id + GROUP BY current.val_nos_module_id, current.val_nos_id `; export const validatorQuantile0001BalanceDeltasQuery = (epoch: Epoch): string => ` SELECT + current.val_nos_module_id as val_nos_module_id, current.val_nos_id as val_nos_id, quantileExact(0.001)(current.val_balance - previous.val_balance + ifNull(withdrawals.withdrawn, 0)) AS amount FROM ( - SELECT val_balance, val_id, val_nos_id + SELECT val_balance, val_id, val_nos_id, val_nos_module_id FROM validators_summary WHERE val_status != '${ValStatus.PendingQueued}' AND @@ -75,9 +77,9 @@ export const validatorQuantile0001BalanceDeltasQuery = (epoch: Epoch): string => previous.val_id = current.val_id LEFT JOIN ( SELECT - sum(val_balance_withdrawn) as withdrawn, val_id, val_nos_id + sum(val_balance_withdrawn) as withdrawn, val_id, val_nos_module_id, val_nos_id FROM ( - SELECT val_balance_withdrawn, val_id, val_nos_id + SELECT val_balance_withdrawn, val_id, val_nos_module_id, val_nos_id FROM validators_summary WHERE val_nos_id IS NOT NULL AND @@ -86,19 +88,20 @@ export const validatorQuantile0001BalanceDeltasQuery = (epoch: Epoch): string => epoch > (${epoch} - 6) AND epoch <= ${epoch} LIMIT 1 BY epoch, val_id ) - GROUP BY val_id, val_nos_id + GROUP BY val_id, val_nos_id, val_nos_module_id ) AS withdrawals ON withdrawals.val_id = current.val_id - GROUP BY current.val_nos_id + GROUP BY current.val_nos_module_id, current.val_nos_id `; export const validatorsCountWithNegativeDeltaQuery = (epoch: Epoch): string => ` SELECT + current.val_nos_module_id as val_nos_module_id, current.val_nos_id as val_nos_id, count(current.val_id) AS amount FROM ( - SELECT val_balance, val_id, val_nos_id, val_slashed + SELECT val_balance, val_id, val_nos_module_id, val_nos_id, val_slashed FROM validators_summary WHERE val_status != '${ValStatus.PendingQueued}' AND @@ -121,9 +124,9 @@ export const validatorsCountWithNegativeDeltaQuery = (epoch: Epoch): string => ` previous.val_id = current.val_id LEFT JOIN ( SELECT - sum(val_balance_withdrawn) as withdrawn, val_id, val_nos_id + sum(val_balance_withdrawn) as withdrawn, val_id, val_nos_module_id, val_nos_id FROM ( - SELECT val_balance_withdrawn, val_id, val_nos_id + SELECT val_balance_withdrawn, val_id, val_nos_module_id, val_nos_id FROM validators_summary WHERE val_nos_id IS NOT NULL AND @@ -132,11 +135,11 @@ export const validatorsCountWithNegativeDeltaQuery = (epoch: Epoch): string => ` epoch > (${epoch} - 6) AND epoch <= ${epoch} LIMIT 1 BY epoch, val_id ) - GROUP BY val_id, val_nos_id + GROUP BY val_id, val_nos_id, val_nos_module_id ) AS withdrawals ON withdrawals.val_id = current.val_id - GROUP BY current.val_nos_id + GROUP BY current.val_nos_module_id, current.val_nos_id HAVING (current.val_balance - previous.val_balance + ifNull(withdrawals.withdrawn, 0)) < 0 AND current.val_slashed = 0 `; @@ -152,14 +155,16 @@ export const validatorsCountWithSyncParticipationByConditionLastNEpochQuery = ( } return ` SELECT + val_nos_module_id, val_nos_id, count() as amount FROM ( SELECT + val_nos_module_id, val_nos_id, count() AS count_fail FROM ( - SELECT val_id, val_nos_id + SELECT val_id, val_nos_module_id, val_nos_id FROM validators_summary WHERE is_sync = 1 AND @@ -169,10 +174,10 @@ export const validatorsCountWithSyncParticipationByConditionLastNEpochQuery = ( ${strFilterValIndexes} LIMIT 1 BY epoch, val_id ) - GROUP BY val_id, val_nos_id + GROUP BY val_id, val_nos_module_id, val_nos_id ) WHERE count_fail = ${epochInterval} - GROUP BY val_nos_id + GROUP BY val_nos_module_id, val_nos_id `; }; @@ -188,14 +193,16 @@ export const validatorCountByConditionAttestationLastNEpochQuery = ( } return ` SELECT + val_nos_module_id, val_nos_id, count() as amount FROM ( SELECT + val_nos_module_id, val_nos_id, count() as count_fail FROM ( - SELECT val_id, val_nos_id + SELECT val_id, val_nos_module_id, val_nos_id FROM validators_summary WHERE ${condition} @@ -204,34 +211,36 @@ export const validatorCountByConditionAttestationLastNEpochQuery = ( ${strFilterValIndexes} LIMIT 1 BY epoch, val_id ) - GROUP BY val_id, val_nos_id + GROUP BY val_id, val_nos_module_id, val_nos_id ) WHERE count_fail = ${epochInterval} - GROUP BY val_nos_id + GROUP BY val_nos_module_id, val_nos_id `; }; export const validatorCountHighAvgIncDelayAttestationOfNEpochQuery = (epoch: Epoch, epochInterval: number): string => { return ` SELECT + val_nos_module_id, val_nos_id, count() as amount FROM ( SELECT + val_nos_module_id, val_nos_id, avg(att_inc_delay) as avg_inclusion_delay FROM ( - SELECT val_id, val_nos_id, att_inc_delay + SELECT val_id, val_nos_module_id, val_nos_id, att_inc_delay FROM validators_summary WHERE val_stuck = 0 AND (epoch <= ${epoch} AND epoch > (${epoch} - ${epochInterval})) LIMIT 1 BY epoch, val_id ) - GROUP BY val_id, val_nos_id + GROUP BY val_id, val_nos_module_id, val_nos_id HAVING avg_inclusion_delay > 2 ) - GROUP BY val_nos_id + GROUP BY val_nos_id, val_nos_module_id `; }; @@ -242,10 +251,11 @@ export const validatorsCountByConditionMissProposeQuery = (epoch: Epoch, validat } return ` SELECT + val_nos_module_id, val_nos_id, count() as amount FROM ( - SELECT val_nos_id + SELECT val_nos_module_id, val_nos_id FROM validators_summary WHERE is_proposer = 1 AND @@ -255,20 +265,22 @@ export const validatorsCountByConditionMissProposeQuery = (epoch: Epoch, validat ${strFilterValIndexes} LIMIT 1 BY epoch, val_id ) - GROUP BY val_nos_id + GROUP BY val_nos_module_id, val_nos_id `; }; export const userSyncParticipationAvgPercentQuery = (epoch: Epoch): string => ` SELECT + val_nos_module_id, avg(sync_percent) as amount FROM ( - SELECT sync_percent + SELECT val_nos_module_id, sync_percent FROM validators_summary WHERE is_sync = 1 AND val_nos_id IS NOT NULL AND val_stuck = 0 AND epoch = ${epoch} LIMIT 1 BY val_id ) + GROUP BY val_nos_module_id `; export const otherSyncParticipationAvgPercentQuery = (epoch: Epoch): string => ` @@ -297,23 +309,25 @@ export const chainSyncParticipationAvgPercentQuery = (epoch: Epoch): string => ` export const operatorsSyncParticipationAvgPercentsQuery = (epoch: Epoch): string => ` SELECT + val_nos_module_id, val_nos_id, avg(sync_percent) as amount FROM ( - SELECT val_nos_id, sync_percent + SELECT val_nos_module_id, val_nos_id, sync_percent FROM validators_summary WHERE is_sync = 1 AND val_nos_id IS NOT NULL AND val_stuck = 0 AND epoch = ${epoch} LIMIT 1 BY val_id ) - GROUP BY val_nos_id + GROUP BY val_nos_module_id, val_nos_id `; export const totalBalance24hDifferenceQuery = (epoch: Epoch): string => ` SELECT + curr.val_nos_module_id as val_nos_module_id, SUM(curr.val_balance - previous.val_balance + ifNull(withdrawals.withdrawn, 0)) as amount FROM ( - SELECT val_balance, val_id, val_nos_id + SELECT val_balance, val_id, val_nos_module_id FROM validators_summary WHERE val_status != '${ValStatus.PendingQueued}' AND @@ -323,7 +337,7 @@ export const totalBalance24hDifferenceQuery = (epoch: Epoch): string => ` LIMIT 1 BY val_id ) as curr INNER JOIN ( - SELECT val_balance, val_id, val_nos_id + SELECT val_balance, val_id FROM validators_summary WHERE val_status != '${ValStatus.PendingQueued}' AND @@ -333,13 +347,12 @@ export const totalBalance24hDifferenceQuery = (epoch: Epoch): string => ` LIMIT 1 BY val_id ) AS previous ON - previous.val_nos_id = curr.val_nos_id AND previous.val_id = curr.val_id LEFT JOIN ( SELECT - sum(val_balance_withdrawn) as withdrawn, val_id, val_nos_id + sum(val_balance_withdrawn) as withdrawn, val_id FROM ( - SELECT val_balance_withdrawn, val_id, val_nos_id + SELECT val_balance_withdrawn, val_id FROM validators_summary WHERE val_nos_id IS NOT NULL AND @@ -348,19 +361,20 @@ export const totalBalance24hDifferenceQuery = (epoch: Epoch): string => ` epoch > (${epoch} - 225) AND epoch <= ${epoch} LIMIT 1 BY epoch, val_id ) - GROUP BY val_id, val_nos_id + GROUP BY val_id ) AS withdrawals ON - withdrawals.val_nos_id = curr.val_nos_id AND withdrawals.val_id = curr.val_id + GROUP BY curr.val_nos_module_id `; export const operatorBalance24hDifferenceQuery = (epoch: Epoch): string => ` SELECT + curr.val_nos_module_id as val_nos_module_id, curr.val_nos_id as val_nos_id, SUM(curr.val_balance - previous.val_balance + ifNull(withdrawals.withdrawn, 0)) as amount FROM ( - SELECT val_balance, val_id, val_nos_id + SELECT val_balance, val_id, val_nos_module_id, val_nos_id FROM validators_summary WHERE val_status != '${ValStatus.PendingQueued}' AND @@ -400,11 +414,12 @@ export const operatorBalance24hDifferenceQuery = (epoch: Epoch): string => ` ON withdrawals.val_nos_id = curr.val_nos_id AND withdrawals.val_id = curr.val_id - GROUP BY curr.val_nos_id + GROUP BY curr.val_nos_module_id, curr.val_nos_id `; export const userNodeOperatorsStatsQuery = (epoch: Epoch): string => ` SELECT + val_nos_module_id, val_nos_id, SUM(a) as active_ongoing, SUM(p) as pending, @@ -414,6 +429,7 @@ export const userNodeOperatorsStatsQuery = (epoch: Epoch): string => ` SUM(st) as stuck FROM ( SELECT + val_nos_module_id, val_nos_id, IF(val_status = '${ValStatus.ActiveOngoing}', count(val_status), 0) as a, IF(val_status = '${ValStatus.PendingQueued}' OR val_status = '${ValStatus.PendingInitialized}', count(val_status), 0) as p, @@ -432,19 +448,20 @@ export const userNodeOperatorsStatsQuery = (epoch: Epoch): string => ` ) as w, IF (val_stuck = 1, count(val_stuck), 0) as st FROM ( - SELECT val_nos_id, val_status, val_slashed, val_balance, val_stuck + SELECT val_nos_module_id, val_nos_id, val_status, val_slashed, val_balance, val_stuck FROM validators_summary WHERE val_nos_id IS NOT NULL AND epoch = ${epoch} LIMIT 1 BY val_id ) - GROUP BY val_nos_id, val_status, val_slashed, val_balance, val_stuck + GROUP BY val_nos_module_id, val_nos_id, val_status, val_slashed, val_balance, val_stuck ) - GROUP by val_nos_id + GROUP by val_nos_module_id, val_nos_id `; export const userValidatorsSummaryStatsQuery = (epoch: Epoch): string => ` SELECT + val_nos_module_id, SUM(a) as active_ongoing, SUM(p) as pending, SUM(s) as slashed, @@ -453,6 +470,7 @@ export const userValidatorsSummaryStatsQuery = (epoch: Epoch): string => ` SUM(st) as stuck FROM ( SELECT + val_nos_module_id, IF(val_status = '${ValStatus.ActiveOngoing}', count(val_status), 0) as a, IF(val_status = '${ValStatus.PendingQueued}' OR val_status = '${ValStatus.PendingInitialized}', count(val_status), 0) as p, IF(val_status = '${ValStatus.ActiveSlashed}' OR val_status = '${ValStatus.ExitedSlashed}' OR val_slashed = 1, count(val_status), 0) as s, @@ -470,14 +488,15 @@ export const userValidatorsSummaryStatsQuery = (epoch: Epoch): string => ` ) as w, IF (val_stuck = 1, count(val_stuck), 0) as st FROM ( - SELECT val_status, val_slashed, val_balance, val_stuck + SELECT val_nos_module_id, val_status, val_slashed, val_balance, val_stuck FROM validators_summary WHERE val_nos_id IS NOT NULL AND epoch = ${epoch} LIMIT 1 BY val_id ) - GROUP BY val_status, val_slashed, val_balance, val_stuck + GROUP BY val_nos_module_id, val_status, val_slashed, val_balance, val_stuck ) + GROUP by val_nos_module_id `; export const otherValidatorsSummaryStatsQuery = (epoch: Epoch): string => ` @@ -517,24 +536,26 @@ export const otherValidatorsSummaryStatsQuery = (epoch: Epoch): string => ` export const userNodeOperatorsProposesStatsLastNEpochQuery = (epoch: Epoch, epochInterval = 120): string => ` SELECT + val_nos_module_id, val_nos_id, SUM(a) as all, SUM(m) as missed FROM ( SELECT + val_nos_module_id, val_nos_id, - count(block_proposed) as a, + COUNT(block_proposed) as a, IF(block_proposed = 0, count(block_proposed), 0) as m FROM ( - SELECT val_nos_id, block_proposed + SELECT val_nos_module_id, val_nos_id, block_proposed FROM validators_summary WHERE is_proposer = 1 AND val_stuck = 0 AND (epoch <= ${epoch} AND epoch > (${epoch} - ${epochInterval})) LIMIT 1 BY epoch, val_id ) - GROUP BY val_nos_id, block_proposed + GROUP BY val_nos_module_id, val_nos_id, block_proposed ) - GROUP by val_nos_id + GROUP BY val_nos_module_id, val_nos_id `; export const epochMetadata = (epoch: Epoch): string => ` @@ -551,6 +572,7 @@ export const epochProcessing = (epoch: Epoch): string => ` export const userNodeOperatorsRewardsAndPenaltiesStats = (epoch: Epoch): string => ` SELECT + att.val_nos_module_id as val_nos_module_id, att.val_nos_id as val_nos_id, -- attestation_reward as att_reward, @@ -571,52 +593,62 @@ export const userNodeOperatorsRewardsAndPenaltiesStats = (epoch: Epoch): string calculated_balance_change - real_balance_change as calculation_error FROM ( SELECT + val_nos_module_id, val_nos_id, sum(att_earned_reward) as attestation_reward, sum(att_missed_reward) as attestation_missed, sum(att_penalty) as attestation_penalty FROM ( - SELECT val_nos_id, att_earned_reward, att_missed_reward, att_penalty + SELECT val_nos_module_id, val_nos_id, att_earned_reward, att_missed_reward, att_penalty FROM validators_summary WHERE val_nos_id IS NOT NULL AND val_stuck = 0 AND epoch = ${epoch} - 1 LIMIT 1 BY val_id ) - GROUP BY val_nos_id + GROUP BY val_nos_module_id, val_nos_id ) as att LEFT JOIN ( SELECT + val_nos_module_id, val_nos_id, sum(propose_earned_reward) as prop_reward, sum(propose_missed_reward) as prop_missed, sum(propose_penalty) as prop_penalty FROM ( - SELECT val_nos_id, propose_earned_reward, propose_missed_reward, propose_penalty + SELECT val_nos_module_id, val_nos_id, propose_earned_reward, propose_missed_reward, propose_penalty FROM validators_summary WHERE val_nos_id IS NOT NULL AND val_stuck = 0 AND epoch = ${epoch} and is_proposer = 1 LIMIT 1 BY val_id ) - GROUP BY val_nos_id - ) as prop ON att.val_nos_id = prop.val_nos_id + GROUP BY val_nos_module_id, val_nos_id + ) as prop + ON + att.val_nos_module_id = prop.val_nos_module_id AND + att.val_nos_id = prop.val_nos_id LEFT JOIN ( SELECT + val_nos_module_id, val_nos_id, sum(sync_earned_reward) as sync_reward, sum(sync_missed_reward) as sync_missed, sum(sync_penalty) as sync_penalty FROM ( - SELECT val_nos_id, sync_earned_reward, sync_missed_reward, sync_penalty + SELECT val_nos_module_id, val_nos_id, sync_earned_reward, sync_missed_reward, sync_penalty FROM validators_summary WHERE val_nos_id IS NOT NULL AND val_stuck = 0 AND epoch = ${epoch} and is_sync = 1 LIMIT 1 BY val_id ) - GROUP BY val_nos_id - ) as sync ON att.val_nos_id = sync.val_nos_id + GROUP BY val_nos_module_id, val_nos_id + ) as sync + ON + att.val_nos_module_id = sync.val_nos_module_id AND + att.val_nos_id = sync.val_nos_id LEFT JOIN ( SELECT + current.val_nos_module_id as val_nos_module_id, current.val_nos_id as val_nos_id, sum(current.val_balance - previous.val_balance + ifNull(withdrawals.withdrawn, 0)) AS real_balance_change FROM ( - SELECT val_balance, val_id, val_nos_id + SELECT val_balance, val_id, val_nos_module_id, val_nos_id FROM validators_summary as curr WHERE val_nos_id IS NOT NULL AND @@ -650,8 +682,11 @@ export const userNodeOperatorsRewardsAndPenaltiesStats = (epoch: Epoch): string ) AS withdrawals ON withdrawals.val_id = current.val_id - GROUP BY current.val_nos_id - ) as bal ON att.val_nos_id = bal.val_nos_id + GROUP BY current.val_nos_module_id, current.val_nos_id + ) as bal + ON + att.val_nos_module_id = bal.val_nos_module_id AND + att.val_nos_id = bal.val_nos_id `; export const avgChainRewardsAndPenaltiesStats = (epoch: Epoch): string => ` @@ -705,6 +740,7 @@ export const avgChainRewardsAndPenaltiesStats = (epoch: Epoch): string => ` export const userNodeOperatorsWithdrawalsStats = (epoch: Epoch): string => ` SELECT + val_nos_module_id, val_nos_id, ifNull( sumIf( @@ -735,7 +771,7 @@ export const userNodeOperatorsWithdrawalsStats = (epoch: Epoch): string => ` 0 ) as partial_withdrawn_count FROM ( - SELECT val_balance_withdrawn, val_balance, val_id, val_nos_id + SELECT val_balance_withdrawn, val_balance, val_id, val_nos_module_id, val_nos_id FROM validators_summary WHERE val_nos_id IS NOT NULL AND @@ -744,7 +780,7 @@ export const userNodeOperatorsWithdrawalsStats = (epoch: Epoch): string => ` epoch = ${epoch} LIMIT 1 BY val_id ) - GROUP BY val_nos_id + GROUP BY val_nos_module_id, val_nos_id `; export const otherChainWithdrawalsStats = (epoch: Epoch): string => ` diff --git a/src/storage/clickhouse/clickhouse.service.ts b/src/storage/clickhouse/clickhouse.service.ts index 94c6f67f..8a9a1698 100644 --- a/src/storage/clickhouse/clickhouse.service.ts +++ b/src/storage/clickhouse/clickhouse.service.ts @@ -40,6 +40,7 @@ import { import { AvgChainRewardsStats, EpochProcessingState, + NOsBalance24hDiff, NOsDelta, NOsProposesStats, NOsValidatorsByConditionAttestationCount, @@ -61,6 +62,7 @@ import migration_000003_epoch_meta from './migrations/migration_000003_epoch_met import migration_000004_epoch_processing from './migrations/migration_000004_epoch_processing'; import migration_000005_withdrawals from './migrations/migration_000005_withdrawals'; import migration_000006_stuck_validators from './migrations/migration_000006_stuck_validators'; +import migration_000007_module_id from './migrations/migration_000007_module_id'; @Injectable() export class ClickhouseService implements OnModuleInit { @@ -230,6 +232,7 @@ export class ClickhouseService implements OnModuleInit { migration_000004_epoch_processing, migration_000005_withdrawals, migration_000006_stuck_validators, + migration_000007_module_id, ]; for (const query of migrations) { await this.db.exec({ query }); @@ -260,9 +263,11 @@ export class ClickhouseService implements OnModuleInit { /** * Send query to Clickhouse and receives information about User Sync Committee participants */ - public async getUserSyncParticipationAvgPercent(epoch: Epoch): Promise { - const ret = await this.select(userSyncParticipationAvgPercentQuery(epoch)); - return { amount: Number(ret[0].amount) }; + public async getUserSyncParticipationAvgPercent(epoch: Epoch): Promise { + return (await this.select(userSyncParticipationAvgPercentQuery(epoch))).map((v) => ({ + ...v, + amount: Number(v.amount), + })); } /** @@ -497,13 +502,15 @@ export class ClickhouseService implements OnModuleInit { })); } - public async getTotalBalance24hDifference(epoch: Epoch): Promise { - const ret = await this.select<{ amount }[]>(totalBalance24hDifferenceQuery(epoch)); - if (ret[0]) return Number(ret[0].amount); + public async getTotalBalance24hDifference(epoch: Epoch): Promise<{ val_nos_module_id; amount }[]> { + return (await this.select<{ val_nos_module_id; amount }[]>(totalBalance24hDifferenceQuery(epoch))).map((v) => ({ + ...v, + amount: Number(v.amount), + })); } - public async getOperatorBalance24hDifference(epoch: Epoch): Promise<{ val_nos_id; amount }[]> { - return (await this.select<{ val_nos_id; amount }[]>(operatorBalance24hDifferenceQuery(epoch))).map((v) => ({ + public async getOperatorBalance24hDifference(epoch: Epoch): Promise { + return (await this.select(operatorBalance24hDifferenceQuery(epoch))).map((v) => ({ ...v, amount: Number(v.amount), })); @@ -529,16 +536,16 @@ export class ClickhouseService implements OnModuleInit { * Send query to Clickhouse and receives information about summary * how many User Node Operator validators have active, slashed, pending status */ - public async getUserValidatorsSummaryStats(epoch: Epoch): Promise { - const ret = await this.select(userValidatorsSummaryStatsQuery(epoch)); - return { - active_ongoing: Number(ret[0].active_ongoing), - pending: Number(ret[0].pending), - slashed: Number(ret[0].slashed), - withdraw_pending: Number(ret[0].withdraw_pending), - withdrawn: Number(ret[0].withdrawn), - stuck: Number(ret[0].stuck), - }; + public async getUserValidatorsSummaryStats(epoch: Epoch): Promise { + return (await this.select(userValidatorsSummaryStatsQuery(epoch))).map((v) => ({ + ...v, + active_ongoing: Number(v.active_ongoing), + pending: Number(v.pending), + slashed: Number(v.slashed), + withdraw_pending: Number(v.withdraw_pending), + withdrawn: Number(v.withdrawn), + stuck: Number(v.stuck), + })); } /** @@ -548,6 +555,7 @@ export class ClickhouseService implements OnModuleInit { public async getOtherValidatorsSummaryStats(epoch: Epoch): Promise { const ret = await this.select(otherValidatorsSummaryStatsQuery(epoch)); return { + ...ret[0], active_ongoing: Number(ret[0].active_ongoing), pending: Number(ret[0].pending), slashed: Number(ret[0].slashed), @@ -663,6 +671,7 @@ export class ClickhouseService implements OnModuleInit { public async getOtherChainWithdrawalsStats(epoch: Epoch): Promise { return (await this.select(otherChainWithdrawalsStats(epoch))).map((v) => ({ + ...v, full_withdrawn_sum: +v.full_withdrawn_sum, full_withdrawn_count: +v.full_withdrawn_count, partial_withdrawn_sum: +v.partial_withdrawn_sum, diff --git a/src/storage/clickhouse/clickhouse.types.ts b/src/storage/clickhouse/clickhouse.types.ts index cd2517ec..e243546a 100644 --- a/src/storage/clickhouse/clickhouse.types.ts +++ b/src/storage/clickhouse/clickhouse.types.ts @@ -1,6 +1,7 @@ import { Epoch } from 'common/eth-providers/consensus-provider/types'; export interface ValidatorsStatusStats { + val_nos_module_id?: string; active_ongoing: number; pending: number; slashed: number; @@ -10,31 +11,43 @@ export interface ValidatorsStatusStats { } export interface NOsDelta { + val_nos_module_id: string; val_nos_id: string; amount: number; } export interface NOsValidatorsNegDeltaCount { + val_nos_module_id: string; val_nos_id: string; amount: number; } export interface NOsValidatorsSyncAvgPercent { + val_nos_module_id: string; val_nos_id: string; amount: number; } export interface NOsValidatorsSyncByConditionCount { + val_nos_module_id: string; val_nos_id: string; amount: number; } export interface NOsValidatorsByConditionAttestationCount { + val_nos_module_id: string; val_nos_id: string; amount: number; } export interface NOsValidatorsByConditionProposeCount { + val_nos_module_id: string; + val_nos_id: string; + amount: number; +} + +export interface NOsBalance24hDiff { + val_nos_module_id: string; val_nos_id: string; amount: number; } @@ -44,6 +57,7 @@ export interface NOsValidatorsStatusStats extends ValidatorsStatusStats { } export interface NOsValidatorsRewardsStats { + val_nos_module_id: string; val_nos_id: string; prop_reward: number; prop_missed: number; @@ -75,12 +89,14 @@ export interface AvgChainRewardsStats { } export interface NOsProposesStats { + val_nos_module_id: string; val_nos_id: string; all: number; missed: number; } export interface SyncCommitteeParticipationAvgPercents { + val_nos_module_id?: string; amount: number; } @@ -97,5 +113,6 @@ export interface WithdrawalsStats { partial_withdrawn_count: number; } export interface NOsWithdrawalsStats extends WithdrawalsStats { + val_nos_module_id: string; val_nos_id: string; } diff --git a/src/storage/clickhouse/migrations/migration_000007_module_id.ts b/src/storage/clickhouse/migrations/migration_000007_module_id.ts new file mode 100644 index 00000000..ae2057a1 --- /dev/null +++ b/src/storage/clickhouse/migrations/migration_000007_module_id.ts @@ -0,0 +1,7 @@ +const sql = ` +ALTER TABLE validators_summary +// state +ADD COLUMN IF NOT EXISTS val_nos_module_id Nullable(UInt32) AFTER val_id +`; + +export default sql; From 6182ab87308c7df2105c067c722477498e498078 Mon Sep 17 00:00:00 2001 From: vgorkavenko Date: Mon, 26 Jun 2023 16:56:16 +0400 Subject: [PATCH 2/4] fix: test --- .../keysapi-source/keysapi-source.module.ts | 11 ----------- test/duties.e2e-spec.ts | 3 +++ 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/common/validators-registry/keysapi-source/keysapi-source.module.ts b/src/common/validators-registry/keysapi-source/keysapi-source.module.ts index c3683367..d16be580 100644 --- a/src/common/validators-registry/keysapi-source/keysapi-source.module.ts +++ b/src/common/validators-registry/keysapi-source/keysapi-source.module.ts @@ -1,20 +1,9 @@ -import { ValidatorRegistryModule } from '@lido-nestjs/registry'; import { Module } from '@nestjs/common'; -import { ExecutionProvider } from 'common/eth-providers'; - import { KeysapiSourceClient } from './keysapi-source.client'; import { KeysapiSourceService } from './keysapi-source.service'; @Module({ - imports: [ - ValidatorRegistryModule.forFeatureAsync({ - async useFactory(provider: ExecutionProvider) { - return { provider }; - }, - inject: [ExecutionProvider], - }), - ], providers: [KeysapiSourceService, KeysapiSourceClient], exports: [KeysapiSourceService], }) diff --git a/test/duties.e2e-spec.ts b/test/duties.e2e-spec.ts index 196658e3..c7e9dfda 100644 --- a/test/duties.e2e-spec.ts +++ b/test/duties.e2e-spec.ts @@ -66,6 +66,7 @@ const testSyncMember = { /// val_id: 285113, val_pubkey: '0x82750f01239832e15f0706f38cbbe35bed4cdfa4537391c14af00d8c2ae8dd695f1db09a1fbe81956ade016b245a2343', + val_nos_module_id: 1, val_nos_id: 0, val_nos_name: 'test1', val_slashed: false, @@ -103,6 +104,7 @@ const testProposerMember = { /// val_id: 389499, val_pubkey: '0x88cb7b40e37964130a2c3b1b2a0a37658ca53bd914881244b836257132132f734613f0450fe59528baa0a3e10bd37dd7', + val_nos_module_id: 1, val_nos_id: 1, val_nos_name: 'test2', val_slashed: false, @@ -148,6 +150,7 @@ describe('Duties', () => { testValidators.forEach((v) => keysMap.set(v.pubkey, { index: v.registry_index, + moduleIndex: 1, operatorIndex: v.operator_index, operatorName: v.operator_name, key: v.pubkey, From 0767608554fc30f88a252657f6b4c7e6cee3afaa Mon Sep 17 00:00:00 2001 From: vgorkavenko Date: Wed, 28 Jun 2023 14:08:05 +0400 Subject: [PATCH 3/4] fix: add default value --- src/storage/clickhouse/migrations/migration_000007_module_id.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/storage/clickhouse/migrations/migration_000007_module_id.ts b/src/storage/clickhouse/migrations/migration_000007_module_id.ts index ae2057a1..79c736f4 100644 --- a/src/storage/clickhouse/migrations/migration_000007_module_id.ts +++ b/src/storage/clickhouse/migrations/migration_000007_module_id.ts @@ -1,7 +1,7 @@ const sql = ` ALTER TABLE validators_summary // state -ADD COLUMN IF NOT EXISTS val_nos_module_id Nullable(UInt32) AFTER val_id +ADD COLUMN IF NOT EXISTS val_nos_module_id UInt32 DEFAULT 1 AFTER val_id `; export default sql; From 3c7ae14b66a55779d24b23440c2028ceb571d343 Mon Sep 17 00:00:00 2001 From: vgorkavenko Date: Wed, 28 Jun 2023 14:08:21 +0400 Subject: [PATCH 4/4] fix: remove db name from dashboard --- .../provisioning/dashboards/operators.json | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/docker/grafana/provisioning/dashboards/operators.json b/docker/grafana/provisioning/dashboards/operators.json index 1f33e97e..7028afc7 100644 --- a/docker/grafana/provisioning/dashboards/operators.json +++ b/docker/grafana/provisioning/dashboards/operators.json @@ -1499,8 +1499,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT\n val_id as Validator,\n val_status as Status,\n val_balance / pow(10,9) as Balance\nFROM\n stats.validators_summary\nWHERE val_nos_name = '${nos_name_var}' AND epoch = ${epoch_number_var} AND (val_status in ['active_exiting','exited_unslashed', 'exited_slashed'] or val_status == 'withdrawal_possible' and val_balance != 0)\nORDER BY val_id, val_balance\nLIMIT 1 by val_id", - "rawQuery": "SELECT\n val_id as Validator,\n val_status as Status,\n val_balance / pow(10,9) as Balance\nFROM\n stats.validators_summary\nWHERE val_nos_name = 'cryptomanufaktur.io' AND epoch = 166657 AND (val_status in ['active_exiting','exited_unslashed', 'exited_slashed'] or val_status == 'withdrawal_possible' and val_balance != 0)\nORDER BY val_id, val_balance\nLIMIT 1 by val_id", + "query": "SELECT\n val_id as Validator,\n val_status as Status,\n val_balance / pow(10,9) as Balance\nFROM\n validators_summary\nWHERE val_nos_name = '${nos_name_var}' AND epoch = ${epoch_number_var} AND (val_status in ['active_exiting','exited_unslashed', 'exited_slashed'] or val_status == 'withdrawal_possible' and val_balance != 0)\nORDER BY val_id, val_balance\nLIMIT 1 by val_id", + "rawQuery": "SELECT\n val_id as Validator,\n val_status as Status,\n val_balance / pow(10,9) as Balance\nFROM\n validators_summary\nWHERE val_nos_name = 'cryptomanufaktur.io' AND epoch = 166657 AND (val_status in ['active_exiting','exited_unslashed', 'exited_slashed'] or val_status == 'withdrawal_possible' and val_balance != 0)\nORDER BY val_id, val_balance\nLIMIT 1 by val_id", "refId": "A", "round": "0s", "skip_comments": true @@ -1781,8 +1781,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT\n current.val_balance - previous.val_balance + ifNull(withdrawals.withdrawn, 0) AS GWei,\n current.val_id as Validator\nFROM \n (\n SELECT val_balance, val_id, val_nos_id, val_slashed\n FROM stats.validators_summary \n WHERE \n val_status != 'pending_queued' AND \n val_nos_name = '${nos_name_var}' AND\n val_stuck = 0 AND\n epoch = ${epoch_number_var}\n LIMIT 1 by val_id\n ) AS current\nLEFT JOIN\n (\n SELECT val_balance, val_id, val_nos_id \n FROM stats.validators_summary \n WHERE \n val_status != 'pending_queued' AND \n val_nos_name = '${nos_name_var}' AND\n val_stuck = 0 AND\n epoch = ${epoch_number_var} - 6\n LIMIT 1 by val_id\n ) AS previous\nON\n previous.val_nos_id = current.val_nos_id AND \n previous.val_id = current.val_id\nLEFT JOIN (\n SELECT\n sum(val_balance_withdrawn) as withdrawn, val_id, val_nos_id\n FROM (\n SELECT val_balance_withdrawn, val_id, val_nos_id\n FROM stats.validators_summary\n WHERE\n val_nos_name = '${nos_name_var}' AND\n val_stuck = 0 AND\n epoch > (${epoch_number_var} - 6) AND epoch <= ${epoch_number_var}\n LIMIT 1 BY epoch, val_id\n )\n GROUP BY val_id, val_nos_id\n) AS withdrawals\nON\n withdrawals.val_nos_id = current.val_nos_id AND\n withdrawals.val_id = current.val_id\nHAVING (current.val_balance - previous.val_balance + ifNull(withdrawals.withdrawn, 0)) < 0 and current.val_slashed = 0\nORDER BY GWei ASC, Validator\nLIMIT ${limit}", - "rawQuery": "SELECT\n current.val_balance - previous.val_balance + ifNull(withdrawals.withdrawn, 0) AS GWei,\n current.val_id as Validator\nFROM \n (\n SELECT val_balance, val_id, val_nos_id, val_slashed\n FROM stats.validators_summary \n WHERE \n val_status != 'pending_queued' AND \n val_nos_name = 'Allnodes' AND\n val_stuck = 0 AND\n epoch = 166139\n LIMIT 1 by val_id\n ) AS current\nLEFT JOIN\n (\n SELECT val_balance, val_id, val_nos_id \n FROM stats.validators_summary \n WHERE \n val_status != 'pending_queued' AND \n val_nos_name = 'Allnodes' AND\n val_stuck = 0 AND\n epoch = 166139 - 6\n LIMIT 1 by val_id\n ) AS previous\nON\n previous.val_nos_id = current.val_nos_id AND \n previous.val_id = current.val_id\nLEFT JOIN (\n SELECT\n sum(val_balance_withdrawn) as withdrawn, val_id, val_nos_id\n FROM (\n SELECT val_balance_withdrawn, val_id, val_nos_id\n FROM stats.validators_summary\n WHERE\n val_nos_name = 'Allnodes' AND\n val_stuck = 0 AND\n epoch > (166139 - 6) AND epoch <= 166139\n LIMIT 1 BY epoch, val_id\n )\n GROUP BY val_id, val_nos_id\n) AS withdrawals\nON\n withdrawals.val_nos_id = current.val_nos_id AND\n withdrawals.val_id = current.val_id\nHAVING (current.val_balance - previous.val_balance + ifNull(withdrawals.withdrawn, 0)) < 0 and current.val_slashed = 0\nORDER BY GWei ASC, Validator\nLIMIT 100", + "query": "SELECT\n current.val_balance - previous.val_balance + ifNull(withdrawals.withdrawn, 0) AS GWei,\n current.val_id as Validator\nFROM \n (\n SELECT val_balance, val_id, val_nos_id, val_slashed\n FROM validators_summary \n WHERE \n val_status != 'pending_queued' AND \n val_nos_name = '${nos_name_var}' AND\n val_stuck = 0 AND\n epoch = ${epoch_number_var}\n LIMIT 1 by val_id\n ) AS current\nLEFT JOIN\n (\n SELECT val_balance, val_id, val_nos_id \n FROM validators_summary \n WHERE \n val_status != 'pending_queued' AND \n val_nos_name = '${nos_name_var}' AND\n val_stuck = 0 AND\n epoch = ${epoch_number_var} - 6\n LIMIT 1 by val_id\n ) AS previous\nON\n previous.val_nos_id = current.val_nos_id AND \n previous.val_id = current.val_id\nLEFT JOIN (\n SELECT\n sum(val_balance_withdrawn) as withdrawn, val_id, val_nos_id\n FROM (\n SELECT val_balance_withdrawn, val_id, val_nos_id\n FROM validators_summary\n WHERE\n val_nos_name = '${nos_name_var}' AND\n val_stuck = 0 AND\n epoch > (${epoch_number_var} - 6) AND epoch <= ${epoch_number_var}\n LIMIT 1 BY epoch, val_id\n )\n GROUP BY val_id, val_nos_id\n) AS withdrawals\nON\n withdrawals.val_nos_id = current.val_nos_id AND\n withdrawals.val_id = current.val_id\nHAVING (current.val_balance - previous.val_balance + ifNull(withdrawals.withdrawn, 0)) < 0 and current.val_slashed = 0\nORDER BY GWei ASC, Validator\nLIMIT ${limit}", + "rawQuery": "SELECT\n current.val_balance - previous.val_balance + ifNull(withdrawals.withdrawn, 0) AS GWei,\n current.val_id as Validator\nFROM \n (\n SELECT val_balance, val_id, val_nos_id, val_slashed\n FROM validators_summary \n WHERE \n val_status != 'pending_queued' AND \n val_nos_name = 'Allnodes' AND\n val_stuck = 0 AND\n epoch = 166139\n LIMIT 1 by val_id\n ) AS current\nLEFT JOIN\n (\n SELECT val_balance, val_id, val_nos_id \n FROM validators_summary \n WHERE \n val_status != 'pending_queued' AND \n val_nos_name = 'Allnodes' AND\n val_stuck = 0 AND\n epoch = 166139 - 6\n LIMIT 1 by val_id\n ) AS previous\nON\n previous.val_nos_id = current.val_nos_id AND \n previous.val_id = current.val_id\nLEFT JOIN (\n SELECT\n sum(val_balance_withdrawn) as withdrawn, val_id, val_nos_id\n FROM (\n SELECT val_balance_withdrawn, val_id, val_nos_id\n FROM validators_summary\n WHERE\n val_nos_name = 'Allnodes' AND\n val_stuck = 0 AND\n epoch > (166139 - 6) AND epoch <= 166139\n LIMIT 1 BY epoch, val_id\n )\n GROUP BY val_id, val_nos_id\n) AS withdrawals\nON\n withdrawals.val_nos_id = current.val_nos_id AND\n withdrawals.val_id = current.val_id\nHAVING (current.val_balance - previous.val_balance + ifNull(withdrawals.withdrawn, 0)) < 0 and current.val_slashed = 0\nORDER BY GWei ASC, Validator\nLIMIT 100", "refId": "A", "round": "0s", "skip_comments": true @@ -1901,8 +1901,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT\n block_to_propose as Block,\n val_id as Validator\nFROM stats.validators_summary\nWHERE is_proposer = 1 AND block_proposed = 0 AND val_stuck = 0 AND (epoch = ${epoch_number_var}) AND val_nos_name = '${nos_name_var}'\nORDER BY block_to_propose DESC, val_id\nLIMIT 1 by val_id", - "rawQuery": "SELECT\n block_to_propose as Block,\n val_id as Validator\nFROM stats.validators_summary\nWHERE is_proposer = 1 AND block_proposed = 0 AND val_stuck = 0 AND (epoch = 166139) AND val_nos_name = 'Allnodes'\nORDER BY block_to_propose DESC, val_id\nLIMIT 1 by val_id", + "query": "SELECT\n block_to_propose as Block,\n val_id as Validator\nFROM validators_summary\nWHERE is_proposer = 1 AND block_proposed = 0 AND val_stuck = 0 AND (epoch = ${epoch_number_var}) AND val_nos_name = '${nos_name_var}'\nORDER BY block_to_propose DESC, val_id\nLIMIT 1 by val_id", + "rawQuery": "SELECT\n block_to_propose as Block,\n val_id as Validator\nFROM validators_summary\nWHERE is_proposer = 1 AND block_proposed = 0 AND val_stuck = 0 AND (epoch = 166139) AND val_nos_name = 'Allnodes'\nORDER BY block_to_propose DESC, val_id\nLIMIT 1 by val_id", "refId": "A", "round": "0s", "skip_comments": true @@ -2034,8 +2034,8 @@ "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "hide": false, "intervalFactor": 1, - "query": "SELECT\n val_id as Validator\nFROM (\n SELECT\n val_id,\n count() AS count_fail\n FROM (\n SELECT val_id\n FROM stats.validators_summary\n WHERE\n is_sync = 1 AND val_stuck = 0 AND sync_percent < (${chain_sync_avg_participation} - ${sync_participation_distance_var}) AND\n (epoch <= ${epoch_number_var} AND epoch > (${epoch_number_var} - ${sync_epochs_var})) AND\n val_nos_name = '${nos_name_var}'\n LIMIT 1 by epoch, val_id \n )\n GROUP BY val_id\n)\nWHERE count_fail = ${sync_epochs_var}\nORDER BY count_fail DESC, val_id", - "rawQuery": "SELECT\n val_id as Validator\nFROM (\n SELECT\n val_id,\n count() AS count_fail\n FROM (\n SELECT val_id\n FROM stats.validators_summary\n WHERE\n is_sync = 1 AND val_stuck = 0 AND sync_percent < (76.66713145608082 - 0) AND\n (epoch <= 166139 AND epoch > (166139 - 3)) AND\n val_nos_name = 'Allnodes'\n LIMIT 1 by epoch, val_id \n )\n GROUP BY val_id\n)\nWHERE count_fail = 3\nORDER BY count_fail DESC, val_id", + "query": "SELECT\n val_id as Validator\nFROM (\n SELECT\n val_id,\n count() AS count_fail\n FROM (\n SELECT val_id\n FROM validators_summary\n WHERE\n is_sync = 1 AND val_stuck = 0 AND sync_percent < (${chain_sync_avg_participation} - ${sync_participation_distance_var}) AND\n (epoch <= ${epoch_number_var} AND epoch > (${epoch_number_var} - ${sync_epochs_var})) AND\n val_nos_name = '${nos_name_var}'\n LIMIT 1 by epoch, val_id \n )\n GROUP BY val_id\n)\nWHERE count_fail = ${sync_epochs_var}\nORDER BY count_fail DESC, val_id", + "rawQuery": "SELECT\n val_id as Validator\nFROM (\n SELECT\n val_id,\n count() AS count_fail\n FROM (\n SELECT val_id\n FROM validators_summary\n WHERE\n is_sync = 1 AND val_stuck = 0 AND sync_percent < (76.66713145608082 - 0) AND\n (epoch <= 166139 AND epoch > (166139 - 3)) AND\n val_nos_name = 'Allnodes'\n LIMIT 1 by epoch, val_id \n )\n GROUP BY val_id\n)\nWHERE count_fail = 3\nORDER BY count_fail DESC, val_id", "refId": "B", "round": "0s", "skip_comments": true @@ -2133,8 +2133,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "\nSELECT\n val_id as Validator\nFROM (\n SELECT\n val_id,\n count() as count_fail\n FROM (\n SELECT val_id\n FROM stats.validators_summary\n WHERE \n att_happened = 0 AND val_stuck = 0 AND val_nos_name = '${nos_name_var}' AND\n (epoch <= ${epoch_number_var} AND epoch > (${epoch_number_var} - ${att_epochs_var}))\n LIMIT 1 by epoch, val_id\n )\n GROUP BY val_id\n)\nWHERE count_fail = ${att_epochs_var}\nORDER BY count_fail DESC, val_id\nLIMIT ${limit}", - "rawQuery": "SELECT\n val_id as Validator\nFROM (\n SELECT\n val_id,\n count() as count_fail\n FROM (\n SELECT val_id\n FROM stats.validators_summary\n WHERE \n att_happened = 0 AND val_stuck = 0 AND val_nos_name = 'Allnodes' AND\n (epoch <= 166139 AND epoch > (166139 - 3))\n LIMIT 1 by epoch, val_id\n )\n GROUP BY val_id\n)\nWHERE count_fail = 3\nORDER BY count_fail DESC, val_id\nLIMIT 100", + "query": "\nSELECT\n val_id as Validator\nFROM (\n SELECT\n val_id,\n count() as count_fail\n FROM (\n SELECT val_id\n FROM validators_summary\n WHERE \n att_happened = 0 AND val_stuck = 0 AND val_nos_name = '${nos_name_var}' AND\n (epoch <= ${epoch_number_var} AND epoch > (${epoch_number_var} - ${att_epochs_var}))\n LIMIT 1 by epoch, val_id\n )\n GROUP BY val_id\n)\nWHERE count_fail = ${att_epochs_var}\nORDER BY count_fail DESC, val_id\nLIMIT ${limit}", + "rawQuery": "SELECT\n val_id as Validator\nFROM (\n SELECT\n val_id,\n count() as count_fail\n FROM (\n SELECT val_id\n FROM validators_summary\n WHERE \n att_happened = 0 AND val_stuck = 0 AND val_nos_name = 'Allnodes' AND\n (epoch <= 166139 AND epoch > (166139 - 3))\n LIMIT 1 by epoch, val_id\n )\n GROUP BY val_id\n)\nWHERE count_fail = 3\nORDER BY count_fail DESC, val_id\nLIMIT 100", "refId": "A", "round": "0s", "skip_comments": true @@ -2244,8 +2244,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT\n val_id as Validator,\n count() as count_fail\nFROM (\n SELECT val_id\n FROM stats.validators_summary\n WHERE\n val_nos_name = '${nos_name_var}' AND\n att_happened = 1 AND\n att_inc_delay > 1 AND\n val_stuck = 0 AND\n (epoch <= ${epoch_number_var} AND epoch > (${epoch_number_var} - ${att_epochs_var}))\n LIMIT 1 by epoch, val_id\n)\nGROUP BY val_id\nHAVING count_fail = ${att_epochs_var}\nORDER BY count_fail DESC, val_id\nLIMIT ${limit}", - "rawQuery": "SELECT\n val_id as Validator,\n count() as count_fail\nFROM (\n SELECT val_id\n FROM stats.validators_summary\n WHERE\n val_nos_name = 'Allnodes' AND\n att_happened = 1 AND\n att_inc_delay > 1 AND\n val_stuck = 0 AND\n (epoch <= 166139 AND epoch > (166139 - 3))\n LIMIT 1 by epoch, val_id\n)\nGROUP BY val_id\nHAVING count_fail = 3\nORDER BY count_fail DESC, val_id\nLIMIT 100", + "query": "SELECT\n val_id as Validator,\n count() as count_fail\nFROM (\n SELECT val_id\n FROM validators_summary\n WHERE\n val_nos_name = '${nos_name_var}' AND\n att_happened = 1 AND\n att_inc_delay > 1 AND\n val_stuck = 0 AND\n (epoch <= ${epoch_number_var} AND epoch > (${epoch_number_var} - ${att_epochs_var}))\n LIMIT 1 by epoch, val_id\n)\nGROUP BY val_id\nHAVING count_fail = ${att_epochs_var}\nORDER BY count_fail DESC, val_id\nLIMIT ${limit}", + "rawQuery": "SELECT\n val_id as Validator,\n count() as count_fail\nFROM (\n SELECT val_id\n FROM validators_summary\n WHERE\n val_nos_name = 'Allnodes' AND\n att_happened = 1 AND\n att_inc_delay > 1 AND\n val_stuck = 0 AND\n (epoch <= 166139 AND epoch > (166139 - 3))\n LIMIT 1 by epoch, val_id\n)\nGROUP BY val_id\nHAVING count_fail = 3\nORDER BY count_fail DESC, val_id\nLIMIT 100", "refId": "A", "round": "0s", "skip_comments": true @@ -2356,8 +2356,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT\n val_id as Validator,\n count() as count_fail\nFROM (\n SELECT val_id\n FROM stats.validators_summary\n WHERE\n val_nos_name = '${nos_name_var}' AND\n (att_valid_head + att_valid_target + att_valid_source = 1) AND\n val_stuck = 0 AND\n (epoch <= ${epoch_number_var} AND epoch > (${epoch_number_var} - ${att_epochs_var}))\n LIMIT 1 by epoch, val_id\n)\nGROUP BY val_id\nHAVING count_fail = ${att_epochs_var}\nORDER BY count_fail DESC, val_id\nLIMIT ${limit}", - "rawQuery": "SELECT\n val_id as Validator,\n count() as count_fail\nFROM (\n SELECT val_id\n FROM stats.validators_summary\n WHERE\n val_nos_name = 'Allnodes' AND\n (att_valid_head + att_valid_target + att_valid_source = 1) AND\n val_stuck = 0 AND\n (epoch <= 166139 AND epoch > (166139 - 3))\n LIMIT 1 by epoch, val_id\n)\nGROUP BY val_id\nHAVING count_fail = 3\nORDER BY count_fail DESC, val_id\nLIMIT 100", + "query": "SELECT\n val_id as Validator,\n count() as count_fail\nFROM (\n SELECT val_id\n FROM validators_summary\n WHERE\n val_nos_name = '${nos_name_var}' AND\n (att_valid_head + att_valid_target + att_valid_source = 1) AND\n val_stuck = 0 AND\n (epoch <= ${epoch_number_var} AND epoch > (${epoch_number_var} - ${att_epochs_var}))\n LIMIT 1 by epoch, val_id\n)\nGROUP BY val_id\nHAVING count_fail = ${att_epochs_var}\nORDER BY count_fail DESC, val_id\nLIMIT ${limit}", + "rawQuery": "SELECT\n val_id as Validator,\n count() as count_fail\nFROM (\n SELECT val_id\n FROM validators_summary\n WHERE\n val_nos_name = 'Allnodes' AND\n (att_valid_head + att_valid_target + att_valid_source = 1) AND\n val_stuck = 0 AND\n (epoch <= 166139 AND epoch > (166139 - 3))\n LIMIT 1 by epoch, val_id\n)\nGROUP BY val_id\nHAVING count_fail = 3\nORDER BY count_fail DESC, val_id\nLIMIT 100", "refId": "A", "round": "0s", "skip_comments": true @@ -2881,8 +2881,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT\n current.val_balance - previous.val_balance + ifNull(withdrawals.withdrawn, 0) AS GWei,\n current.val_id as Validator\nFROM \n (\n SELECT val_balance, val_id, val_nos_id \n FROM stats.validators_summary \n WHERE \n val_status != 'pending_queued' AND \n val_nos_id IS NOT NULL AND\n val_stuck = 0 AND\n epoch = ${epoch_number_var} AND\n val_nos_name = '${nos_name_var}'\n LIMIT 1 by val_id\n ) AS current\nLEFT JOIN\n (\n SELECT val_balance, val_id, val_nos_id \n FROM stats.validators_summary \n WHERE \n val_status != 'pending_queued' AND \n val_nos_id IS NOT NULL AND\n val_stuck = 0 AND\n epoch = ${epoch_number_var} - 6 AND\n val_nos_name = '${nos_name_var}'\n LIMIT 1 by val_id\n ) AS previous\nON\n previous.val_nos_id = current.val_nos_id AND \n previous.val_id = current.val_id\nLEFT JOIN (\n SELECT\n sum(val_balance_withdrawn) as withdrawn, val_id, val_nos_id\n FROM (\n SELECT val_balance_withdrawn, val_id, val_nos_id\n FROM stats.validators_summary\n WHERE\n val_nos_id IS NOT NULL AND\n val_stuck = 0 AND\n epoch > (${epoch_number_var} - 6) AND epoch <= ${epoch_number_var} AND\n val_nos_name = '${nos_name_var}'\n LIMIT 1 BY epoch, val_id\n )\n GROUP BY val_id, val_nos_id\n) AS withdrawals\nON\n withdrawals.val_nos_id = current.val_nos_id AND\n withdrawals.val_id = current.val_id\nORDER BY GWei ASC, Validator\nLIMIT 100", - "rawQuery": "SELECT\n current.val_balance - previous.val_balance + ifNull(withdrawals.withdrawn, 0) AS GWei,\n current.val_id as Validator\nFROM \n (\n SELECT val_balance, val_id, val_nos_id \n FROM stats.validators_summary \n WHERE \n val_status != 'pending_queued' AND \n val_nos_id IS NOT NULL AND\n val_stuck = 0 AND\n epoch = 166139 AND\n val_nos_name = 'Allnodes'\n LIMIT 1 by val_id\n ) AS current\nLEFT JOIN\n (\n SELECT val_balance, val_id, val_nos_id \n FROM stats.validators_summary \n WHERE \n val_status != 'pending_queued' AND \n val_nos_id IS NOT NULL AND\n val_stuck = 0 AND\n epoch = 166139 - 6 AND\n val_nos_name = 'Allnodes'\n LIMIT 1 by val_id\n ) AS previous\nON\n previous.val_nos_id = current.val_nos_id AND \n previous.val_id = current.val_id\nLEFT JOIN (\n SELECT\n sum(val_balance_withdrawn) as withdrawn, val_id, val_nos_id\n FROM (\n SELECT val_balance_withdrawn, val_id, val_nos_id\n FROM stats.validators_summary\n WHERE\n val_nos_id IS NOT NULL AND\n val_stuck = 0 AND\n epoch > (166139 - 6) AND epoch <= 166139 AND\n val_nos_name = 'Allnodes'\n LIMIT 1 BY epoch, val_id\n )\n GROUP BY val_id, val_nos_id\n) AS withdrawals\nON\n withdrawals.val_nos_id = current.val_nos_id AND\n withdrawals.val_id = current.val_id\nORDER BY GWei ASC, Validator\nLIMIT 100", + "query": "SELECT\n current.val_balance - previous.val_balance + ifNull(withdrawals.withdrawn, 0) AS GWei,\n current.val_id as Validator\nFROM \n (\n SELECT val_balance, val_id, val_nos_id \n FROM validators_summary \n WHERE \n val_status != 'pending_queued' AND \n val_nos_id IS NOT NULL AND\n val_stuck = 0 AND\n epoch = ${epoch_number_var} AND\n val_nos_name = '${nos_name_var}'\n LIMIT 1 by val_id\n ) AS current\nLEFT JOIN\n (\n SELECT val_balance, val_id, val_nos_id \n FROM validators_summary \n WHERE \n val_status != 'pending_queued' AND \n val_nos_id IS NOT NULL AND\n val_stuck = 0 AND\n epoch = ${epoch_number_var} - 6 AND\n val_nos_name = '${nos_name_var}'\n LIMIT 1 by val_id\n ) AS previous\nON\n previous.val_nos_id = current.val_nos_id AND \n previous.val_id = current.val_id\nLEFT JOIN (\n SELECT\n sum(val_balance_withdrawn) as withdrawn, val_id, val_nos_id\n FROM (\n SELECT val_balance_withdrawn, val_id, val_nos_id\n FROM validators_summary\n WHERE\n val_nos_id IS NOT NULL AND\n val_stuck = 0 AND\n epoch > (${epoch_number_var} - 6) AND epoch <= ${epoch_number_var} AND\n val_nos_name = '${nos_name_var}'\n LIMIT 1 BY epoch, val_id\n )\n GROUP BY val_id, val_nos_id\n) AS withdrawals\nON\n withdrawals.val_nos_id = current.val_nos_id AND\n withdrawals.val_id = current.val_id\nORDER BY GWei ASC, Validator\nLIMIT 100", + "rawQuery": "SELECT\n current.val_balance - previous.val_balance + ifNull(withdrawals.withdrawn, 0) AS GWei,\n current.val_id as Validator\nFROM \n (\n SELECT val_balance, val_id, val_nos_id \n FROM validators_summary \n WHERE \n val_status != 'pending_queued' AND \n val_nos_id IS NOT NULL AND\n val_stuck = 0 AND\n epoch = 166139 AND\n val_nos_name = 'Allnodes'\n LIMIT 1 by val_id\n ) AS current\nLEFT JOIN\n (\n SELECT val_balance, val_id, val_nos_id \n FROM validators_summary \n WHERE \n val_status != 'pending_queued' AND \n val_nos_id IS NOT NULL AND\n val_stuck = 0 AND\n epoch = 166139 - 6 AND\n val_nos_name = 'Allnodes'\n LIMIT 1 by val_id\n ) AS previous\nON\n previous.val_nos_id = current.val_nos_id AND \n previous.val_id = current.val_id\nLEFT JOIN (\n SELECT\n sum(val_balance_withdrawn) as withdrawn, val_id, val_nos_id\n FROM (\n SELECT val_balance_withdrawn, val_id, val_nos_id\n FROM validators_summary\n WHERE\n val_nos_id IS NOT NULL AND\n val_stuck = 0 AND\n epoch > (166139 - 6) AND epoch <= 166139 AND\n val_nos_name = 'Allnodes'\n LIMIT 1 BY epoch, val_id\n )\n GROUP BY val_id, val_nos_id\n) AS withdrawals\nON\n withdrawals.val_nos_id = current.val_nos_id AND\n withdrawals.val_id = current.val_id\nORDER BY GWei ASC, Validator\nLIMIT 100", "refId": "A", "round": "0s", "skip_comments": true @@ -3563,8 +3563,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT\n sync_percent as Percent,\n val_id as Validator\nFROM\n stats.validators_summary\nWHERE is_sync = 1 AND val_nos_name = '${nos_name_var}' AND val_stuck = 0 AND epoch = ${epoch_number_var} AND sync_percent < (${chain_sync_avg_participation} - ${sync_participation_distance_var})\nORDER BY sync_percent, val_id\nLIMIT 1 by val_id", - "rawQuery": "SELECT\n sync_percent as Percent,\n val_id as Validator\nFROM\n stats.validators_summary\nWHERE is_sync = 1 AND val_nos_name = 'Allnodes' AND val_stuck = 0 AND epoch = 166141 AND sync_percent < (80.91881795600057 - 0)\nORDER BY sync_percent, val_id\nLIMIT 1 by val_id", + "query": "SELECT\n sync_percent as Percent,\n val_id as Validator\nFROM\n validators_summary\nWHERE is_sync = 1 AND val_nos_name = '${nos_name_var}' AND val_stuck = 0 AND epoch = ${epoch_number_var} AND sync_percent < (${chain_sync_avg_participation} - ${sync_participation_distance_var})\nORDER BY sync_percent, val_id\nLIMIT 1 by val_id", + "rawQuery": "SELECT\n sync_percent as Percent,\n val_id as Validator\nFROM\n validators_summary\nWHERE is_sync = 1 AND val_nos_name = 'Allnodes' AND val_stuck = 0 AND epoch = 166141 AND sync_percent < (80.91881795600057 - 0)\nORDER BY sync_percent, val_id\nLIMIT 1 by val_id", "refId": "A", "round": "0s", "skip_comments": true @@ -3840,8 +3840,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": " SELECT\n val_id as Validator\n FROM stats.validators_summary\n WHERE att_happened = 0 AND val_nos_name = '${nos_name_var}' AND val_stuck = 0 AND epoch = ${epoch_number_var}\n ORDER BY val_id\n LIMIT 1 by val_id\n LIMIT ${limit}", - "rawQuery": "SELECT\n val_id as Validator\n FROM stats.validators_summary\n WHERE att_happened = 0 AND val_nos_name = 'Allnodes' AND val_stuck = 0 AND epoch = 166141\n ORDER BY val_id\n LIMIT 1 by val_id\n LIMIT 100", + "query": " SELECT\n val_id as Validator\n FROM validators_summary\n WHERE att_happened = 0 AND val_nos_name = '${nos_name_var}' AND val_stuck = 0 AND epoch = ${epoch_number_var}\n ORDER BY val_id\n LIMIT 1 by val_id\n LIMIT ${limit}", + "rawQuery": "SELECT\n val_id as Validator\n FROM validators_summary\n WHERE att_happened = 0 AND val_nos_name = 'Allnodes' AND val_stuck = 0 AND epoch = 166141\n ORDER BY val_id\n LIMIT 1 by val_id\n LIMIT 100", "refId": "A", "round": "0s", "skip_comments": true @@ -4084,8 +4084,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "\n SELECT\n val_id as Validator\n FROM stats.validators_summary\n WHERE att_happened = 1 AND att_inc_delay > 1 AND val_nos_name = '${nos_name_var}' AND val_stuck = 0 AND epoch = ${epoch_number_var}\n ORDER BY val_id\n LIMIT 1 by val_id\n LIMIT ${limit}", - "rawQuery": "SELECT\n val_id as Validator\n FROM stats.validators_summary\n WHERE att_happened = 1 AND att_inc_delay > 1 AND val_nos_name = 'Allnodes' AND val_stuck = 0 AND epoch = 166141\n ORDER BY val_id\n LIMIT 1 by val_id\n LIMIT 100", + "query": "\n SELECT\n val_id as Validator\n FROM validators_summary\n WHERE att_happened = 1 AND att_inc_delay > 1 AND val_nos_name = '${nos_name_var}' AND val_stuck = 0 AND epoch = ${epoch_number_var}\n ORDER BY val_id\n LIMIT 1 by val_id\n LIMIT ${limit}", + "rawQuery": "SELECT\n val_id as Validator\n FROM validators_summary\n WHERE att_happened = 1 AND att_inc_delay > 1 AND val_nos_name = 'Allnodes' AND val_stuck = 0 AND epoch = 166141\n ORDER BY val_id\n LIMIT 1 by val_id\n LIMIT 100", "refId": "A", "round": "0s", "skip_comments": true @@ -4340,8 +4340,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "\n SELECT\n val_id as Validator\n FROM stats.validators_summary\n WHERE att_valid_head = 0 AND val_nos_name = '${nos_name_var}' AND val_stuck = 0 AND epoch = ${epoch_number_var}\n ORDER BY val_id\n LIMIT 1 BY val_id\n LIMIT ${limit}", - "rawQuery": "SELECT\n val_id as Validator\n FROM stats.validators_summary\n WHERE att_valid_head = 0 AND val_nos_name = 'Allnodes' AND val_stuck = 0 AND epoch = 166141\n ORDER BY val_id\n LIMIT 1 BY val_id\n LIMIT 100", + "query": "\n SELECT\n val_id as Validator\n FROM validators_summary\n WHERE att_valid_head = 0 AND val_nos_name = '${nos_name_var}' AND val_stuck = 0 AND epoch = ${epoch_number_var}\n ORDER BY val_id\n LIMIT 1 BY val_id\n LIMIT ${limit}", + "rawQuery": "SELECT\n val_id as Validator\n FROM validators_summary\n WHERE att_valid_head = 0 AND val_nos_name = 'Allnodes' AND val_stuck = 0 AND epoch = 166141\n ORDER BY val_id\n LIMIT 1 BY val_id\n LIMIT 100", "refId": "A", "round": "0s", "skip_comments": true @@ -4584,8 +4584,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "\n SELECT\n val_id as Validator\n FROM stats.validators_summary\n WHERE att_valid_target = 0 AND val_nos_name = '${nos_name_var}' AND val_stuck = 0 AND epoch = ${epoch_number_var}\n ORDER BY val_id\n LIMIT 1 by val_id\n LIMIT ${limit}", - "rawQuery": "SELECT\n val_id as Validator\n FROM stats.validators_summary\n WHERE att_valid_target = 0 AND val_nos_name = 'Allnodes' AND val_stuck = 0 AND epoch = 166141\n ORDER BY val_id\n LIMIT 1 by val_id\n LIMIT 100", + "query": "\n SELECT\n val_id as Validator\n FROM validators_summary\n WHERE att_valid_target = 0 AND val_nos_name = '${nos_name_var}' AND val_stuck = 0 AND epoch = ${epoch_number_var}\n ORDER BY val_id\n LIMIT 1 by val_id\n LIMIT ${limit}", + "rawQuery": "SELECT\n val_id as Validator\n FROM validators_summary\n WHERE att_valid_target = 0 AND val_nos_name = 'Allnodes' AND val_stuck = 0 AND epoch = 166141\n ORDER BY val_id\n LIMIT 1 by val_id\n LIMIT 100", "refId": "A", "round": "0s", "skip_comments": true @@ -4828,8 +4828,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "\n SELECT\n val_id as Validator\n FROM stats.validators_summary\n WHERE att_valid_source = 0 AND val_nos_name = '${nos_name_var}' AND val_stuck = 0 AND epoch = ${epoch_number_var}\n ORDER BY val_id\n LIMIT 1 by val_id\n LIMIT ${limit}", - "rawQuery": "SELECT\n val_id as Validator\n FROM stats.validators_summary\n WHERE att_valid_source = 0 AND val_nos_name = 'Allnodes' AND val_stuck = 0 AND epoch = 166141\n ORDER BY val_id\n LIMIT 1 by val_id\n LIMIT 100", + "query": "\n SELECT\n val_id as Validator\n FROM validators_summary\n WHERE att_valid_source = 0 AND val_nos_name = '${nos_name_var}' AND val_stuck = 0 AND epoch = ${epoch_number_var}\n ORDER BY val_id\n LIMIT 1 by val_id\n LIMIT ${limit}", + "rawQuery": "SELECT\n val_id as Validator\n FROM validators_summary\n WHERE att_valid_source = 0 AND val_nos_name = 'Allnodes' AND val_stuck = 0 AND epoch = 166141\n ORDER BY val_id\n LIMIT 1 by val_id\n LIMIT 100", "refId": "A", "round": "0s", "skip_comments": true