|
| 1 | +import Prometheus from "prom-client"; |
| 2 | +import toml from "toml"; |
| 3 | +import { Command } from 'commander'; |
| 4 | +import fs from 'fs' |
| 5 | +import { fetchWithTimeoutAndFallback, urlResolve } from "./helper"; |
| 6 | +const program = new Command(); |
| 7 | +const freezedProviders = new Prometheus.Gauge({ |
| 8 | + name: 'provider_frozen', |
| 9 | + help: 'Freezed status of a provider. 1 for frozen, 0 can be not frozen or not found in providers response.', |
| 10 | + labelNames: ['chain'], |
| 11 | +}); |
| 12 | +type Config = { |
| 13 | + lava_rest_api: string[], |
| 14 | + chains: string[], |
| 15 | + lava_provider_address: string, |
| 16 | +} |
| 17 | +async function getFrozenChains(config: Config) { |
| 18 | + const frozenChains = []; |
| 19 | + const latestBlockUrl = config.lava_rest_api.map((url) => urlResolve(url, '/cosmos/base/tendermint/v1beta1/blocks/latest')); |
| 20 | + const latestBlockResponse = await fetchWithTimeoutAndFallback(latestBlockUrl, 5000) as any; |
| 21 | + const latestHeight = latestBlockResponse.block.header.height; |
| 22 | + for (const chain of config.chains) { |
| 23 | + const providerStatusUrl = config.lava_rest_api.map((url) => urlResolve(url, `/lavanet/lava/pairing/providers/${chain}?showFrozen=true`)); |
| 24 | + const providerStatusResponse = await fetchWithTimeoutAndFallback(providerStatusUrl, 5000) as any; |
| 25 | + const frozenTotal = providerStatusResponse.stakeEntry.filter((provider: { stake_applied_block: string; }) => Number(provider.stake_applied_block) > Number(latestHeight)) |
| 26 | + const frozenProvider = frozenTotal.find((provider: { address: string; }) => config.lava_provider_address === provider.address); |
| 27 | + if (frozenProvider) { |
| 28 | + frozenChains.push(chain); |
| 29 | + } |
| 30 | + } |
| 31 | + return frozenChains; |
| 32 | +} |
| 33 | + |
| 34 | +program |
| 35 | + .name('cosmos-exporter') |
| 36 | + .description('Prometheus lava exporter') |
| 37 | + .version('1.0.0'); |
| 38 | + |
| 39 | +program.command('start') |
| 40 | + .description('run exporter') |
| 41 | + .option('--config <config>', 'Path to config file', './config.toml') |
| 42 | + .option('-p, --port <port>', 'port', '5000') |
| 43 | + .action(async (opt) => { |
| 44 | + const tomlConfig = toml.parse(fs.readFileSync(opt.config, "utf-8")) as Config; |
| 45 | + |
| 46 | + Bun.serve({ |
| 47 | + port: opt.port, |
| 48 | + async fetch(req) { |
| 49 | + const frozenChains = await getFrozenChains(tomlConfig); |
| 50 | + tomlConfig.chains.forEach((chain) => { |
| 51 | + freezedProviders.labels({ chain }).set(0); |
| 52 | + }); |
| 53 | + frozenChains.forEach((chain) => { |
| 54 | + freezedProviders.labels({ chain }).set(1); |
| 55 | + }); |
| 56 | + req.headers.set('Content-Type', Prometheus.register.contentType); |
| 57 | + const metrics = await Prometheus.register.metrics(); |
| 58 | + return new Response(metrics) |
| 59 | + }, |
| 60 | + }); |
| 61 | + console.log('RUN WITH CONFIG:') |
| 62 | + console.log(tomlConfig) |
| 63 | + console.log('listening on port ' + opt.port) |
| 64 | + }) |
| 65 | + |
| 66 | +program.parse(process.argv); |
0 commit comments