Skip to content

Commit

Permalink
Cli: use get_inflation_rewards and limit epochs queried (solana-labs#…
Browse files Browse the repository at this point in the history
…16408)

* Fix block-with-limit when not finalized blocks found

* Enable confirmed commitment in getInflationReward

* Use get_inflation_rewards in cli

* Line up rewards output

* Add range validator

* Change cli epoch arg -> num epochs

* Add solana inflation rewards subcommand

* Consolidate epoch rewards meta
  • Loading branch information
CriesofCarrots authored Apr 8, 2021
1 parent 0e262aa commit bb9d2fd
Show file tree
Hide file tree
Showing 8 changed files with 352 additions and 125 deletions.
23 changes: 23 additions & 0 deletions clap-utils/src/input_validators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,29 @@ where
is_parsable_generic::<T, String>(string)
}

// Return an error if string cannot be parsed as numeric type T, and value not within specified
// range
pub fn is_within_range<T>(string: String, range_min: T, range_max: T) -> Result<(), String>
where
T: FromStr + Copy + std::fmt::Debug + PartialOrd + std::ops::Add<Output = T> + From<usize>,
T::Err: Display,
{
match string.parse::<T>() {
Ok(input) => {
let range = range_min..range_max + 1.into();
if !range.contains(&input) {
Err(format!(
"input '{:?}' out of range ({:?}..{:?}]",
input, range_min, range_max
))
} else {
Ok(())
}
}
Err(err) => Err(format!("error parsing '{}': {}", string, err)),
}
}

