diff --git a/src/main.rs b/src/main.rs index 73370a1..446eeda 100644 --- a/src/main.rs +++ b/src/main.rs @@ -84,9 +84,16 @@ async fn main() -> anyhow::Result<()> { .query_future_bonds_and_unbonds(epoch) .await .into_retry_error()?; + let validators = rpc.query_validators(epoch).await.into_retry_error()?; let mut post_state_lock = state.write().await; - post_state_lock.update(block, total_supply_native, future_bonds, future_unbonds); + post_state_lock.update( + block, + total_supply_native, + future_bonds, + future_unbonds, + validators, + ); let post_state = post_state_lock.clone(); drop(post_state_lock); diff --git a/src/rpc.rs b/src/rpc.rs index bc39076..9267adf 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -10,7 +10,7 @@ use tendermint_rpc::{HttpClient, Url}; use crate::shared::{ checksums::Checksums, - namada::{Address, Block, Epoch, Height}, + namada::{Address, Block, Epoch, Height, Validator}, }; pub struct Rpc { @@ -78,6 +78,26 @@ impl Rpc { .context("Should be able to query for block") } + pub async fn query_validators(&self, epoch: Epoch) -> anyhow::Result> { + let futures = self + .clients + .iter() + .map(|client| rpc::get_all_consensus_validators(client, NamadaEpoch(epoch)).boxed()); + + let (res, _ready_future_index, _remaining_futures) = + futures::future::select_all(futures).await; + + res.context("Should be able to query native token") + .map(|set| { + set.into_iter() + .map(|validator| Validator { + address: validator.address.to_string(), + voting_power: validator.bonded_stake.raw_amount().as_u64(), + }) + .collect() + }) + } + pub async fn query_native_token(&self) -> anyhow::Result
{ let futures = self .clients diff --git a/src/shared/namada.rs b/src/shared/namada.rs index 2c902b2..7ff70e9 100644 --- a/src/shared/namada.rs +++ b/src/shared/namada.rs @@ -18,6 +18,12 @@ pub type Epoch = u64; pub type TxId = String; pub type Address = String; +#[derive(Clone, Debug)] +pub struct Validator { + pub address: String, + pub voting_power: u64, +} + #[derive(Clone, Debug)] pub struct Block { pub height: Height, diff --git a/src/state.rs b/src/state.rs index 6e3aaed..8045b55 100644 --- a/src/state.rs +++ b/src/state.rs @@ -3,12 +3,12 @@ use std::num::NonZeroUsize; use lru::LruCache; use prometheus_exporter::prometheus::{ core::{AtomicU64, GenericCounter, GenericCounterVec}, - Histogram, HistogramOpts, IntCounterVec, Opts, Registry, + GaugeVec, Histogram, HistogramOpts, IntCounterVec, Opts, Registry, }; use crate::shared::{ checksums::Checksums, - namada::{Block, Height}, + namada::{Block, Height, Validator}, }; #[derive(Debug, Clone)] @@ -26,10 +26,11 @@ pub struct PrometheusMetrics { pub block_height_counter: GenericCounter, pub epoch_counter: GenericCounter, pub total_supply_native_token: GenericCounter, + pub one_third_threshold: GaugeVec, + pub two_third_threshold: GaugeVec, pub transaction_size: Histogram, - pub transaction_inner_size: Histogram, - pub bonds_per_epoch: GenericCounterVec, - pub unbonds_per_epoch: GenericCounterVec, + pub bonds_per_epoch: GaugeVec, + pub unbonds_per_epoch: GaugeVec, pub transaction_kind: GenericCounterVec, registry: Registry, } @@ -51,6 +52,20 @@ impl PrometheusMetrics { let epoch_counter = GenericCounter::::new("epoch", "the latest epoch recorded") .expect("unable to create counter epoch"); + let one_third_threshold_opts = Opts::new( + "one_third_threshold", + "The number of validators to reach 1/3 of the voting power", + ); + let one_third_threshold = GaugeVec::new(one_third_threshold_opts, &["epoch"]) + .expect("unable to create counter one third threshold"); + + let two_third_threshold_opts = Opts::new( + "two_third_threshold", + "The number of validators to reach 2/3 of the voting power", + ); + let two_third_threshold = GaugeVec::new(two_third_threshold_opts, &["epoch"]) + .expect("unable to create counter two third threshold"); + let total_supply_native_token = GenericCounter::::new( "total_supply_native_token", "the latest total supply native token recorded", @@ -58,27 +73,19 @@ impl PrometheusMetrics { .expect("unable to create counter total supply"); let transaction_size_opts = HistogramOpts::new( - "transaction_size_bytes", - "The sizes of transactions in bytes", + "transaction_batch_size", + "The number of inner transactions in a batch", ) - .buckets(vec![ - 10.0, 50.0, 100.0, 500.0, 1000.0, 5000.0, 10000.0, 50000.0, - ]); + .buckets(vec![1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0]); let transaction_size = Histogram::with_opts(transaction_size_opts) .expect("unable to create histogram transaction sizes"); - let transaction_inner_size_opts = - HistogramOpts::new("transaction_inners", "The number of inner tx for a wrapper") - .buckets(vec![2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0]); - let transaction_inner_size = Histogram::with_opts(transaction_inner_size_opts) - .expect("unable to create histogram transaction sizes"); - let bonds_per_epoch_opts = Opts::new("bonds_per_epoch", "Total bonds per epoch"); - let bonds_per_epoch = IntCounterVec::new(bonds_per_epoch_opts, &["epoch"]) + let bonds_per_epoch = GaugeVec::new(bonds_per_epoch_opts, &["epoch"]) .expect("unable to create histogram transaction sizes"); let unbonds_per_epoch_opts = Opts::new("unbonds_per_epoch", "Total unbonds per epoch"); - let unbonds_per_epoch = IntCounterVec::new(unbonds_per_epoch_opts, &["epoch"]) + let unbonds_per_epoch = GaugeVec::new(unbonds_per_epoch_opts, &["epoch"]) .expect("unable to create histogram transaction sizes"); let transaction_kind_opts = @@ -94,6 +101,12 @@ impl PrometheusMetrics { registry .register(Box::new(total_supply_native_token.clone())) .unwrap(); + registry + .register(Box::new(one_third_threshold.clone())) + .unwrap(); + registry + .register(Box::new(two_third_threshold.clone())) + .unwrap(); registry .register(Box::new(transaction_size.clone())) .unwrap(); @@ -111,8 +124,9 @@ impl PrometheusMetrics { block_height_counter, epoch_counter, total_supply_native_token, + one_third_threshold, + two_third_threshold, transaction_size, - transaction_inner_size, bonds_per_epoch, unbonds_per_epoch, transaction_kind, @@ -153,6 +167,7 @@ impl State { total_supply_native: u64, future_bonds: u64, future_unbonds: u64, + mut validators: Vec, ) { if let Some(height) = self.latest_block_height { self.metrics @@ -185,7 +200,7 @@ impl State { for tx in &block.transactions { self.metrics - .transaction_inner_size + .transaction_size .observe(tx.inners.len() as f64); } @@ -201,30 +216,49 @@ impl State { &block.height.to_string(), ]) .inc(); - - self.metrics - .transaction_size - .observe(inner.kind.size() as f64); } } + validators.sort_by_key(|validator| validator.voting_power); + validators.reverse(); + let total_voting_power = validators + .iter() + .map(|validator| validator.voting_power) + .sum::(); + let one_third_voting_power = total_voting_power / 3; + let two_third_voting_power = total_voting_power * 2 / 3; + let (one_third_threshold, _) = validators.iter().fold((0, 0), |(index, acc), validator| { + if acc >= one_third_voting_power { + (index, acc) + } else { + (index + 1, acc + validator.voting_power) + } + }); + + let (two_third_threshold, _) = validators.iter().fold((0, 0), |(index, acc), validator| { + if acc >= two_third_voting_power { + (index, acc) + } else { + (index + 1, acc + validator.voting_power) + } + }); self.metrics - .bonds_per_epoch - .with_label_values(&[&(block.epoch + 1).to_string()]) - .reset(); + .one_third_threshold + .with_label_values(&[&block.epoch.to_string()]) + .set(one_third_threshold as f64); self.metrics - .bonds_per_epoch - .with_label_values(&[&(block.epoch + 1).to_string()]) - .inc_by(future_bonds); + .two_third_threshold + .with_label_values(&[&block.epoch.to_string()]) + .set(two_third_threshold as f64); self.metrics - .unbonds_per_epoch + .bonds_per_epoch .with_label_values(&[&(block.epoch + 1).to_string()]) - .reset(); + .set(future_bonds as f64); self.metrics .unbonds_per_epoch .with_label_values(&[&(block.epoch + 1).to_string()]) - .inc_by(future_unbonds); + .set(future_unbonds as f64); self.blocks.put(block.height, block); }