From 4a375acebc2211388b555d03f2f52539e3391e0a Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 20 Apr 2021 00:42:19 +0000 Subject: [PATCH] Add --sort argument to `solana validators` (backport #16640) (#16655) * Add --sort argument to `solana validators` (cherry picked from commit b66faf7e805179e010add572beb377e91b6dd384) * Add line numbers to `solana validators` output (cherry picked from commit 818c3198c1d7c08d0c1310ff6390a8f42e1a29da) * Print the header as a footer when there's a large number of validators to show (cherry picked from commit 1824b5a2ce9fb98bbe167f21a39527cbe96079f3) * Add --number argument (cherry picked from commit f14cf3ed1af5f24ad0f30b743d8bbde0315d2a6c) * Prefix current validators with nbsp for easier sed-ing (cherry picked from commit 568438aa6f48900058319346a5542016c05bacd3) Co-authored-by: Michael Vines --- cli-output/src/cli_output.rs | 138 ++++++++++++++++++++++++++--------- cli/src/cli.rs | 22 +++++- cli/src/cluster_query.rs | 76 ++++++++++++++++--- 3 files changed, 189 insertions(+), 47 deletions(-) diff --git a/cli-output/src/cli_output.rs b/cli-output/src/cli_output.rs index 235a8dde0d..401f8e59e0 100644 --- a/cli-output/src/cli_output.rs +++ b/cli-output/src/cli_output.rs @@ -303,14 +303,31 @@ pub struct CliValidatorsStakeByVersion { pub delinquent_active_stake: u64, } +#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)] +pub enum CliValidatorsSortOrder { + Delinquent, + Commission, + EpochCredits, + Identity, + LastVote, + Root, + Stake, + VoteAccount, +} + #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct CliValidators { pub total_active_stake: u64, pub total_current_stake: u64, pub total_delinquent_stake: u64, - pub current_validators: Vec, - pub delinquent_validators: Vec, + pub validators: Vec, + #[serde(skip_serializing)] + pub validators_sort_order: CliValidatorsSortOrder, + #[serde(skip_serializing)] + pub validators_reverse_sort: bool, + #[serde(skip_serializing)] + pub number_validators: bool, pub stake_by_version: BTreeMap, #[serde(skip_serializing)] pub use_lamports_unit: bool, @@ -326,7 +343,6 @@ impl fmt::Display for CliValidators { validator: &CliValidator, total_active_stake: u64, use_lamports_unit: bool, - delinquent: bool, ) -> fmt::Result { fn non_zero_or_dash(v: u64) -> String { if v == 0 { @@ -339,10 +355,10 @@ impl fmt::Display for CliValidators { writeln!( f, "{} {:<44} {:<44} {:>3}% {:>8} {:>10} {:>13} {:>7} {}", - if delinquent { + if validator.delinquent { WARNING.to_string() } else { - " ".to_string() + "\u{a0}".to_string() }, validator.identity_pubkey, validator.vote_account_pubkey, @@ -362,39 +378,75 @@ impl fmt::Display for CliValidators { }, ) } - writeln!( - f, - "{}", - style(format!( - " {:<44} {:<38} {} {} {} {:>11} {:^7} {}", - "Identity", - "Vote Account", - "Commission", - "Last Vote", - "Root Block", - "Epoch Credits", - "Version", - "Active Stake", - )) - .bold() - )?; - for validator in &self.current_validators { + + let padding = if self.number_validators { + ((self.validators.len() + 1) as f64).log10().floor() as usize + 1 + } else { + 0 + }; + let header = style(format!( + "{:padding$} {:<44} {:<38} {} {} {} {:>11} {:^7} {}", + " ", + "Identity", + "Vote Account", + "Commission", + "Last Vote", + "Root Block", + "Epoch Credits", + "Version", + "Active Stake", + padding = padding + 1 + )) + .bold(); + writeln!(f, "{}", header)?; + + let mut sorted_validators = self.validators.clone(); + match self.validators_sort_order { + CliValidatorsSortOrder::Delinquent => { + sorted_validators.sort_by_key(|a| a.delinquent); + } + CliValidatorsSortOrder::Commission => { + sorted_validators.sort_by_key(|a| a.commission); + } + CliValidatorsSortOrder::EpochCredits => { + sorted_validators.sort_by_key(|a| a.epoch_credits); + } + CliValidatorsSortOrder::Identity => { + sorted_validators.sort_by(|a, b| a.identity_pubkey.cmp(&b.identity_pubkey)); + } + CliValidatorsSortOrder::LastVote => { + sorted_validators.sort_by_key(|a| a.last_vote); + } + CliValidatorsSortOrder::Root => { + sorted_validators.sort_by_key(|a| a.root_slot); + } + CliValidatorsSortOrder::VoteAccount => { + sorted_validators.sort_by(|a, b| a.vote_account_pubkey.cmp(&b.vote_account_pubkey)); + } + CliValidatorsSortOrder::Stake => { + sorted_validators.sort_by_key(|a| a.activated_stake); + } + } + + if self.validators_reverse_sort { + sorted_validators.reverse(); + } + + for (i, validator) in sorted_validators.iter().enumerate() { + if padding > 0 { + write!(f, "{:padding$}", i + 1, padding = padding)?; + } write_vote_account( f, validator, self.total_active_stake, self.use_lamports_unit, - false, )?; } - for validator in &self.delinquent_validators { - write_vote_account( - f, - validator, - self.total_active_stake, - self.use_lamports_unit, - true, - )?; + + // The actual header has long scrolled away. Print the header once more as a footer + if self.validators.len() > 100 { + writeln!(f, "{}", header)?; } writeln!(f)?; @@ -453,7 +505,7 @@ impl fmt::Display for CliValidators { } } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Clone)] #[serde(rename_all = "camelCase")] pub struct CliValidator { pub identity_pubkey: String, @@ -465,6 +517,7 @@ pub struct CliValidator { pub epoch_credits: u64, // credits earned in the current epoch pub activated_stake: u64, pub version: String, + pub delinquent: bool, } impl CliValidator { @@ -473,6 +526,25 @@ impl CliValidator { current_epoch: Epoch, version: String, address_labels: &HashMap, + ) -> Self { + Self::_new(vote_account, current_epoch, version, address_labels, false) + } + + pub fn new_delinquent( + vote_account: &RpcVoteAccountInfo, + current_epoch: Epoch, + version: String, + address_labels: &HashMap, + ) -> Self { + Self::_new(vote_account, current_epoch, version, address_labels, true) + } + + fn _new( + vote_account: &RpcVoteAccountInfo, + current_epoch: Epoch, + version: String, + address_labels: &HashMap, + delinquent: bool, ) -> Self { let (credits, epoch_credits) = vote_account .epoch_credits @@ -485,7 +557,6 @@ impl CliValidator { } }) .unwrap_or((0, 0)); - Self { identity_pubkey: format_labeled_address(&vote_account.node_pubkey, address_labels), vote_account_pubkey: format_labeled_address(&vote_account.vote_pubkey, address_labels), @@ -496,6 +567,7 @@ impl CliValidator { epoch_credits, activated_stake: vote_account.activated_stake, version, + delinquent, } } } diff --git a/cli/src/cli.rs b/cli/src/cli.rs index ffd4115fdc..96db56cfba 100644 --- a/cli/src/cli.rs +++ b/cli/src/cli.rs @@ -20,7 +20,8 @@ use solana_clap_utils::{ use solana_cli_output::{ display::{build_balance_message, println_name_value}, return_signers_with_config, CliAccount, CliSignature, CliSignatureVerificationStatus, - CliTransaction, CliTransactionConfirmation, OutputFormat, ReturnSignersConfig, + CliTransaction, CliTransactionConfirmation, CliValidatorsSortOrder, OutputFormat, + ReturnSignersConfig, }; use solana_client::{ blockhash_query::BlockhashQuery, @@ -130,6 +131,9 @@ pub enum CliCommand { }, ShowValidators { use_lamports_unit: bool, + sort_order: CliValidatorsSortOrder, + reverse_sort: bool, + number_validators: bool, }, Supply { print_accounts: bool, @@ -1378,9 +1382,19 @@ pub fn process_command(config: &CliConfig) -> ProcessResult { CliCommand::WaitForMaxStake { max_stake_percent } => { process_wait_for_max_stake(&rpc_client, config, *max_stake_percent) } - CliCommand::ShowValidators { use_lamports_unit } => { - process_show_validators(&rpc_client, config, *use_lamports_unit) - } + CliCommand::ShowValidators { + use_lamports_unit, + sort_order, + reverse_sort, + number_validators, + } => process_show_validators( + &rpc_client, + config, + *use_lamports_unit, + *sort_order, + *reverse_sort, + *number_validators, + ), CliCommand::Supply { print_accounts } => { process_supply(&rpc_client, config, *print_accounts) } diff --git a/cli/src/cluster_query.rs b/cli/src/cluster_query.rs index cd08f98129..38996b65de 100644 --- a/cli/src/cluster_query.rs +++ b/cli/src/cluster_query.rs @@ -349,6 +349,37 @@ impl ClusterQuerySubCommands for App<'_, '_> { .long("lamports") .takes_value(false) .help("Display balance in lamports instead of SOL"), + ) + .arg( + Arg::with_name("number") + .long("number") + .short("n") + .takes_value(false) + .help("Number the validators"), + ) + .arg( + Arg::with_name("reverse") + .long("reverse") + .short("r") + .takes_value(false) + .help("Reverse order while sorting"), + ) + .arg( + Arg::with_name("sort") + .long("sort") + .takes_value(true) + .possible_values(&[ + "delinquent", + "commission", + "credits", + "identity", + "last-vote", + "root", + "stake", + "vote-account", + ]) + .default_value("stake") + .help("Sort order (does not affect JSON output)"), ), ) .subcommand( @@ -582,9 +613,28 @@ pub fn parse_show_stakes( pub fn parse_show_validators(matches: &ArgMatches<'_>) -> Result { let use_lamports_unit = matches.is_present("lamports"); + let number_validators = matches.is_present("number"); + let reverse_sort = matches.is_present("reverse"); + + let sort_order = match value_t_or_exit!(matches, "sort", String).as_str() { + "delinquent" => CliValidatorsSortOrder::Delinquent, + "commission" => CliValidatorsSortOrder::Commission, + "credits" => CliValidatorsSortOrder::EpochCredits, + "identity" => CliValidatorsSortOrder::Identity, + "last-vote" => CliValidatorsSortOrder::LastVote, + "root" => CliValidatorsSortOrder::Root, + "stake" => CliValidatorsSortOrder::Stake, + "vote-account" => CliValidatorsSortOrder::VoteAccount, + _ => unreachable!(), + }; Ok(CliCommandInfo { - command: CliCommand::ShowValidators { use_lamports_unit }, + command: CliCommand::ShowValidators { + use_lamports_unit, + sort_order, + reverse_sort, + number_validators, + }, signers: vec![], }) } @@ -1730,6 +1780,9 @@ pub fn process_show_validators( rpc_client: &RpcClient, config: &CliConfig, use_lamports_unit: bool, + validators_sort_order: CliValidatorsSortOrder, + validators_reverse_sort: bool, + number_validators: bool, ) -> ProcessResult { let epoch_info = rpc_client.get_epoch_info()?; let vote_accounts = rpc_client.get_vote_accounts()?; @@ -1759,9 +1812,8 @@ pub fn process_show_validators( .sum(); let total_current_stake = total_active_stake - total_delinquent_stake; - let mut current = vote_accounts.current; - current.sort_by(|a, b| b.activated_stake.cmp(&a.activated_stake)); - let current_validators: Vec = current + let current_validators: Vec = vote_accounts + .current .iter() .map(|vote_account| { CliValidator::new( @@ -1775,12 +1827,11 @@ pub fn process_show_validators( ) }) .collect(); - let mut delinquent = vote_accounts.delinquent; - delinquent.sort_by(|a, b| b.activated_stake.cmp(&a.activated_stake)); - let delinquent_validators: Vec = delinquent + let delinquent_validators: Vec = vote_accounts + .delinquent .iter() .map(|vote_account| { - CliValidator::new( + CliValidator::new_delinquent( vote_account, epoch_info.epoch, node_version @@ -1812,8 +1863,13 @@ pub fn process_show_validators( total_active_stake, total_current_stake, total_delinquent_stake, - current_validators, - delinquent_validators, + validators: current_validators + .into_iter() + .chain(delinquent_validators.into_iter()) + .collect(), + validators_sort_order, + validators_reverse_sort, + number_validators, stake_by_version, use_lamports_unit, };