CLI: Improve stake (de)activation display

This commit is contained in:
Trent Nelson 2020-04-27 14:34:24 -06:00 committed by Trent Nelson
parent 34507e8a5b
commit f9ee97d6f5
4 changed files with 200 additions and 66 deletions

View File

@ -482,7 +482,7 @@ impl fmt::Display for CliKeyedStakeState {
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct CliStakeState { pub struct CliStakeState {
pub stake_type: CliStakeType, pub stake_type: CliStakeType,
pub total_stake: u64, pub account_balance: u64,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub delegated_stake: Option<u64>, pub delegated_stake: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
@ -497,6 +497,16 @@ pub struct CliStakeState {
pub lockup: Option<CliLockup>, pub lockup: Option<CliLockup>,
#[serde(skip_serializing)] #[serde(skip_serializing)]
pub use_lamports_unit: bool, pub use_lamports_unit: bool,
#[serde(skip_serializing)]
pub current_epoch: Epoch,
#[serde(skip_serializing_if = "Option::is_none")]
pub rent_exempt_reserve: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub active_stake: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub activating_stake: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub deactivating_stake: Option<u64>,
} }
impl fmt::Display for CliStakeState { impl fmt::Display for CliStakeState {
@ -522,52 +532,122 @@ impl fmt::Display for CliStakeState {
Ok(()) Ok(())
} }
writeln!(
f,
"Balance: {}",
build_balance_message(self.account_balance, self.use_lamports_unit, true)
)?;
if let Some(rent_exempt_reserve) = self.rent_exempt_reserve {
writeln!(
f,
"Rent Exempt Reserve: {}",
build_balance_message(rent_exempt_reserve, self.use_lamports_unit, true)
)?;
}
match self.stake_type { match self.stake_type {
CliStakeType::RewardsPool => writeln!(f, "Stake account is a rewards pool")?, CliStakeType::RewardsPool => writeln!(f, "Stake account is a rewards pool")?,
CliStakeType::Uninitialized => writeln!(f, "Stake account is uninitialized")?, CliStakeType::Uninitialized => writeln!(f, "Stake account is uninitialized")?,
CliStakeType::Initialized => { CliStakeType::Initialized => {
writeln!(
f,
"Total Stake: {}",
build_balance_message(self.total_stake, self.use_lamports_unit, true)
)?;
writeln!(f, "Stake account is undelegated")?; writeln!(f, "Stake account is undelegated")?;
show_authorized(f, self.authorized.as_ref().unwrap())?; show_authorized(f, self.authorized.as_ref().unwrap())?;
show_lockup(f, self.lockup.as_ref().unwrap())?; show_lockup(f, self.lockup.as_ref().unwrap())?;
} }
CliStakeType::Stake => { CliStakeType::Stake => {
writeln!( let show_delegation = {
f, self.active_stake.is_some()
"Total Stake: {}", || self.activating_stake.is_some()
build_balance_message(self.total_stake, self.use_lamports_unit, true) || self.deactivating_stake.is_some()
)?; || self
writeln!( .deactivation_epoch
f, .map(|de| de > self.current_epoch)
"Delegated Stake: {}", .unwrap_or(true)
build_balance_message( };
self.delegated_stake.unwrap(), if show_delegation {
self.use_lamports_unit, let delegated_stake = self.delegated_stake.unwrap();
true
)
)?;
if let Some(delegated_vote_account_address) = &self.delegated_vote_account_address {
writeln!( writeln!(
f, f,
"Delegated Vote Account Address: {}", "Delegated Stake: {}",
delegated_vote_account_address build_balance_message(delegated_stake, self.use_lamports_unit, true)
)?;
}
writeln!(
f,
"Stake activates starting from epoch: {}",
self.activation_epoch.unwrap()
)?;
if let Some(deactivation_epoch) = self.deactivation_epoch {
writeln!(
f,
"Stake deactivates starting from epoch: {}",
deactivation_epoch
)?; )?;
if self
.deactivation_epoch
.map(|d| self.current_epoch <= d)
.unwrap_or(true)
{
let active_stake = self.active_stake.unwrap_or(0);
writeln!(
f,
"Active Stake: {}",
build_balance_message(active_stake, self.use_lamports_unit, true),
)?;
let activating_stake = self.activating_stake.or_else(|| {
if self.active_stake.is_none() {
Some(delegated_stake)
} else {
None
}
});
if let Some(activating_stake) = activating_stake {
writeln!(
f,
"Activating Stake: {}",
build_balance_message(
activating_stake,
self.use_lamports_unit,
true
),
)?;
writeln!(
f,
"Stake activates starting from epoch: {}",
self.activation_epoch.unwrap()
)?;
}
}
if let Some(deactivation_epoch) = self.deactivation_epoch {
if self.current_epoch > deactivation_epoch {
let deactivating_stake = self.deactivating_stake.or(self.active_stake);
if let Some(deactivating_stake) = deactivating_stake {
writeln!(
f,
"Inactive Stake: {}",
build_balance_message(
delegated_stake - deactivating_stake,
self.use_lamports_unit,
true
),
)?;
writeln!(
f,
"Deactivating Stake: {}",
build_balance_message(
deactivating_stake,
self.use_lamports_unit,
true
),
)?;
}
}
writeln!(
f,
"Stake deactivates starting from epoch: {}",
deactivation_epoch
)?;
}
if let Some(delegated_vote_account_address) =
&self.delegated_vote_account_address
{
writeln!(
f,
"Delegated Vote Account Address: {}",
delegated_vote_account_address
)?;
}
} else {
writeln!(f, "Stake account is undelegated")?;
} }
show_authorized(f, self.authorized.as_ref().unwrap())?; show_authorized(f, self.authorized.as_ref().unwrap())?;
show_lockup(f, self.lockup.as_ref().unwrap())?; show_lockup(f, self.lockup.as_ref().unwrap())?;

View File

@ -29,7 +29,11 @@ use solana_sdk::{
native_token::lamports_to_sol, native_token::lamports_to_sol,
pubkey::{self, Pubkey}, pubkey::{self, Pubkey},
system_instruction, system_program, system_instruction, system_program,
sysvar::{self, Sysvar}, sysvar::{
self,
stake_history::{self, StakeHistory},
Sysvar,
},
transaction::Transaction, transaction::Transaction,
}; };
use std::{ use std::{
@ -1184,8 +1188,13 @@ pub fn process_show_stakes(
let progress_bar = new_spinner_progress_bar(); let progress_bar = new_spinner_progress_bar();
progress_bar.set_message("Fetching stake accounts..."); progress_bar.set_message("Fetching stake accounts...");
let all_stake_accounts = rpc_client.get_program_accounts(&solana_stake_program::id())?; let all_stake_accounts = rpc_client.get_program_accounts(&solana_stake_program::id())?;
let stake_history_account = rpc_client.get_account(&stake_history::id())?;
progress_bar.finish_and_clear(); progress_bar.finish_and_clear();
let stake_history = StakeHistory::from_account(&stake_history_account).ok_or_else(|| {
CliError::RpcRequestError("Failed to deserialize stake history".to_string())
})?;
let mut stake_accounts: Vec<CliKeyedStakeState> = vec![]; let mut stake_accounts: Vec<CliKeyedStakeState> = vec![];
for (stake_pubkey, stake_account) in all_stake_accounts { for (stake_pubkey, stake_account) in all_stake_accounts {
if let Ok(stake_state) = stake_account.state() { if let Ok(stake_state) = stake_account.state() {
@ -1198,6 +1207,7 @@ pub fn process_show_stakes(
stake_account.lamports, stake_account.lamports,
&stake_state, &stake_state,
use_lamports_unit, use_lamports_unit,
&stake_history,
), ),
}); });
} }
@ -1214,6 +1224,7 @@ pub fn process_show_stakes(
stake_account.lamports, stake_account.lamports,
&stake_state, &stake_state,
use_lamports_unit, use_lamports_unit,
&stake_history,
), ),
}); });
} }