// Return an error if a pubkey cannot be parsed.
pub fn is_pubkey<T>(string: T) -> Result<(), String>
where
Expand Down
74 changes: 72 additions & 2 deletions cli-output/src/cli_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,76 @@ pub struct CliEpochReward {
pub apr: Option<f64>,
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CliKeyedEpochReward {
pub address: String,
pub reward: Option<CliEpochReward>,
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CliEpochRewardshMetadata {
pub epoch: Epoch,
pub effective_slot: Slot,
pub block_time: UnixTimestamp,
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CliKeyedEpochRewards {
#[serde(flatten, skip_serializing_if = "Option::is_none")]
pub epoch_metadata: Option<CliEpochRewardshMetadata>,
pub rewards: Vec<CliKeyedEpochReward>,
}

impl QuietDisplay for CliKeyedEpochRewards {}
impl VerboseDisplay for CliKeyedEpochRewards {}

impl fmt::Display for CliKeyedEpochRewards {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.rewards.is_empty() {
writeln!(f, "No rewards found in epoch")?;
return Ok(());
}

if let Some(metadata) = &self.epoch_metadata {
writeln!(f, "Epoch: {}", metadata.epoch)?;
writeln!(f, "Reward Slot: {}", metadata.effective_slot)?;
let timestamp = metadata.block_time;
writeln!(f, "Block Time: {}", unix_timestamp_to_string(timestamp))?;
}
writeln!(f, "Epoch Rewards:")?;
writeln!(
f,
" {:<44} {:<18} {:<18} {:>14} {:>14}",
"Address", "Amount", "New Balance", "Percent Change", "APR"
)?;
for keyed_reward in &self.rewards {
match &keyed_reward.reward {
Some(reward) => {
writeln!(
f,
" {:<44} ◎{:<17.9} ◎{:<17.9} {:>13.2}% {}",
keyed_reward.address,
lamports_to_sol(reward.amount),
lamports_to_sol(reward.post_balance),
reward.percent_change,
reward
.apr
.map(|apr| format!("{:>13.2}%", apr))
.unwrap_or_default(),
)?;
}
None => {
writeln!(f, " {:<44} No rewards in epoch", keyed_reward.address,)?;
}
}
}
Ok(())
}
}

fn show_votes_and_credits(
f: &mut fmt::Formatter,
votes: &[CliLockout],
Expand Down Expand Up @@ -708,13 +778,13 @@ fn show_epoch_rewards(
writeln!(f, "Epoch Rewards:")?;
writeln!(
f,
" {:<6} {:<11} {:<16} {:<16} {:>14} {:>14}",
" {:<6} {:<11} {:<18} {:<18} {:>14} {:>14}",
"Epoch", "Reward Slot", "Amount", "New Balance", "Percent Change", "APR"
)?;
for reward in epoch_rewards {
writeln!(
f,
" {:<6} {:<11} ◎{:<16.9} ◎{:<14.9} {:>13.2}% {}",
" {:<6} {:<11} ◎{:<17.9} ◎{:<17.9} {:>13.2}% {}",
reward.epoch,
reward.effective_slot,
lamports_to_sol(reward.amount),
Expand Down
6 changes: 6 additions & 0 deletions cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ pub enum CliCommand {
ShowStakeAccount {
pubkey: Pubkey,
use_lamports_unit: bool,
with_rewards: Option<usize>,
},
StakeAuthorize {
stake_account_pubkey: Pubkey,
Expand Down Expand Up @@ -330,6 +331,7 @@ pub enum CliCommand {
ShowVoteAccount {
pubkey: Pubkey,
use_lamports_unit: bool,
with_rewards: Option<usize>,
},
WithdrawFromVoteAccount {
vote_account_pubkey: Pubkey,
Expand Down Expand Up @@ -1674,11 +1676,13 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
CliCommand::ShowStakeAccount {
pubkey: stake_account_pubkey,
use_lamports_unit,
with_rewards,
} => process_show_stake_account(
&rpc_client,
config,
&stake_account_pubkey,
*use_lamports_unit,
*with_rewards,
),
CliCommand::ShowStakeHistory { use_lamports_unit } => {
process_show_stake_history(&rpc_client, config, *use_lamports_unit)
Expand Down Expand Up @@ -1807,11 +1811,13 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
CliCommand::ShowVoteAccount {
pubkey: vote_account_pubkey,
use_lamports_unit,
with_rewards,
} => process_show_vote_account(
&rpc_client,
config,
&vote_account_pubkey,
*use_lamports_unit,
*with_rewards,
),
CliCommand::WithdrawFromVoteAccount {
vote_account_pubkey,
Expand Down
105 changes: 98 additions & 7 deletions cli/src/inflation.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
use crate::cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult};
use clap::{App, ArgMatches, SubCommand};
use solana_clap_utils::keypair::*;
use solana_cli_output::CliInflation;
use clap::{App, Arg, ArgMatches, SubCommand};
use solana_clap_utils::{
input_parsers::{pubkeys_of, value_of},
input_validators::is_valid_pubkey,
keypair::*,
};
use solana_cli_output::{
CliEpochRewardshMetadata, CliInflation, CliKeyedEpochReward, CliKeyedEpochRewards,
};
use solana_client::rpc_client::RpcClient;
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{clock::Epoch, pubkey::Pubkey};
use std::sync::Arc;

#[derive(Debug, PartialEq)]
pub enum InflationCliCommand {
Show,
Rewards(Vec<Pubkey>, Option<Epoch>),
}

pub trait InflationSubCommands {
Expand All @@ -17,17 +25,47 @@ pub trait InflationSubCommands {

impl InflationSubCommands for App<'_, '_> {
fn inflation_subcommands(self) -> Self {
self.subcommand(SubCommand::with_name("inflation").about("Show inflation information"))
self.subcommand(
SubCommand::with_name("inflation")
.about("Show inflation information")
.subcommand(
SubCommand::with_name("rewards")
.about("Show inflation rewards for a set of addresses")
.arg(pubkey!(
Arg::with_name("addresses")
.value_name("ADDRESS")
.index(1)
.multiple(true)
.required(true),
"Address of account to query for rewards. "
))
.arg(
Arg::with_name("rewards_epoch")
.long("rewards-epoch")
.takes_value(true)
.value_name("EPOCH")
.help("Display rewards for specific epoch [default: latest epoch]"),
),
),
)
}
}

pub fn parse_inflation_subcommand(
_matches: &ArgMatches<'_>,
matches: &ArgMatches<'_>,
_default_signer: &DefaultSigner,
_wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let command = match matches.subcommand() {
("rewards", Some(matches)) => {
let addresses = pubkeys_of(matches, "addresses").unwrap();
let rewards_epoch = value_of(matches, "rewards_epoch");
InflationCliCommand::Rewards(addresses, rewards_epoch)
}
_ => InflationCliCommand::Show,
};
Ok(CliCommandInfo {
command: CliCommand::Inflation(InflationCliCommand::Show),
command: CliCommand::Inflation(command),
signers: vec![],
})
}
Expand All @@ -37,8 +75,15 @@ pub fn process_inflation_subcommand(
config: &CliConfig,
inflation_subcommand: &InflationCliCommand,
) -> ProcessResult {
assert_eq!(*inflation_subcommand, InflationCliCommand::Show);
match inflation_subcommand {
InflationCliCommand::Show => process_show(rpc_client, config),
InflationCliCommand::Rewards(ref addresses, rewards_epoch) => {
process_rewards(rpc_client, config, addresses, *rewards_epoch)
}
}
}

fn process_show(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
let governor = rpc_client.get_inflation_governor()?;
let current_rate = rpc_client.get_inflation_rate()?;

Expand All @@ -49,3 +94,49 @@ pub fn process_inflation_subcommand(

Ok(config.output_format.formatted_string(&inflation))
}

fn process_rewards(
rpc_client: &RpcClient,
config: &CliConfig,
addresses: &[Pubkey],
rewards_epoch: Option<Epoch>,
) -> ProcessResult {
let rewards = rpc_client
.get_inflation_reward(&addresses, rewards_epoch)
.map_err(|err| {
if let Some(epoch) = rewards_epoch {
format!("Rewards not available for epoch {}", epoch)
} else {
format!("Rewards not available {}", err)
}
})?;
let epoch_schedule = rpc_client.get_epoch_schedule()?;

let mut epoch_rewards: Vec<CliKeyedEpochReward> = vec![];
let epoch_metadata = if let Some(Some(first_reward)) = rewards.iter().find(|&v| v.is_some()) {
let (epoch_start_time, epoch_end_time) =
crate::stake::get_epoch_boundary_timestamps(rpc_client, first_reward, &epoch_schedule)?;
for (reward, address) in rewards.iter().zip(addresses) {
let cli_reward = reward.as_ref().and_then(|reward| {
crate::stake::make_cli_reward(reward, epoch_start_time, epoch_end_time)
});
epoch_rewards.push(CliKeyedEpochReward {
address: address.to_string(),
reward: cli_reward,
});
}
let block_time = rpc_client.get_block_time(first_reward.effective_slot)?;
Some(CliEpochRewardshMetadata {
epoch: first_reward.epoch,
effective_slot: first_reward.effective_slot,
block_time,
})
} else {
None
};
let cli_rewards = CliKeyedEpochRewards {
epoch_metadata,
rewards: epoch_rewards,
};
Ok(config.output_format.formatted_string(&cli_rewards))
}
Loading

0 comments on commit bb9d2fd

Please sign in to comment.