From c84338f16060b544b99beaf11d1cb533de17823c Mon Sep 17 00:00:00 2001 From: katelyn martin Date: Mon, 29 Apr 2024 09:37:19 -0400 Subject: [PATCH] =?UTF-8?q?pcli:=20=F0=9F=8F=93=20`query=20validator=20sta?= =?UTF-8?q?tus`=20prints=20a=20table?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit this commit addresses @erwanor's review comment: https://github.com/penumbra-zone/penumbra/pull/4263#discussion_r1582520469 > Frankly I would prefer that we do this in this pr, and finish this > ticket. The cumulative voting share does not matter. The reason this > command is important is that we have no way to observe validator > identities that are not part of the main staking indexed since we > merged #3846. My suggestion was about rendering the data in a table > rather than pretty-printing a toml. The command is useful if we just > render: > > * validator info (name / IK) > > * state > > * validator exchange rate > > * bonding state > > * voting power > > > The rest is bonus and completely auxiliary. What matters is that > there's some way to observe validator state. What we should tackle in > a separate issue is discoverability of validator identities. this commit prints the validator information in a table, including: * Voting Power * Commission * State * Bonding State * Exchange Rate * Identity Key * Name --- .../bin/pcli/src/command/query/validator.rs | 115 ++++++++++++++++-- 1 file changed, 107 insertions(+), 8 deletions(-) diff --git a/crates/bin/pcli/src/command/query/validator.rs b/crates/bin/pcli/src/command/query/validator.rs index c34237027a..c34826b6cc 100644 --- a/crates/bin/pcli/src/command/query/validator.rs +++ b/crates/bin/pcli/src/command/query/validator.rs @@ -1,11 +1,16 @@ -use std::{fs::File, io::Write, ops::RangeInclusive, time::Duration}; +use std::{ + fs::File, + io::Write, + ops::{Deref, RangeInclusive}, + time::Duration, +}; use anyhow::{anyhow, Context, Error, Result}; use colored::Colorize; use comfy_table::{presets, Table}; use futures::TryStreamExt; use penumbra_app::params::AppParameters; -use penumbra_num::Amount; +use penumbra_num::{fixpoint::U128x128, Amount}; use penumbra_proto::{ core::{ app::v1::{ @@ -20,8 +25,9 @@ use penumbra_proto::{ DomainType, }; use penumbra_stake::{ - validator::{self, Info, ValidatorToml}, - IdentityKey, Uptime, + rate::RateData, + validator::{self, Info, Status, Validator, ValidatorToml}, + IdentityKey, Uptime, BPS_SQUARED_SCALING_FACTOR, }; use crate::App; @@ -363,17 +369,110 @@ impl ValidatorCmd { // Parse the validator status, or return an error if it was not found within the // client's response. - let Info { status, .. } = validator_info + let info = validator_info .ok_or_else(|| anyhow!("response did not include validator info"))? .try_into() .context("parsing validator info")?; - // Serialize the status to TOML, and print it to stdout. - let toml = toml::to_string_pretty(&status)?; - println!("{}", toml); + // Initialize a table, add a header and insert this validator's information. + let mut table = Table::new(); + table + .load_preset(presets::NOTHING) + .set_header(vec![ + "Voting Power", + "Commission", + "State", + "Bonding State", + "Exchange Rate", + "Identity Key", + "Name", + ]) + .add_row(StatusRow::new(info)); + println!("{table}"); } } Ok(()) } } + +/// A row within the `status` command's table output. +struct StatusRow { + power: f64, + commission: u16, + state: validator::State, + bonding_state: validator::BondingState, + exchange_rate: U128x128, + identity_key: IdentityKey, + name: String, +} + +impl StatusRow { + /// Constructs a new [`StatusRow`]. + fn new( + Info { + validator: + Validator { + funding_streams, + identity_key, + name, + .. + }, + status: + Status { + state, + bonding_state, + voting_power, + .. + }, + rate_data: + RateData { + validator_exchange_rate, + .. + }, + }: Info, + ) -> Self { + // Calculate the scaled voting power, exchange rate, and commissions. + let power = (voting_power.value() as f64) * 1e-6; + let commission = funding_streams.iter().map(|fs| fs.rate_bps()).sum(); + let exchange_rate = { + let rate_bps_sq = U128x128::from(validator_exchange_rate); + (rate_bps_sq / BPS_SQUARED_SCALING_FACTOR.deref()).expect("nonzero scaling factor") + }; + + Self { + power, + commission, + state, + bonding_state, + exchange_rate, + identity_key, + name, + } + } +} + +impl Into for StatusRow { + fn into(self) -> comfy_table::Row { + let Self { + power, + commission, + state, + bonding_state, + exchange_rate, + identity_key, + name, + } = self; + + [ + format!("{power:.3}"), + format!("{commission}bps"), + state.to_string(), + bonding_state.to_string(), + exchange_rate.to_string(), + identity_key.to_string(), + name, + ] + .into() + } +}