View File

@ -1256,53 +1256,85 @@ pub fn process_stake_set_lockup(
} }
} }
fn u64_some_if_not_zero(n: u64) -> Option<u64> {
if n > 0 {
Some(n)
} else {
None
}
}
pub fn build_stake_state( pub fn build_stake_state(
stake_lamports: u64, account_balance: u64,
stake_state: &StakeState, stake_state: &StakeState,
use_lamports_unit: bool, use_lamports_unit: bool,
stake_history: &StakeHistory,
) -> CliStakeState { ) -> CliStakeState {
match stake_state { match stake_state {
StakeState::Stake( StakeState::Stake(
Meta { Meta {
authorized, lockup, .. rent_exempt_reserve,
authorized,
lockup,
}, },
stake, stake,
) => CliStakeState { ) => {
stake_type: CliStakeType::Stake, // The first entry in stake history is the previous epoch, so +1 for current
total_stake: stake_lamports, let current_epoch = stake_history.iter().next().unwrap().0 + 1;
delegated_stake: Some(stake.delegation.stake), let (active_stake, activating_stake, deactivating_stake) = stake
delegated_vote_account_address: if stake.delegation.voter_pubkey != Pubkey::default() { .delegation
Some(stake.delegation.voter_pubkey.to_string()) .stake_activating_and_deactivating(current_epoch, Some(stake_history));
} else { CliStakeState {
None stake_type: CliStakeType::Stake,
}, account_balance,
activation_epoch: Some(if stake.delegation.activation_epoch < std::u64::MAX { delegated_stake: Some(stake.delegation.stake),
stake.delegation.activation_epoch delegated_vote_account_address: if stake.delegation.voter_pubkey
} else { != Pubkey::default()
0 {
}), Some(stake.delegation.voter_pubkey.to_string())
deactivation_epoch: if stake.delegation.deactivation_epoch < std::u64::MAX { } else {
Some(stake.delegation.deactivation_epoch) None
} else { },
None activation_epoch: Some(if stake.delegation.activation_epoch < std::u64::MAX {
}, stake.delegation.activation_epoch
authorized: Some(authorized.into()), } else {
lockup: Some(lockup.into()), 0
use_lamports_unit, }),
}, deactivation_epoch: if stake.delegation.deactivation_epoch < std::u64::MAX {
Some(stake.delegation.deactivation_epoch)
} else {
None
},
authorized: Some(authorized.into()),
lockup: Some(lockup.into()),
use_lamports_unit,
current_epoch,
rent_exempt_reserve: Some(*rent_exempt_reserve),
active_stake: u64_some_if_not_zero(active_stake),
activating_stake: u64_some_if_not_zero(activating_stake),
deactivating_stake: u64_some_if_not_zero(deactivating_stake),
}
}
StakeState::RewardsPool => CliStakeState { StakeState::RewardsPool => CliStakeState {
stake_type: CliStakeType::RewardsPool, stake_type: CliStakeType::RewardsPool,
account_balance,
..CliStakeState::default()
},
StakeState::Uninitialized => CliStakeState {
account_balance,
..CliStakeState::default() ..CliStakeState::default()
}, },
StakeState::Uninitialized => CliStakeState::default(),
StakeState::Initialized(Meta { StakeState::Initialized(Meta {
authorized, lockup, .. rent_exempt_reserve,
authorized,
lockup,
}) => CliStakeState { }) => CliStakeState {
stake_type: CliStakeType::Initialized, stake_type: CliStakeType::Initialized,
total_stake: stake_lamports, account_balance,
authorized: Some(authorized.into()), authorized: Some(authorized.into()),
lockup: Some(lockup.into()), lockup: Some(lockup.into()),
use_lamports_unit, use_lamports_unit,
rent_exempt_reserve: Some(*rent_exempt_reserve),
..CliStakeState::default() ..CliStakeState::default()
}, },
} }
@ -1324,7 +1356,18 @@ pub fn process_show_stake_account(
} }
match stake_account.state() { match stake_account.state() {
Ok(stake_state) => { Ok(stake_state) => {
let state = build_stake_state(stake_account.lamports, &stake_state, use_lamports_unit); let stake_history_account = rpc_client.get_account(&stake_history::id())?;
let stake_history =
StakeHistory::from_account(&stake_history_account).ok_or_else(|| {
CliError::RpcRequestError("Failed to deserialize stake history".to_string())
})?;
let state = build_stake_state(
stake_account.lamports,
&stake_state,
use_lamports_unit,
&stake_history,
);
Ok(config.output_format.formatted_string(&state)) Ok(config.output_format.formatted_string(&state))
} }
Err(err) => Err(CliError::RpcRequestError(format!( Err(err) => Err(CliError::RpcRequestError(format!(

View File

@ -202,7 +202,7 @@ impl Delegation {
} }
#[allow(clippy::comparison_chain)] #[allow(clippy::comparison_chain)]
fn stake_activating_and_deactivating( pub fn stake_activating_and_deactivating(
&self, &self,
epoch: Epoch, epoch: Epoch,
history: Option<&StakeHistory>, history: Option<&StakeHistory>,