diff --git a/.gitignore b/.gitignore index 4f4f3bd..7d73194 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ dist .env.sisyphos config/ignore.json config/perseverance.json +config/sisyphos.json alerts.yaml \ No newline at end of file diff --git a/config/sisyphos.json b/config/sisyphos.json index 64ee2d6..0bb50c6 100644 --- a/config/sisyphos.json +++ b/config/sisyphos.json @@ -9,6 +9,7 @@ "flip": { "enabled": true, "network": "sisyphos", + "eventLog": false, "defaultMetrics": [], "accounts": [], "skipEvents": [ diff --git a/src/metrics/chainflip/gatherGlobalValues.ts b/src/metrics/chainflip/gatherGlobalValues.ts index 0851741..ee36dcc 100644 --- a/src/metrics/chainflip/gatherGlobalValues.ts +++ b/src/metrics/chainflip/gatherGlobalValues.ts @@ -4,15 +4,9 @@ export const gatherGlobalValues = async (context: Context): Promise => { const { logger, api, registry, metricFailure } = context; try { - const epoch: any = await api.query.validator.currentEpoch(); - global.epochIndex = Number(epoch); + global.epochIndex = Number(context.data.epoch.current_epoch_index); - const epochKey = await api.query.polkadotThresholdSigner.currentKeyEpoch(); - const dotAggKeyAddress = await api.query.polkadotThresholdSigner.keys(epochKey.toJSON()); - - if (dotAggKeyAddress) { - global.dotAggKeyAddress = dotAggKeyAddress.toJSON(); - } + global.dotAggKeyAddress = context.data.dot_aggkey; } catch (err) { logger.error(err); } diff --git a/src/metrics/chainflip/gaugeAuthorities.ts b/src/metrics/chainflip/gaugeAuthorities.ts index b8ac865..516d014 100644 --- a/src/metrics/chainflip/gaugeAuthorities.ts +++ b/src/metrics/chainflip/gaugeAuthorities.ts @@ -29,20 +29,10 @@ export const gaugeAuthorities = async (context: Context): Promise => { metricFailure.labels({ metric: metricName }).set(0); - let currentAuthorities: any; try { - let onlineCounter = 0; - - currentAuthorities = await api.query.validator.currentAuthorities(); - metric.set(currentAuthorities.toJSON().length); - global.currentAuthorities = currentAuthorities.toJSON().length; - for (const idSs58 of currentAuthorities.toJSON()) { - const result = await makeRpcRequest(api, 'account_info_v2', idSs58); - if (result.is_online) { - onlineCounter++; - } - } - metricOnline.set(onlineCounter); + metric.set(context.data.authorities.authorities); + global.currentAuthorities = context.data.authorities.authorities; + metricOnline.set(context.data.authorities.online_authorities); } catch (e) { logger.error(e); metricFailure.labels({ metric: metricName }).set(1); diff --git a/src/metrics/chainflip/gaugeBackupValidator.ts b/src/metrics/chainflip/gaugeBackupValidator.ts index 1849600..82c5de4 100644 --- a/src/metrics/chainflip/gaugeBackupValidator.ts +++ b/src/metrics/chainflip/gaugeBackupValidator.ts @@ -31,33 +31,8 @@ export const gaugeBackupValidator = async (context: Context): Promise => { let currentBackups: any; try { - let onlineCounter = 0; - let backupSetSize = 0; - - currentBackups = await api.query.validator.backups(); - const accountInfo: any[] = []; - - for (const idSs58 of currentBackups.keys()) { - const result = await makeRpcRequest(api, 'account_info_v2', idSs58); - if (result.is_current_backup) { - accountInfo.push(result); - } - } - - accountInfo.sort((a: any, b: any) => { - return Number(BigInt(b.balance) - BigInt(a.balance)); - }); - const accounts = accountInfo.slice(0, 50); - - for (const account of accounts) { - backupSetSize++; - if (account.is_online) { - onlineCounter++; - } - } - - metric.set(backupSetSize); - metricOnline.set(onlineCounter); + metric.set(context.data.authorities.backups); + metricOnline.set(context.data.authorities.online_backups); } catch (e) { logger.error(e); metricFailure.labels({ metric: metricName }).set(1); diff --git a/src/metrics/chainflip/gaugeBitcoinBalance.ts b/src/metrics/chainflip/gaugeBitcoinBalance.ts index 43806b0..a880735 100644 --- a/src/metrics/chainflip/gaugeBitcoinBalance.ts +++ b/src/metrics/chainflip/gaugeBitcoinBalance.ts @@ -21,11 +21,7 @@ export const gaugeBitcoinBalance = async (context: Context): Promise => { metricFailure.labels({ metric: metricName }).set(0); try { - const bitcoinAvailableUtxos: any = await api.query.environment.bitcoinAvailableUtxos(); - const aggregatedAmount = bitcoinAvailableUtxos.reduce((acc: number, utxo: any) => { - return acc + Number(utxo.amount); - }, 0); - metric.set(aggregatedAmount); + metric.set(context.data.btc_utxos.total_balance); } catch (err) { logger.error(err); metricFailure.labels({ metric: metricName }).set(1); diff --git a/src/metrics/chainflip/gaugeBlocksPerEpoch.ts b/src/metrics/chainflip/gaugeBlocksPerEpoch.ts index db04a6f..fd9334d 100644 --- a/src/metrics/chainflip/gaugeBlocksPerEpoch.ts +++ b/src/metrics/chainflip/gaugeBlocksPerEpoch.ts @@ -18,10 +18,8 @@ export const gaugeBlocksPerEpoch = async (context: Context): Promise => { if (registry.getSingleMetric(metricName) === undefined) registry.registerMetric(metric); metricFailure.labels({ metric: metricName }).set(0); - let blocksPerEpoch: any; try { - blocksPerEpoch = await api.query.validator.blocksPerEpoch(); - metric.set(blocksPerEpoch.toJSON()); + metric.set(context.data.epoch.blocks_per_epoch); } catch (e) { logger.error(e); metricFailure.labels({ metric: metricName }).set(1); diff --git a/src/metrics/chainflip/gaugeBtcUtxos.ts b/src/metrics/chainflip/gaugeBtcUtxos.ts index 1a66716..38ed28e 100644 --- a/src/metrics/chainflip/gaugeBtcUtxos.ts +++ b/src/metrics/chainflip/gaugeBtcUtxos.ts @@ -18,9 +18,8 @@ export const gaugeBtcUtxos = async (context: Context): Promise => { metricFailure.labels({ metric: metricName }).set(0); if (registry.getSingleMetric(metricName) === undefined) registry.registerMetric(metric); try { - const utxos: any = await api.query.environment.bitcoinAvailableUtxos(); - const metricValue: number = utxos.length; - metric.set(metricValue); + const utxos: any = context.data.btc_utxos.count; + metric.set(utxos); } catch (err) { logger.error(err); metricFailure.labels({ metric: metricName }).set(1); diff --git a/src/metrics/chainflip/gaugeCurrentEpochDurationBlocks.ts b/src/metrics/chainflip/gaugeCurrentEpochDurationBlocks.ts index ea2ce6c..0cba6d2 100644 --- a/src/metrics/chainflip/gaugeCurrentEpochDurationBlocks.ts +++ b/src/metrics/chainflip/gaugeCurrentEpochDurationBlocks.ts @@ -20,8 +20,8 @@ export const gaugeCurrentEpochDurationBlocks = async (context: Context): Promise metricFailure.labels({ metric: metricName }).set(0); try { - const currentEpochStartedAt: number = await api.query.validator.currentEpochStartedAt(); - const currentBlockHeight: number = await api.query.system.number(); + const currentEpochStartedAt: number = context.data.epoch.current_epoch_started_at; + const currentBlockHeight: number = context.header.number; const currentEpochDurationBlocks: number = currentBlockHeight - currentEpochStartedAt; metric.set(currentEpochDurationBlocks); diff --git a/src/metrics/chainflip/gaugeDepositChannels.ts b/src/metrics/chainflip/gaugeDepositChannels.ts index 9f49b18..5ea5965 100644 --- a/src/metrics/chainflip/gaugeDepositChannels.ts +++ b/src/metrics/chainflip/gaugeDepositChannels.ts @@ -12,61 +12,32 @@ const metric: Gauge = new promClient.Gauge({ registers: [], }); -const axios = new Axios({ - baseURL: env.PROCESSOR_ENDPOINT, - timeout: 6000, - headers: { - accept: 'application/json', - 'cache-control': 'no-cache', - 'content-type': 'application/json', - }, -}); - export const gaugeDepositChannels = async (context: Context): Promise => { if (context.config.skipMetrics.includes('cf_open_deposit_channels')) { return; } const { logger, api, registry, metricFailure } = context; logger.debug(`Scraping ${metricName}`); - const config = context.config as FlipConfig; - const { accounts } = config; if (registry.getSingleMetric(metricName) === undefined) registry.registerMetric(metric); metricFailure.labels({ metric: metricName }).set(0); try { - const data = await axios.post( - env.PROCESSOR_ENDPOINT, - `{"query":"query GetOpenSwapChannels {\\n channels: allSwapChannels(\\n condition: {isExpired: false}\\n orderBy: ISSUED_EVENT_ID_DESC\\n ) {\\n nodes {\\n sourceChain\\n }\\n }\\n}"}`, - ); - - const channels = JSON.parse(data.data).data.channels.nodes; - let ethChannels = 0; - let btcChannels = 0; - let dotChannels = 0; - let arbChannels = 0; - channels.forEach((channel: any) => { - switch (channel.sourceChain) { - case 'Ethereum': - ethChannels++; - break; - case 'Bitcoin': - btcChannels++; - break; - case 'Polkadot': - dotChannels++; - break; - case 'Arbitrum': - arbChannels++; - } - }); + // BTC + const btcChannels = context.data.open_deposit_channels.bitcoin; metric.labels('bitcoin').set(btcChannels); + // DOT + const dotChannels = context.data.open_deposit_channels.polkadot; metric.labels('polkadot').set(dotChannels); + // ETH + const ethChannels = context.data.open_deposit_channels.ethereum; metric.labels('ethereum').set(ethChannels); + // ARB + const arbChannels = context.data.open_deposit_channels.arbitrum; metric.labels('arbitrum').set(arbChannels); } catch (e) { logger.error(e); diff --git a/src/metrics/chainflip/gaugeFeeDeficit.ts b/src/metrics/chainflip/gaugeFeeDeficit.ts index bd3fd9c..5d425df 100644 --- a/src/metrics/chainflip/gaugeFeeDeficit.ts +++ b/src/metrics/chainflip/gaugeFeeDeficit.ts @@ -20,40 +20,51 @@ export const gaugeFeeDeficit = async (context: Context): Promise => { if (registry.getSingleMetric(metricName) === undefined) registry.registerMetric(metric); try { // ETH fees balance - const feeWitheldEth = Number( - (await api.query.ethereumIngressEgress.withheldTransactionFees('Eth')).toJSON(), - ); - const feeSpentEth = await api.query.ethereumBroadcaster.transactionFeeDeficit.entries(); - let totalSpent = 0; - feeSpentEth.forEach(([key, element]: [any, any]) => { - totalSpent += Number(element.toJSON()); - }); - const deficitEth = (feeWitheldEth - totalSpent) / 1e18; - metric.labels('ethereum').set(deficitEth); + const eth_fees = context.data.fee_imbalance.ethereum; + if (Object.hasOwn(eth_fees, 'Deficit')) { + // Deficit case + const metricValue = -(Number(eth_fees.Deficit) / 1e18); + metric.labels('ethereum').set(metricValue); + } else { + // Surplus case + const metricValue = Number(eth_fees.Surplus) / 1e18; + metric.labels('ethereum').set(metricValue); + } // ARB fees balance - const feeWitheldArb = Number( - (await api.query.arbitrumIngressEgress.withheldTransactionFees('ArbEth')).toJSON(), - ); - const feeSpentArb = await api.query.arbitrumBroadcaster.transactionFeeDeficit.entries(); - totalSpent = 0; - feeSpentArb.forEach(([key, element]: [any, any]) => { - totalSpent += Number(element.toJSON()); - }); - const deficitArb = (feeWitheldArb - totalSpent) / 1e18; - metric.labels('arbitrum').set(deficitArb); + const arb_fees = context.data.fee_imbalance.arbitrum; + if (Object.hasOwn(arb_fees, 'Deficit')) { + // Deficit case + const metricValue = -(Number(arb_fees.Deficit) / 1e18); + metric.labels('arbitrum').set(metricValue); + } else { + // Surplus case + const metricValue = Number(arb_fees.Surplus) / 1e18; + metric.labels('arbitrum').set(metricValue); + } // DOT fees balance - const feeWitheldDot = Number( - (await api.query.polkadotIngressEgress.withheldTransactionFees('Dot')).toJSON(), - ); - const feeSpentDot = await api.query.polkadotBroadcaster.transactionFeeDeficit.entries(); - totalSpent = 0; - feeSpentDot.forEach(([key, element]: [any, any]) => { - totalSpent += Number(element.toJSON()); - }); - const deficitDot = (feeWitheldDot - totalSpent) / 1e10; - metric.labels('polkadot').set(deficitDot); + const dot_fees = context.data.fee_imbalance.polkadot; + if (Object.hasOwn(dot_fees, 'Deficit')) { + // Deficit case + const metricValue = -(Number(dot_fees.Deficit) / 1e10); + metric.labels('polkadot').set(metricValue); + } else { + // Surplus case + const metricValue = Number(dot_fees.Deficit) / 1e10; + metric.labels('polkadot').set(metricValue); + } + // BTC fees balance + const btc_fees = context.data.fee_imbalance.bitcoin; + if (Object.hasOwn(btc_fees, 'Deficit')) { + // Deficit case + const metricValue = -(Number(btc_fees.Deficit) / 1e8); + metric.labels('bitcoin').set(metricValue); + } else { + // Surplus case + const metricValue = Number(btc_fees.Deficit) / 1e8; + metric.labels('bitcoin').set(metricValue); + } } catch (err) { logger.error(err); metricFailure.labels({ metric: metricName }).set(1); diff --git a/src/metrics/chainflip/gaugeFlipTotalSupply.ts b/src/metrics/chainflip/gaugeFlipTotalSupply.ts index 99d5d27..4b69662 100644 --- a/src/metrics/chainflip/gaugeFlipTotalSupply.ts +++ b/src/metrics/chainflip/gaugeFlipTotalSupply.ts @@ -18,7 +18,7 @@ export const gaugeFlipTotalSupply = async (context: Context): Promise => { metricFailure.labels({ metric: metricName }).set(0); if (registry.getSingleMetric(metricName) === undefined) registry.registerMetric(metric); try { - const totalSupply: bigint = await api.query.flip.totalIssuance(); + const totalSupply: bigint = context.data.flip_supply.total_supply; const metricValue: number = Number(Number(totalSupply) / 10 ** 18); metric.set(metricValue); } catch (err) { diff --git a/src/metrics/chainflip/gaugeMinActiveBid.ts b/src/metrics/chainflip/gaugeMinActiveBid.ts index b8ab5ab..7d066a8 100644 --- a/src/metrics/chainflip/gaugeMinActiveBid.ts +++ b/src/metrics/chainflip/gaugeMinActiveBid.ts @@ -22,8 +22,7 @@ export const gaugeMinActiveBid = async (context: Context): Promise => { metricFailure.labels({ metric: metricName }).set(0); try { - const result = await makeRpcRequest(api, 'auction_state'); - const { min_active_bid } = result; + const min_active_bid = context.data.epoch.min_active_bid; const MAB: number = Number(Number(min_active_bid) / 10 ** 18); diff --git a/src/metrics/chainflip/gaugePendingBroadcast.ts b/src/metrics/chainflip/gaugePendingBroadcast.ts index 864d858..28351f9 100644 --- a/src/metrics/chainflip/gaugePendingBroadcast.ts +++ b/src/metrics/chainflip/gaugePendingBroadcast.ts @@ -20,21 +20,17 @@ export const gaugePendingBroadcast = async (context: Context): Promise => metricFailure.labels({ metric: metricName }).set(0); try { - const dotQueue: any = await api.query.polkadotBroadcaster.pendingBroadcasts(); - const dotQueueLenght: number = dotQueue.size; - metric.labels('polkadot').set(dotQueueLenght); + // Polkadot + metric.labels('polkadot').set(context.data.pending_broadcasts.polkadot); - const btcQueue: any = await api.query.bitcoinBroadcaster.pendingBroadcasts(); - const btcQueueLenght: number = btcQueue.size; - metric.labels('bitcoin').set(btcQueueLenght); + // Bitcoin + metric.labels('bitcoin').set(context.data.pending_broadcasts.bitcoin); - const ethQueue: any = await api.query.ethereumBroadcaster.pendingBroadcasts(); - const ethQueueLenght: number = ethQueue.size; - metric.labels('ethereum').set(ethQueueLenght); + // Ethereum + metric.labels('ethereum').set(context.data.pending_broadcasts.ethereum); - const arbQueue: any = await api.query.arbitrumBroadcaster.pendingBroadcasts(); - const arbQueueLenght: number = arbQueue.size; - metric.labels('arbitrum').set(arbQueueLenght); + // Arbitrum + metric.labels('arbitrum').set(context.data.pending_broadcasts.arbitrum); } catch (err) { logger.error(err); metricFailure.labels({ metric: metricName }).set(1); diff --git a/src/metrics/chainflip/gaugePendingRedemptions.ts b/src/metrics/chainflip/gaugePendingRedemptions.ts index 8d48d18..5e7be3e 100644 --- a/src/metrics/chainflip/gaugePendingRedemptions.ts +++ b/src/metrics/chainflip/gaugePendingRedemptions.ts @@ -31,14 +31,12 @@ export const gaugePendingRedemptions = async (context: Context): Promise = logger.debug(`Scraping ${metricNamePendingRedemption}, ${metricNamePendingRedemptionBalance}`); try { - const pendingRedemptions = await api.query.funding.pendingRedemptions.entries(); - let totalRedemptionBalance: number = 0; - pendingRedemptions.forEach(([key, element]: [any, any]) => { - totalRedemptionBalance += parseInt(element.toJSON().total, 16) / 1e18; - }); + const pendingRedemptions = context.data.pending_redemptions.count; + const totalRedemptionBalance: number = + Number(context.data.pending_redemptions.total_balance) / 1e18; metricPendingRedemptionBalance.set(totalRedemptionBalance); - metricPendingRedemption.set(pendingRedemptions.length); + metricPendingRedemption.set(pendingRedemptions); } catch (e) { logger.error(e); metricFailure.labels({ metric: metricNamePendingRedemption }).set(1); diff --git a/src/metrics/chainflip/gaugeReputation.ts b/src/metrics/chainflip/gaugeReputation.ts deleted file mode 100644 index 4184740..0000000 --- a/src/metrics/chainflip/gaugeReputation.ts +++ /dev/null @@ -1,35 +0,0 @@ -import promClient, { Gauge } from 'prom-client'; -import { Context } from '../../lib/interfaces'; -import { FlipConfig } from '../../config/interfaces'; - -const metricName: string = 'cf_reputation'; -const metric: Gauge = new promClient.Gauge({ - name: metricName, - help: 'The reputation of a validator', - labelNames: ['ss58', 'alias'], - registers: [], -}); - -export const gaugeReputation = async (context: Context): Promise => { - if (context.config.skipMetrics.includes('cf_reputation')) { - return; - } - const { logger, api, registry, metricFailure } = context; - logger.debug(`Scraping ${metricName}`); - const config = context.config as FlipConfig; - const { accounts } = config; - - if (registry.getSingleMetric(metricName) === undefined) registry.registerMetric(metric); - - metricFailure.labels({ metric: metricName }).set(0); - - try { - for (const { ss58Address, alias } of accounts) { - const reputation = await api.query.reputation.reputations(ss58Address); - metric.labels(ss58Address, alias).set(Number(reputation.reputationPoints)); - } - } catch (e) { - logger.error(e); - metricFailure.labels({ metric: metricName }).set(1); - } -}; diff --git a/src/metrics/chainflip/gaugeRotationDuration.ts b/src/metrics/chainflip/gaugeRotationDuration.ts index 1db5a21..ffc40ec 100644 --- a/src/metrics/chainflip/gaugeRotationDuration.ts +++ b/src/metrics/chainflip/gaugeRotationDuration.ts @@ -44,46 +44,45 @@ export const gaugeRotationDuration = async (context: Context): Promise => metricFailure.labels({ metric: 'gaugeRotationDuration' }).set(0); try { - const currentRotationPhase: any = await api.query.validator.currentRotationPhase(); - const keys: string = Object.keys(currentRotationPhase.toJSON())[0]; + const currentRotationPhase: any = context.data.epoch.rotation_phase; - switch (keys) { - case 'idle': + switch (currentRotationPhase) { + case 'Idle': if (currentPhase !== rotationPhase.idle) { currentPhase = rotationPhase.idle; metricRotationDuration.reset(); metricRotationPhase.reset(); } break; - case 'keygensInProgress': + case 'KeygensInProgress': if (currentPhase !== rotationPhase.keygensInProgress) { currentPhase = rotationPhase.keygensInProgress; } metricRotationPhase.labels({ rotationPhase: 'keygensInProgress' }).inc(); metricRotationDuration.inc(); break; - case 'keyHandoversInProgress': + case 'KeyHandoversInProgress': if (currentPhase !== rotationPhase.keyHandoversInProgress) { currentPhase = rotationPhase.keyHandoversInProgress; } metricRotationPhase.labels({ rotationPhase: 'keyHandoversInProgress' }).inc(); metricRotationDuration.inc(); break; - case 'activatingKeys': + case 'ActivatingKeys': if (currentPhase !== rotationPhase.activatingKeys) { currentPhase = rotationPhase.activatingKeys; } metricRotationPhase.labels({ rotationPhase: 'activatingKeys' }).inc(); metricRotationDuration.inc(); break; - case 'newKeysActivated': + case 'NewKeysActivated': if (currentPhase !== rotationPhase.newKeysActivated) { currentPhase = rotationPhase.newKeysActivated; } metricRotationPhase.labels({ rotationPhase: 'newKeysActivated' }).inc(); metricRotationDuration.inc(); break; - case 'sessionRotating': + case 'SessionRotating': if (currentPhase !== rotationPhase.sessionRotating) { currentPhase = rotationPhase.sessionRotating; } diff --git a/src/metrics/chainflip/gaugeSuspendedValidator.ts b/src/metrics/chainflip/gaugeSuspendedValidator.ts index 7414d2c..b4b7a88 100644 --- a/src/metrics/chainflip/gaugeSuspendedValidator.ts +++ b/src/metrics/chainflip/gaugeSuspendedValidator.ts @@ -20,9 +20,9 @@ export const gaugeSuspendedValidator = async (context: Context): Promise = metricFailure.labels({ metric: metricName }).set(0); try { - const suspensionList: any = await api.query.reputation.suspensions.entries(); - suspensionList.forEach(([key, element]: [any, any]) => { - metric.labels({ offence: key.toHuman() }).set(element.length); + const suspensionList: any = context.data.suspended_validators; + suspensionList.forEach(([offence, count]: [any, any]) => { + metric.labels(offence).set(count); }); } catch (err) { logger.error(err); diff --git a/src/metrics/chainflip/gaugeSwappingQueue.ts b/src/metrics/chainflip/gaugeSwappingQueue.ts index f069d8c..bf01d18 100644 --- a/src/metrics/chainflip/gaugeSwappingQueue.ts +++ b/src/metrics/chainflip/gaugeSwappingQueue.ts @@ -19,8 +19,7 @@ export const gaugeSwappingQueue = async (context: Context): Promise => { metricFailure.labels({ metric: metricName }).set(0); try { - const swapQueue: any[] = await api.query.swapping.swapQueue.entries(); - const swapQueueLenght: number = swapQueue.length; + const swapQueueLenght: number = context.data.pending_swaps; metric.set(swapQueueLenght); } catch (err) { logger.error(err); diff --git a/src/metrics/chainflip/gaugeTssRetryQueues.ts b/src/metrics/chainflip/gaugeTssRetryQueues.ts index e8cb532..504ef52 100644 --- a/src/metrics/chainflip/gaugeTssRetryQueues.ts +++ b/src/metrics/chainflip/gaugeTssRetryQueues.ts @@ -1,18 +1,10 @@ import promClient, { Gauge } from 'prom-client'; import { Context } from '../../lib/interfaces'; -const metricNameRequestRetryQueue: string = 'cf_tss_request_retry_queue'; -const metricRequestRetryQueue: Gauge = new promClient.Gauge({ - name: metricNameRequestRetryQueue, - help: 'Size of the TSS request retry queue, it contains an entry for every request of TSS we receive if it gets rescheduled', - labelNames: ['tss'], - registers: [], -}); - -const metricNameCeremonyRetryQueue: string = 'cf_tss_ceremony_retry_queue'; -const metricPendingCeremonyRetryQueue: Gauge = new promClient.Gauge({ - name: metricNameCeremonyRetryQueue, - help: 'Size of the TSS retry queue, it contains an entry for every ceremony with a block at which it should be retried', +const metricNamePendingTss: string = 'cf_pending_tss'; +const metricPendingTss: Gauge = new promClient.Gauge({ + name: metricNamePendingTss, + help: 'Number of pending tss ceremonies', labelNames: ['tss'], registers: [], }); @@ -22,68 +14,23 @@ export const gaugeTssRetryQueues = async (context: Context): Promise => { return; } const { logger, api, registry, metricFailure } = context; - logger.debug(`Scraping ${metricNameRequestRetryQueue}, ${metricNameCeremonyRetryQueue}`); + logger.debug(`Scraping ${metricNamePendingTss}`); - if (registry.getSingleMetric(metricNameRequestRetryQueue) === undefined) - registry.registerMetric(metricRequestRetryQueue); - if (registry.getSingleMetric(metricNameCeremonyRetryQueue) === undefined) - registry.registerMetric(metricPendingCeremonyRetryQueue); - metricFailure.labels({ metric: metricNameRequestRetryQueue }).set(0); - metricFailure.labels({ metric: metricNameCeremonyRetryQueue }).set(0); + if (registry.getSingleMetric(metricNamePendingTss) === undefined) + registry.registerMetric(metricPendingTss); + metricFailure.labels({ metric: metricNamePendingTss }).set(0); try { - // requestRetryQueue - const dotRequestRetryQueue: any = - await api.query.polkadotThresholdSigner.requestRetryQueue.entries(); - let dotRequestRetryQueueLength: number = 0; - dotRequestRetryQueue.forEach(([key, element]: [any, any]) => { - dotRequestRetryQueueLength += Number(element.toHuman().length); - }); - metricRequestRetryQueue.labels('polkadot').set(dotRequestRetryQueueLength); - - const btcRequestRetryQueue: any = - await api.query.bitcoinThresholdSigner.requestRetryQueue.entries(); - let btcRequestRetryQueueLength: number = 0; - btcRequestRetryQueue.forEach(([key, element]: [any, any]) => { - btcRequestRetryQueueLength += Number(element.toHuman().length); - }); - metricRequestRetryQueue.labels('bitcoin').set(btcRequestRetryQueueLength); - - const ethRequestRetryQueue: any = - await api.query.evmThresholdSigner.requestRetryQueue.entries(); - let ethRequestRetryQueueLength: number = 0; - ethRequestRetryQueue.forEach(([key, element]: [any, any]) => { - ethRequestRetryQueueLength += Number(element.toHuman().length); - }); - metricRequestRetryQueue.labels('evm').set(ethRequestRetryQueueLength); - - // ceremonyRetryQueues - const dotCeremonyRetryQueue: any = - await api.query.polkadotThresholdSigner.ceremonyRetryQueues.entries(); - let dotCeremonyRetryQueueLength: number = 0; - dotCeremonyRetryQueue.forEach(([key, element]: [any, any]) => { - dotCeremonyRetryQueueLength += Number(element.toHuman().length); - }); - metricPendingCeremonyRetryQueue.labels('polkadot').set(dotCeremonyRetryQueueLength); + // EVM + metricPendingTss.labels('evm').set(context.data.pending_tss.evm); - const btcCeremonyRetryQueue: any = - await api.query.bitcoinThresholdSigner.ceremonyRetryQueues.entries(); - let btcCeremonyRetryQueueLength: number = 0; - btcCeremonyRetryQueue.forEach(([key, element]: [any, any]) => { - btcCeremonyRetryQueueLength += Number(element.toHuman().length); - }); - metricPendingCeremonyRetryQueue.labels('bitcoin').set(btcCeremonyRetryQueueLength); + // Bitcoin + metricPendingTss.labels('bitcoin').set(context.data.pending_tss.bitcoin); - const ethCeremonyRetryQueue: any = - await api.query.evmThresholdSigner.ceremonyRetryQueues.entries(); - let ethCeremonyRetryQueueLength: number = 0; - ethCeremonyRetryQueue.forEach(([key, element]: [any, any]) => { - ethCeremonyRetryQueueLength += Number(element.toHuman().length); - }); - metricPendingCeremonyRetryQueue.labels('evm').set(ethCeremonyRetryQueueLength); + // Polkadot + metricPendingTss.labels('polkadot').set(context.data.pending_tss.polkadot); } catch (err) { logger.error(err); - metricFailure.labels({ metric: metricNameRequestRetryQueue }).set(1); - metricFailure.labels({ metric: metricNameCeremonyRetryQueue }).set(1); + metricFailure.labels({ metric: metricNamePendingTss }).set(1); } }; diff --git a/src/metrics/chainflip/gaugeValidatorStatus.ts b/src/metrics/chainflip/gaugeValidatorStatus.ts index c4e68c5..7b93a34 100644 --- a/src/metrics/chainflip/gaugeValidatorStatus.ts +++ b/src/metrics/chainflip/gaugeValidatorStatus.ts @@ -45,7 +45,13 @@ const metricBalance: Gauge = new promClient.Gauge({ registers: [], labelNames: ['ss58Address', 'alias'], }); - +const metricNameReputation: string = 'cf_reputation'; +const metricReputation: Gauge = new promClient.Gauge({ + name: metricNameReputation, + help: 'The reputation of a validator', + labelNames: ['ss58', 'alias'], + registers: [], +}); export const gaugeValidatorStatus = async (context: Context): Promise => { if (context.config.skipMetrics.includes('cf_validator')) { return; @@ -58,7 +64,7 @@ export const gaugeValidatorStatus = async (context: Context): Promise => { } logger.debug( - `Scraping ${metricNameValidatorOnline}, ${metricNameValidatorAuthority}, ${metricNameValidatorBackup}, ${metricNameValidatorQualified}`, + `Scraping ${metricNameValidatorOnline}, ${metricNameValidatorAuthority}, ${metricNameValidatorBackup}, ${metricNameValidatorQualified}, ${metricNameReputation}`, ); if (registry.getSingleMetric(metricNameValidatorOnline) === undefined) @@ -73,6 +79,8 @@ export const gaugeValidatorStatus = async (context: Context): Promise => { registry.registerMetric(metricBidding); if (registry.getSingleMetric(metricNameValidatorBalance) === undefined) registry.registerMetric(metricBalance); + if (registry.getSingleMetric(metricNameReputation) === undefined) + registry.registerMetric(metricReputation); metricFailure.labels({ metric: metricNameValidatorOnline }).set(0); metricFailure.labels({ metric: metricNameValidatorAuthority }).set(0); @@ -80,10 +88,17 @@ export const gaugeValidatorStatus = async (context: Context): Promise => { metricFailure.labels({ metric: metricNameValidatorQualified }).set(0); metricFailure.labels({ metric: metricNameValidatorBidding }).set(0); metricFailure.labels({ metric: metricNameValidatorBalance }).set(0); + metricFailure.labels({ metric: metricNameReputation }).set(0); + const accounts = []; + const vanityNames = []; for (const { ss58Address, alias } of config.accounts) { - try { - const result = await makeRpcRequest(api, 'account_info_v2', ss58Address); + accounts.push(ss58Address); + vanityNames.push(alias); + } + try { + const result = await makeRpcRequest(api, 'monitoring_accounts_info', accounts); + for (const [i, validatorInfo] of result.entries()) { const { balance, is_current_authority, @@ -91,23 +106,26 @@ export const gaugeValidatorStatus = async (context: Context): Promise => { is_online, is_bidding, is_qualified, - } = result; - metricAuthorityOnline.labels({ alias, ss58Address }).set(is_online ? 1 : 0); - metricAuthority.labels({ alias, ss58Address }).set(is_current_authority ? 1 : 0); - metricBackup.labels({ alias, ss58Address }).set(is_current_backup ? 1 : 0); - metricQualified.labels({ alias, ss58Address }).set(is_qualified ? 1 : 0); - metricBidding.labels({ alias, ss58Address }).set(is_bidding ? 1 : 0); + reputation_points, + } = validatorInfo; + metricAuthorityOnline.labels(accounts[i], vanityNames[i]).set(is_online ? 1 : 0); + metricAuthority.labels(accounts[i], vanityNames[i]).set(is_current_authority ? 1 : 0); + metricBackup.labels(accounts[i], vanityNames[i]).set(is_current_backup ? 1 : 0); + metricQualified.labels(accounts[i], vanityNames[i]).set(is_qualified ? 1 : 0); + metricBidding.labels(accounts[i], vanityNames[i]).set(is_bidding ? 1 : 0); + metricReputation.labels(accounts[i], vanityNames[i]).set(reputation_points); const balanceValue: number = Number(Number(balance) / 10 ** 18); - metricBalance.labels({ alias, ss58Address }).set(balanceValue || 0); - } catch (e) { - logger.error(e); - metricFailure.labels({ metric: metricNameValidatorOnline }).set(1); - metricFailure.labels({ metric: metricNameValidatorAuthority }).set(1); - metricFailure.labels({ metric: metricNameValidatorBackup }).set(1); - metricFailure.labels({ metric: metricNameValidatorQualified }).set(1); - metricFailure.labels({ metric: metricNameValidatorBidding }).set(1); - metricFailure.labels({ metric: metricNameValidatorBalance }).set(1); + metricBalance.labels(accounts[i], vanityNames[i]).set(balanceValue || 0); } + } catch (e) { + logger.error(e); + metricFailure.labels({ metric: metricNameValidatorOnline }).set(1); + metricFailure.labels({ metric: metricNameValidatorAuthority }).set(1); + metricFailure.labels({ metric: metricNameValidatorBackup }).set(1); + metricFailure.labels({ metric: metricNameValidatorQualified }).set(1); + metricFailure.labels({ metric: metricNameValidatorBidding }).set(1); + metricFailure.labels({ metric: metricNameValidatorBalance }).set(1); + metricFailure.labels({ metric: metricNameReputation }).set(1); } }; diff --git a/src/metrics/chainflip/guageExternalChainsBlockHeight.ts b/src/metrics/chainflip/guageExternalChainsBlockHeight.ts index b0c0a44..b12b9ca 100644 --- a/src/metrics/chainflip/guageExternalChainsBlockHeight.ts +++ b/src/metrics/chainflip/guageExternalChainsBlockHeight.ts @@ -21,32 +21,16 @@ export const gaugeExternalChainsBlockHeight = async (context: Context) => { try { // Ethereum - const ethBlockHeight = Number( - (await api.query.ethereumChainTracking.currentChainState()).toJSON().blockHeight, - ); - global.ethHeight = ethBlockHeight; - metric.labels('ethereum').set(ethBlockHeight); + metric.labels('ethereum').set(context.data.external_chains_height.ethereum); // Bitcoin - const btcBlockHeight = Number( - (await api.query.bitcoinChainTracking.currentChainState()).toJSON().blockHeight, - ); - global.btcHeight = btcBlockHeight; - metric.labels('bitcoin').set(btcBlockHeight); + metric.labels('bitcoin').set(context.data.external_chains_height.bitcoin); // Polkadot - const dotBlockHeight = Number( - (await api.query.polkadotChainTracking.currentChainState()).toJSON().blockHeight, - ); - global.dotHeight = dotBlockHeight; - metric.labels('polkadot').set(dotBlockHeight); + metric.labels('polkadot').set(context.data.external_chains_height.polkadot); // Arbitrum - const arbBlockHeight = Number( - (await api.query.arbitrumChainTracking.currentChainState()).toJSON().blockHeight, - ); - global.arbHeight = arbBlockHeight; - metric.labels('arbitrum').set(arbBlockHeight); + metric.labels('arbitrum').set(context.data.external_chains_height.arbitrum); } catch (e) { logger.error(e); metricFailure.labels({ metric: metricName }).set(1); diff --git a/src/metrics/chainflip/index.ts b/src/metrics/chainflip/index.ts index bb2da79..f0e77aa 100644 --- a/src/metrics/chainflip/index.ts +++ b/src/metrics/chainflip/index.ts @@ -9,7 +9,6 @@ export * from './gaugeRotationDuration'; export * from './gaugeSuspendedValidator'; export * from './guageBlockHeight'; export * from './gaugeBackupValidator'; -export * from './gaugeReputation'; export * from './gaugeBuildVersion'; export * from './gaugeBlockWeight'; export * from './gaugePendingRedemptions'; diff --git a/src/utils/customRpcSpecification.ts b/src/utils/customRpcSpecification.ts index daee8db..5c7f0a3 100644 --- a/src/utils/customRpcSpecification.ts +++ b/src/utils/customRpcSpecification.ts @@ -119,9 +119,29 @@ export const customRpcs = { name: 'hash', type: 'String', }, + { + name: 'epoch_index', + type: 'Option', + isOptional: true, + }, ], type: 'RpcFailingWitnessValidators', description: '', }, + monitoring_data: { + params: [], + type: 'RpcMonitoringData', + description: '', + }, + monitoring_accounts_info: { + params: [ + { + name: 'accounts', + type: 'Vec', + }, + ], + type: 'RpcMonitoringAccountInfo', + description: '', + }, }, }; diff --git a/src/utils/makeRpcRequest.ts b/src/utils/makeRpcRequest.ts index 1c0dc32..535f1ed 100644 --- a/src/utils/makeRpcRequest.ts +++ b/src/utils/makeRpcRequest.ts @@ -82,6 +82,88 @@ const validators = { failing_count: U32, validators: z.array(z.tuple([string, string, boolean])), }), + monitoring_data: z.object({ + external_chains_height: z.object({ + bitcoin: U32, + ethereum: U32, + polkadot: U32, + arbitrum: U32, + solana: U32, + }), + btc_utxos: z.object({ + total_balance: U128, + count: U32, + }), + epoch: z.object({ + blocks_per_epoch: U32, + current_epoch_started_at: U32, + current_epoch_index: U32, + min_active_bid: z.optional(U128), + rotation_phase: string, + }), + pending_redemptions: z.object({ + total_balance: U128, // maybe u128 + count: U32, + }), + pending_broadcasts: z.object({ + ethereum: U32, + bitcoin: U32, + polkadot: U32, + arbitrum: U32, + solana: U32, + }), + pending_tss: z.object({ + evm: U32, + bitcoin: U32, + polkadot: U32, + solana: U32, + }), + open_deposit_channels: z.object({ + ethereum: U32, + bitcoin: U32, + polkadot: U32, + arbitrum: U32, + solana: U32, + }), + fee_imbalance: z.object({ + ethereum: U32, + bitcoin: U32, + polkadot: U32, + arbitrum: U32, + solana: U32, + }), + authorities: z.object({ + authorities: U32, + online_authorities: U32, + backups: U32, + online_backups: U32, + }), + build_version: z.object({ + spec_version: U32, + spec_name: string, + }), + suspended_validators: z.array(Offence, U32), + pending_swaps: U32, + dot_aggkey: string, + flip_supply: z.object({ + total_supply: U128, + offchain_supply: U128, + }), + }), + monitoring_accounts_info: z.array( + z.object({ + balance: U128, + bond: U128, + last_heartbeat: U32, + reputation_points: I32, + keyholder_epochs: z.array(U32), + is_current_authority: boolean, + is_current_backup: boolean, + is_qualified: boolean, + is_online: boolean, + is_bidding: boolean, + }), + ), } as const; type RpcParamsMap = { @@ -96,7 +178,9 @@ type RpcParamsMap = { eth_key_manager_address: []; eth_state_chain_gateway_address: []; flip_supply: []; - witness_count: [hash: string]; + witness_count: [hash: string, epoch_index?: number]; + monitoring_data: []; + monitoring_accounts_info: [accounts: string[]]; }; type RpcCall = keyof RpcParamsMap & keyof typeof validators & keyof typeof customRpcs.cf; diff --git a/src/watchers/chainflip.ts b/src/watchers/chainflip.ts index 53fc1b9..db9c4ae 100644 --- a/src/watchers/chainflip.ts +++ b/src/watchers/chainflip.ts @@ -2,38 +2,37 @@ import { Context } from '../lib/interfaces'; import promClient from 'prom-client'; import { countEvents, + eventsRotationInfo, + gatherGlobalValues, gaugeAuthorities, + gaugeBackupValidator, gaugeBitcoinBalance, gaugeBlockHeight, gaugeBlocksPerEpoch, + gaugeBtcUtxos, + gaugeBuildVersion, gaugeCurrentEpochDurationBlocks, + gaugeDepositChannels, + gaugeExternalChainsBlockHeight, + gaugeFeeDeficit, gaugeFlipTotalSupply, + gaugeMinActiveBid, + gaugePendingBroadcast, + gaugePendingRedemptions, + gaugePriceDelta, gaugeRotating, gaugeRotationDuration, gaugeSuspendedValidator, - gaugeBackupValidator, - gaugeReputation, - gaugeBuildVersion, - gaugeBlockWeight, - gaugePendingRedemptions, - gaugeValidatorStatus, - gaugeMinActiveBid, - eventsRotationInfo, - gaugeTssRetryQueues, gaugeSwappingQueue, - gaugeBtcUtxos, - gaugePendingBroadcast, - gatherGlobalValues, + gaugeTssRetryQueues, + gaugeValidatorStatus, gaugeWitnessChainTracking, gaugeWitnessCount, - gaugeFeeDeficit, - gaugeExternalChainsBlockHeight, - gaugePriceDelta, - gaugeDepositChannels, } from '../metrics/chainflip'; import { ApiPromise, WsProvider } from '@polkadot/api'; import { customRpcs } from '../utils/customRpcSpecification'; import stateChainTypes from '../utils/chainTypes'; +import makeRpcRequest from '../utils/makeRpcRequest'; const metricFailureName: string = 'metric_scrape_failure'; const metricFailure: promClient.Gauge = new promClient.Gauge({ @@ -56,10 +55,6 @@ declare global { var dotAggKeyAddress: string; var currentBlock: number; var currentAuthorities: number; - var btcHeight: number; - var ethHeight: number; - var dotHeight: number; - var arbHeight: number; interface CustomApiPromise extends ApiPromise { rpc: ApiPromise['rpc'] & { @@ -94,7 +89,7 @@ async function startWatcher(context: Context) { logger.error(`ws connection closed ${err}`); metric.set(1); }); - const api: ApiPromise = await ApiPromise.create({ + const api: any = await ApiPromise.create({ provider, noInitWarn: true, types: stateChainTypes as DeepMutable, @@ -102,7 +97,8 @@ async function startWatcher(context: Context) { }); context.api = api; - await api.rpc.chain.subscribeNewHeads(async (header) => { + await api.rpc.chain.subscribeNewHeads(async (header: any) => { + context.data = await makeRpcRequest(api, 'monitoring_data'); gatherGlobalValues(context); gaugeBlockHeight({ ...context, header }); gaugeAuthorities(context); @@ -110,13 +106,12 @@ async function startWatcher(context: Context) { gaugeWitnessCount(context); gaugeExternalChainsBlockHeight(context); gaugeBitcoinBalance(context); - gaugeCurrentEpochDurationBlocks(context); + gaugeCurrentEpochDurationBlocks({ ...context, header }); gaugeBlocksPerEpoch(context); gaugeSuspendedValidator(context); gaugeFlipTotalSupply(context); gaugeRotationDuration(context); gaugeBackupValidator(context); - gaugeReputation(context); gaugeBuildVersion(context); gaugeValidatorStatus(context); gaugeMinActiveBid(context);