Add show-stakes subcommand
This commit is contained in:
@ -64,6 +64,20 @@ pub fn pubkey_of(matches: &ArgMatches<'_>, name: &str) -> Option<Pubkey> {
|
|||||||
value_of(matches, name).or_else(|| keypair_of(matches, name).map(|keypair| keypair.pubkey()))
|
value_of(matches, name).or_else(|| keypair_of(matches, name).map(|keypair| keypair.pubkey()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn pubkeys_of(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<Pubkey>> {
|
||||||
|
matches.values_of(name).map(|values| {
|
||||||
|
values
|
||||||
|
.map(|value| {
|
||||||
|
value.parse::<Pubkey>().unwrap_or_else(|_| {
|
||||||
|
read_keypair_file(value)
|
||||||
|
.expect("read_keypair_file failed")
|
||||||
|
.pubkey()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Return pubkey/signature pairs for a string of the form pubkey=signature
|
// Return pubkey/signature pairs for a string of the form pubkey=signature
|
||||||
pub fn pubkeys_sigs_of(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<(Pubkey, Signature)>> {
|
pub fn pubkeys_sigs_of(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<(Pubkey, Signature)>> {
|
||||||
matches.values_of(name).map(|values| {
|
matches.values_of(name).map(|values| {
|
||||||
@ -154,7 +168,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_keypair_of() {
|
fn test_keypair_of() {
|
||||||
let keypair = Keypair::new();
|
let keypair = Keypair::new();
|
||||||
let outfile = tmp_file_path("test_gen_keypair_file.json", &keypair.pubkey());
|
let outfile = tmp_file_path("test_keypair_of.json", &keypair.pubkey());
|
||||||
let _ = write_keypair_file(&keypair, &outfile).unwrap();
|
let _ = write_keypair_file(&keypair, &outfile).unwrap();
|
||||||
|
|
||||||
let matches = app()
|
let matches = app()
|
||||||
@ -178,7 +192,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_pubkey_of() {
|
fn test_pubkey_of() {
|
||||||
let keypair = Keypair::new();
|
let keypair = Keypair::new();
|
||||||
let outfile = tmp_file_path("test_gen_keypair_file.json", &keypair.pubkey());
|
let outfile = tmp_file_path("test_pubkey_of.json", &keypair.pubkey());
|
||||||
let _ = write_keypair_file(&keypair, &outfile).unwrap();
|
let _ = write_keypair_file(&keypair, &outfile).unwrap();
|
||||||
|
|
||||||
let matches = app()
|
let matches = app()
|
||||||
@ -202,6 +216,26 @@ mod tests {
|
|||||||
fs::remove_file(&outfile).unwrap();
|
fs::remove_file(&outfile).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pubkeys_of() {
|
||||||
|
let keypair = Keypair::new();
|
||||||
|
let outfile = tmp_file_path("test_pubkeys_of.json", &keypair.pubkey());
|
||||||
|
let _ = write_keypair_file(&keypair, &outfile).unwrap();
|
||||||
|
|
||||||
|
let matches = app().clone().get_matches_from(vec![
|
||||||
|
"test",
|
||||||
|
"--multiple",
|
||||||
|
&keypair.pubkey().to_string(),
|
||||||
|
"--multiple",
|
||||||
|
&outfile,
|
||||||
|
]);
|
||||||
|
assert_eq!(
|
||||||
|
pubkeys_of(&matches, "multiple"),
|
||||||
|
Some(vec![keypair.pubkey(), keypair.pubkey()])
|
||||||
|
);
|
||||||
|
fs::remove_file(&outfile).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pubkeys_sigs_of() {
|
fn test_pubkeys_sigs_of() {
|
||||||
let key1 = Pubkey::new_rand();
|
let key1 = Pubkey::new_rand();
|
||||||
|
@ -126,6 +126,10 @@ pub enum CliCommand {
|
|||||||
slot_limit: Option<u64>,
|
slot_limit: Option<u64>,
|
||||||
},
|
},
|
||||||
ShowGossip,
|
ShowGossip,
|
||||||
|
ShowStakes {
|
||||||
|
use_lamports_unit: bool,
|
||||||
|
vote_account_pubkeys: Option<Vec<Pubkey>>,
|
||||||
|
},
|
||||||
ShowValidators {
|
ShowValidators {
|
||||||
use_lamports_unit: bool,
|
use_lamports_unit: bool,
|
||||||
},
|
},
|
||||||
@ -380,6 +384,7 @@ pub fn parse_command(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, Box<dyn
|
|||||||
command: CliCommand::ShowGossip,
|
command: CliCommand::ShowGossip,
|
||||||
require_keypair: false,
|
require_keypair: false,
|
||||||
}),
|
}),
|
||||||
|
("show-stakes", Some(matches)) => parse_show_stakes(matches),
|
||||||
("show-validators", Some(matches)) => parse_show_validators(matches),
|
("show-validators", Some(matches)) => parse_show_validators(matches),
|
||||||
// Nonce Commands
|
// Nonce Commands
|
||||||
("authorize-nonce-account", Some(matches)) => parse_authorize_nonce_account(matches),
|
("authorize-nonce-account", Some(matches)) => parse_authorize_nonce_account(matches),
|
||||||
@ -1200,6 +1205,14 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
|||||||
process_show_block_production(&rpc_client, config, *epoch, *slot_limit)
|
process_show_block_production(&rpc_client, config, *epoch, *slot_limit)
|
||||||
}
|
}
|
||||||
CliCommand::ShowGossip => process_show_gossip(&rpc_client),
|
CliCommand::ShowGossip => process_show_gossip(&rpc_client),
|
||||||
|
CliCommand::ShowStakes {
|
||||||
|
use_lamports_unit,
|
||||||
|
vote_account_pubkeys,
|
||||||
|
} => process_show_stakes(
|
||||||
|
&rpc_client,
|
||||||
|
*use_lamports_unit,
|
||||||
|
vote_account_pubkeys.as_deref(),
|
||||||
|
),
|
||||||
CliCommand::ShowValidators { use_lamports_unit } => {
|
CliCommand::ShowValidators { use_lamports_unit } => {
|
||||||
process_show_validators(&rpc_client, *use_lamports_unit)
|
process_show_validators(&rpc_client, *use_lamports_unit)
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ use indicatif::{ProgressBar, ProgressStyle};
|
|||||||
use solana_clap_utils::{input_parsers::*, input_validators::*};
|
use solana_clap_utils::{input_parsers::*, input_validators::*};
|
||||||
use solana_client::{rpc_client::RpcClient, rpc_response::RpcVoteAccountInfo};
|
use solana_client::{rpc_client::RpcClient, rpc_response::RpcVoteAccountInfo};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
|
account_utils::State,
|
||||||
clock::{self, Slot},
|
clock::{self, Slot},
|
||||||
commitment_config::CommitmentConfig,
|
commitment_config::CommitmentConfig,
|
||||||
epoch_schedule::{Epoch, EpochSchedule},
|
epoch_schedule::{Epoch, EpochSchedule},
|
||||||
@ -169,9 +170,28 @@ impl ClusterQuerySubCommands for App<'_, '_> {
|
|||||||
SubCommand::with_name("show-gossip")
|
SubCommand::with_name("show-gossip")
|
||||||
.about("Show the current gossip network nodes"),
|
.about("Show the current gossip network nodes"),
|
||||||
)
|
)
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("show-stakes")
|
||||||
|
.about("Show stake account information")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("vote_account_pubkeys")
|
||||||
|
.index(1)
|
||||||
|
.value_name("VOTE ACCOUNT PUBKEYS")
|
||||||
|
.takes_value(true)
|
||||||
|
.multiple(true)
|
||||||
|
.validator(is_pubkey_or_keypair)
|
||||||
|
.help("Only show stake accounts delegated to the provided vote accounts"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("lamports")
|
||||||
|
.long("lamports")
|
||||||
|
.takes_value(false)
|
||||||
|
.help("Display balance in lamports instead of SOL"),
|
||||||
|
),
|
||||||
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("show-validators")
|
SubCommand::with_name("show-validators")
|
||||||
.about("Show information about the current validators")
|
.about("Show summary information about the current validators")
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("lamports")
|
Arg::with_name("lamports")
|
||||||
.long("lamports")
|
.long("lamports")
|
||||||
@ -260,6 +280,19 @@ pub fn parse_get_transaction_count(matches: &ArgMatches<'_>) -> Result<CliComman
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse_show_stakes(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||||
|
let use_lamports_unit = matches.is_present("lamports");
|
||||||
|
let vote_account_pubkeys = pubkeys_of(matches, "vote_account_pubkeys");
|
||||||
|
|
||||||
|
Ok(CliCommandInfo {
|
||||||
|
command: CliCommand::ShowStakes {
|
||||||
|
use_lamports_unit,
|
||||||
|
vote_account_pubkeys,
|
||||||
|
},
|
||||||
|
require_keypair: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_show_validators(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
pub fn parse_show_validators(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||||
let use_lamports_unit = matches.is_present("lamports");
|
let use_lamports_unit = matches.is_present("lamports");
|
||||||
|
|
||||||
@ -754,6 +787,45 @@ pub fn process_show_gossip(rpc_client: &RpcClient) -> ProcessResult {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn process_show_stakes(
|
||||||
|
rpc_client: &RpcClient,
|
||||||
|
use_lamports_unit: bool,
|
||||||
|
vote_account_pubkeys: Option<&[Pubkey]>,
|
||||||
|
) -> ProcessResult {
|
||||||
|
use crate::stake::print_stake_state;
|
||||||
|
use solana_stake_program::stake_state::StakeState;
|
||||||
|
|
||||||
|
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())?;
|
||||||
|
progress_bar.finish_and_clear();
|
||||||
|
|
||||||
|
for (stake_pubkey, stake_account) in all_stake_accounts {
|
||||||
|
if let Ok(stake_state) = stake_account.state() {
|
||||||
|
match stake_state {
|
||||||
|
StakeState::Initialized(_) => {
|
||||||
|
if vote_account_pubkeys.is_none() {
|
||||||
|
println!("\nstake pubkey: {}", stake_pubkey);
|
||||||
|
print_stake_state(stake_account.lamports, &stake_state, use_lamports_unit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StakeState::Stake(_, stake) => {
|
||||||
|
if vote_account_pubkeys.is_none()
|
||||||
|
|| vote_account_pubkeys
|
||||||
|
.unwrap()
|
||||||
|
.contains(&stake.delegation.voter_pubkey)
|
||||||
|
{
|
||||||
|
println!("\nstake pubkey: {}", stake_pubkey);
|
||||||
|
print_stake_state(stake_account.lamports, &stake_state, use_lamports_unit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok("".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn process_show_validators(rpc_client: &RpcClient, use_lamports_unit: bool) -> ProcessResult {
|
pub fn process_show_validators(rpc_client: &RpcClient, use_lamports_unit: bool) -> ProcessResult {
|
||||||
let epoch_schedule = rpc_client.get_epoch_schedule()?;
|
let epoch_schedule = rpc_client.get_epoch_schedule()?;
|
||||||
let vote_accounts = rpc_client.get_vote_accounts()?;
|
let vote_accounts = rpc_client.get_vote_accounts()?;
|
||||||
|
@ -24,10 +24,9 @@ use solana_sdk::{
|
|||||||
},
|
},
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
};
|
};
|
||||||
use solana_stake_program::stake_state::Meta;
|
|
||||||
use solana_stake_program::{
|
use solana_stake_program::{
|
||||||
stake_instruction::{self, StakeError},
|
stake_instruction::{self, StakeError},
|
||||||
stake_state::{Authorized, Lockup, StakeAuthorize, StakeState},
|
stake_state::{Authorized, Lockup, Meta, StakeAuthorize, StakeState},
|
||||||
};
|
};
|
||||||
use solana_vote_program::vote_state::VoteState;
|
use solana_vote_program::vote_state::VoteState;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
@ -969,20 +968,7 @@ pub fn process_redeem_vote_credits(
|
|||||||
log_instruction_custom_error::<StakeError>(result)
|
log_instruction_custom_error::<StakeError>(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_show_stake_account(
|
pub fn print_stake_state(stake_lamports: u64, stake_state: &StakeState, use_lamports_unit: bool) {
|
||||||
rpc_client: &RpcClient,
|
|
||||||
_config: &CliConfig,
|
|
||||||
stake_account_pubkey: &Pubkey,
|
|
||||||
use_lamports_unit: bool,
|
|
||||||
) -> ProcessResult {
|
|
||||||
let stake_account = rpc_client.get_account(stake_account_pubkey)?;
|
|
||||||
if stake_account.owner != solana_stake_program::id() {
|
|
||||||
return Err(CliError::RpcRequestError(format!(
|
|
||||||
"{:?} is not a stake account",
|
|
||||||
stake_account_pubkey
|
|
||||||
))
|
|
||||||
.into());
|
|
||||||
}
|
|
||||||
fn show_authorized(authorized: &Authorized) {
|
fn show_authorized(authorized: &Authorized) {
|
||||||
println!("authorized staker: {}", authorized.staker);
|
println!("authorized staker: {}", authorized.staker);
|
||||||
println!("authorized withdrawer: {}", authorized.staker);
|
println!("authorized withdrawer: {}", authorized.staker);
|
||||||
@ -991,16 +977,16 @@ pub fn process_show_stake_account(
|
|||||||
println!("lockup epoch: {}", lockup.epoch);
|
println!("lockup epoch: {}", lockup.epoch);
|
||||||
println!("lockup custodian: {}", lockup.custodian);
|
println!("lockup custodian: {}", lockup.custodian);
|
||||||
}
|
}
|
||||||
match stake_account.state() {
|
match stake_state {
|
||||||
Ok(StakeState::Stake(
|
StakeState::Stake(
|
||||||
Meta {
|
Meta {
|
||||||
authorized, lockup, ..
|
authorized, lockup, ..
|
||||||
},
|
},
|
||||||
stake,
|
stake,
|
||||||
)) => {
|
) => {
|
||||||
println!(
|
println!(
|
||||||
"total stake: {}",
|
"total stake: {}",
|
||||||
build_balance_message(stake_account.lamports, use_lamports_unit, true)
|
build_balance_message(stake_lamports, use_lamports_unit, true)
|
||||||
);
|
);
|
||||||
println!("credits observed: {}", stake.credits_observed);
|
println!("credits observed: {}", stake.credits_observed);
|
||||||
println!(
|
println!(
|
||||||
@ -1026,16 +1012,40 @@ pub fn process_show_stake_account(
|
|||||||
}
|
}
|
||||||
show_authorized(&authorized);
|
show_authorized(&authorized);
|
||||||
show_lockup(&lockup);
|
show_lockup(&lockup);
|
||||||
Ok("".to_string())
|
|
||||||
}
|
}
|
||||||
Ok(StakeState::RewardsPool) => Ok("Stake account is a rewards pool".to_string()),
|
StakeState::RewardsPool => println!("stake account is a rewards pool"),
|
||||||
Ok(StakeState::Uninitialized) => Ok("Stake account is uninitialized".to_string()),
|
StakeState::Uninitialized => println!("stake account is uninitialized"),
|
||||||
Ok(StakeState::Initialized(Meta {
|
StakeState::Initialized(Meta {
|
||||||
authorized, lockup, ..
|
authorized, lockup, ..
|
||||||
})) => {
|
}) => {
|
||||||
println!("Stake account is undelegated");
|
println!(
|
||||||
|
"total stake: {}",
|
||||||
|
build_balance_message(stake_lamports, use_lamports_unit, true)
|
||||||
|
);
|
||||||
|
println!("stake account is undelegated");
|
||||||
show_authorized(&authorized);
|
show_authorized(&authorized);
|
||||||
show_lockup(&lockup);
|
show_lockup(&lockup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_show_stake_account(
|
||||||
|
rpc_client: &RpcClient,
|
||||||
|
_config: &CliConfig,
|
||||||
|
stake_account_pubkey: &Pubkey,
|
||||||
|
use_lamports_unit: bool,
|
||||||
|
) -> ProcessResult {
|
||||||
|
let stake_account = rpc_client.get_account(stake_account_pubkey)?;
|
||||||
|
if stake_account.owner != solana_stake_program::id() {
|
||||||
|
return Err(CliError::RpcRequestError(format!(
|
||||||
|
"{:?} is not a stake account",
|
||||||
|
stake_account_pubkey
|
||||||
|
))
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
match stake_account.state() {
|
||||||
|
Ok(stake_state) => {
|
||||||
|
print_stake_state(stake_account.lamports, &stake_state, use_lamports_unit);
|
||||||
Ok("".to_string())
|
Ok("".to_string())
|
||||||
}
|
}
|
||||||
Err(err) => Err(CliError::RpcRequestError(format!(
|
Err(err) => Err(CliError::RpcRequestError(format!(
|
||||||
|
Reference in New Issue
Block a user