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")]
pub struct CliStakeState {
pub stake_type: CliStakeType,
pub total_stake: u64,
pub account_balance: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub delegated_stake: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
@ -497,6 +497,16 @@ pub struct CliStakeState {
pub lockup: Option<CliLockup>,
#[serde(skip_serializing)]
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 {
@ -522,52 +532,122 @@ impl fmt::Display for CliStakeState {
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 {
CliStakeType::RewardsPool => writeln!(f, "Stake account is a rewards pool")?,
CliStakeType::Uninitialized => writeln!(f, "Stake account is uninitialized")?,
CliStakeType::Initialized => {
writeln!(
f,
"Total Stake: {}",
build_balance_message(self.total_stake, self.use_lamports_unit, true)
)?;
writeln!(f, "Stake account is undelegated")?;
show_authorized(f, self.authorized.as_ref().unwrap())?;
show_lockup(f, self.lockup.as_ref().unwrap())?;
}
CliStakeType::Stake => {
writeln!(
f,
"Total Stake: {}",
build_balance_message(self.total_stake, self.use_lamports_unit, true)
)?;
writeln!(
f,
"Delegated Stake: {}",
build_balance_message(
self.delegated_stake.unwrap(),
self.use_lamports_unit,
true
)
)?;
if let Some(delegated_vote_account_address) = &self.delegated_vote_account_address {
let show_delegation = {
self.active_stake.is_some()
|| self.activating_stake.is_some()
|| self.deactivating_stake.is_some()
|| self
.deactivation_epoch
.map(|de| de > self.current_epoch)
.unwrap_or(true)
};
if show_delegation {
let delegated_stake = self.delegated_stake.unwrap();
writeln!(
f,
"Delegated Vote Account Address: {}",
delegated_vote_account_address
)?;
}
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
"Delegated Stake: {}",
build_balance_message(delegated_stake, self.use_lamports_unit, true)
)?;
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_lockup(f, self.lockup.as_ref().unwrap())?;

View File

@ -29,7 +29,11 @@ use solana_sdk::{
native_token::lamports_to_sol,
pubkey::{self, Pubkey},
system_instruction, system_program,
sysvar::{self, Sysvar},
sysvar::{
self,
stake_history::{self, StakeHistory},
Sysvar,
},
transaction::Transaction,
};
use std::{
@ -1184,8 +1188,13 @@ pub fn process_show_stakes(
let progress_bar = new_spinner_progress_bar();
progress_bar.set_message("Fetching stake accounts...");
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();
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![];
for (stake_pubkey, stake_account) in all_stake_accounts {
if let Ok(stake_state) = stake_account.state() {
@ -1198,6 +1207,7 @@ pub fn process_show_stakes(
stake_account.lamports,
&stake_state,
use_lamports_unit,
&stake_history,
),
});
}
@ -1214,6 +1224,7 @@ pub fn process_show_stakes(
stake_account.lamports,
&stake_state,
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(
stake_lamports: u64,
account_balance: u64,
stake_state: &StakeState,
use_lamports_unit: bool,
stake_history: &StakeHistory,
) -> CliStakeState {
match stake_state {
StakeState::Stake(
Meta {
authorized, lockup, ..
rent_exempt_reserve,
authorized,
lockup,
},
stake,
) => CliStakeState {
stake_type: CliStakeType::Stake,
total_stake: stake_lamports,
delegated_stake: Some(stake.delegation.stake),
delegated_vote_account_address: if stake.delegation.voter_pubkey != Pubkey::default() {
Some(stake.delegation.voter_pubkey.to_string())
} else {
None
},
activation_epoch: Some(if stake.delegation.activation_epoch < std::u64::MAX {
stake.delegation.activation_epoch
} else {
0
}),
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,
},
) => {
// The first entry in stake history is the previous epoch, so +1 for current
let current_epoch = stake_history.iter().next().unwrap().0 + 1;
let (active_stake, activating_stake, deactivating_stake) = stake
.delegation
.stake_activating_and_deactivating(current_epoch, Some(stake_history));
CliStakeState {
stake_type: CliStakeType::Stake,
account_balance,
delegated_stake: Some(stake.delegation.stake),
delegated_vote_account_address: if stake.delegation.voter_pubkey
!= Pubkey::default()
{
Some(stake.delegation.voter_pubkey.to_string())
} else {
None
},
activation_epoch: Some(if stake.delegation.activation_epoch < std::u64::MAX {
stake.delegation.activation_epoch
} else {
0
}),
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 {
stake_type: CliStakeType::RewardsPool,
account_balance,
..CliStakeState::default()
},
StakeState::Uninitialized => CliStakeState {
account_balance,
..CliStakeState::default()
},
StakeState::Uninitialized => CliStakeState::default(),
StakeState::Initialized(Meta {
authorized, lockup, ..
rent_exempt_reserve,
authorized,
lockup,
}) => CliStakeState {
stake_type: CliStakeType::Initialized,
total_stake: stake_lamports,
account_balance,
authorized: Some(authorized.into()),
lockup: Some(lockup.into()),
use_lamports_unit,
rent_exempt_reserve: Some(*rent_exempt_reserve),
..CliStakeState::default()
},
}
@ -1324,7 +1356,18 @@ pub fn process_show_stake_account(
}
match stake_account.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))
}
Err(err) => Err(CliError::RpcRequestError(format!(

View File

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