solana-cli: selectively require keypair (#6477)

* Make parse_command consistent

* Strip pubkey out of parse_stake_create_account

* Move validator-info args into module

* Strip pubkey out of parse_validator_info_command

* Strip pubkey out of parse_vote_create_account

* Strip pubkey out of balance parsing

* Strip pubkey out of parse pay

* Only verify keypair existence if command requires it

* Use struct instead of tuple
This commit is contained in:
Tyera Eulberg 2019-10-21 17:08:09 -06:00 committed by GitHub
parent 564c14a2c6
commit 8319fa05d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 842 additions and 571 deletions

View File

@ -6,7 +6,7 @@ use chrono::prelude::*;
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
use log::*; use log::*;
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
use serde_json::{self, json}; use serde_json::{self, json, Value};
use solana_budget_api::budget_instruction::{self, BudgetError}; use solana_budget_api::budget_instruction::{self, BudgetError};
use solana_client::{client_error::ClientError, rpc_client::RpcClient}; use solana_client::{client_error::ClientError, rpc_client::RpcClient};
#[cfg(not(test))] #[cfg(not(test))]
@ -28,9 +28,9 @@ use solana_sdk::{
system_transaction, system_transaction,
transaction::{Transaction, TransactionError}, transaction::{Transaction, TransactionError},
}; };
use solana_stake_api::stake_state::{Authorized, Lockup, StakeAuthorize}; use solana_stake_api::stake_state::{Lockup, StakeAuthorize};
use solana_storage_api::storage_instruction::StorageAccountType; use solana_storage_api::storage_instruction::StorageAccountType;
use solana_vote_api::vote_state::{VoteAuthorize, VoteInit}; use solana_vote_api::vote_state::VoteAuthorize;
use std::{ use std::{
fs::File, fs::File,
io::{Read, Write}, io::{Read, Write},
@ -63,7 +63,13 @@ pub enum CliCommand {
// Program Deployment // Program Deployment
Deploy(String), Deploy(String),
// Stake Commands // Stake Commands
CreateStakeAccount(Pubkey, Authorized, Lockup, u64), CreateStakeAccount {
stake_account_pubkey: Pubkey,
staker: Option<Pubkey>,
withdrawer: Option<Pubkey>,
lockup: Lockup,
lamports: u64,
},
DeactivateStake(Pubkey), DeactivateStake(Pubkey),
DelegateStake(Pubkey, Pubkey, bool), DelegateStake(Pubkey, Pubkey, bool),
RedeemVoteCredits(Pubkey, Pubkey), RedeemVoteCredits(Pubkey, Pubkey),
@ -86,9 +92,19 @@ pub enum CliCommand {
ShowStorageAccount(Pubkey), ShowStorageAccount(Pubkey),
// Validator Info Commands // Validator Info Commands
GetValidatorInfo(Option<Pubkey>), GetValidatorInfo(Option<Pubkey>),
SetValidatorInfo(ValidatorInfo, Option<Pubkey>), SetValidatorInfo {
validator_info: Value,
force_keybase: bool,
info_pubkey: Option<Pubkey>,
},
// Vote Commands // Vote Commands
CreateVoteAccount(Pubkey, VoteInit), CreateVoteAccount {
vote_account_pubkey: Pubkey,
node_pubkey: Pubkey,
authorized_voter: Option<Pubkey>,
authorized_withdrawer: Option<Pubkey>,
commission: u8,
},
ShowVoteAccount { ShowVoteAccount {
pubkey: Pubkey, pubkey: Pubkey,
use_lamports_unit: bool, use_lamports_unit: bool,
@ -108,7 +124,7 @@ pub enum CliCommand {
use_lamports_unit: bool, use_lamports_unit: bool,
}, },
Balance { Balance {
pubkey: Pubkey, pubkey: Option<Pubkey>,
use_lamports_unit: bool, use_lamports_unit: bool,
}, },
Cancel(Pubkey), Cancel(Pubkey),
@ -119,7 +135,7 @@ pub enum CliCommand {
timestamp: Option<DateTime<Utc>>, timestamp: Option<DateTime<Utc>>,
timestamp_pubkey: Option<Pubkey>, timestamp_pubkey: Option<Pubkey>,
witnesses: Option<Vec<Pubkey>>, witnesses: Option<Vec<Pubkey>>,
cancelable: Option<Pubkey>, cancelable: bool,
}, },
ShowAccount { ShowAccount {
pubkey: Pubkey, pubkey: Pubkey,
@ -130,6 +146,12 @@ pub enum CliCommand {
Witness(Pubkey, Pubkey), // Witness(to, process_id) Witness(Pubkey, Pubkey), // Witness(to, process_id)
} }
#[derive(Debug, PartialEq)]
pub struct CliCommandInfo {
pub command: CliCommand,
pub require_keypair: bool,
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum CliError { pub enum CliError {
BadParameter(String), BadParameter(String),
@ -161,7 +183,7 @@ pub struct CliConfig {
pub command: CliCommand, pub command: CliCommand,
pub json_rpc_url: String, pub json_rpc_url: String,
pub keypair: Keypair, pub keypair: Keypair,
pub keypair_path: String, pub keypair_path: Option<String>,
pub rpc_client: Option<RpcClient>, pub rpc_client: Option<RpcClient>,
} }
@ -172,40 +194,53 @@ impl Default for CliConfig {
CliConfig { CliConfig {
command: CliCommand::Balance { command: CliCommand::Balance {
pubkey: Pubkey::default(), pubkey: Some(Pubkey::default()),
use_lamports_unit: false, use_lamports_unit: false,
}, },
json_rpc_url: "http://127.0.0.1:8899".to_string(), json_rpc_url: "http://127.0.0.1:8899".to_string(),
keypair: Keypair::new(), keypair: Keypair::new(),
keypair_path: keypair_path.to_str().unwrap().to_string(), keypair_path: Some(keypair_path.to_str().unwrap().to_string()),
rpc_client: None, rpc_client: None,
} }
} }
} }
pub fn parse_command( pub fn parse_command(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, Box<dyn error::Error>> {
pubkey: &Pubkey,
matches: &ArgMatches<'_>,
) -> Result<CliCommand, Box<dyn error::Error>> {
let response = match matches.subcommand() { let response = match matches.subcommand() {
// Cluster Query Commands // Cluster Query Commands
("cluster-version", Some(_matches)) => Ok(CliCommand::ClusterVersion), ("cluster-version", Some(_matches)) => Ok(CliCommandInfo {
("fees", Some(_fees_matches)) => Ok(CliCommand::Fees), command: CliCommand::ClusterVersion,
("get-epoch-info", Some(_matches)) => Ok(CliCommand::GetEpochInfo), require_keypair: false,
("get-genesis-blockhash", Some(_matches)) => Ok(CliCommand::GetGenesisBlockhash), }),
("get-slot", Some(_matches)) => Ok(CliCommand::GetSlot), ("fees", Some(_matches)) => Ok(CliCommandInfo {
("get-transaction-count", Some(_matches)) => Ok(CliCommand::GetTransactionCount), command: CliCommand::Fees,
require_keypair: false,
}),
("get-epoch-info", Some(_matches)) => Ok(CliCommandInfo {
command: CliCommand::GetEpochInfo,
require_keypair: false,
}),
("get-genesis-blockhash", Some(_matches)) => Ok(CliCommandInfo {
command: CliCommand::GetGenesisBlockhash,
require_keypair: false,
}),
("get-slot", Some(_matches)) => Ok(CliCommandInfo {
command: CliCommand::GetSlot,
require_keypair: false,
}),
("get-transaction-count", Some(_matches)) => Ok(CliCommandInfo {
command: CliCommand::GetTransactionCount,
require_keypair: false,
}),
("ping", Some(matches)) => parse_cluster_ping(matches), ("ping", Some(matches)) => parse_cluster_ping(matches),
("show-validators", Some(matches)) => parse_show_validators(matches), ("show-validators", Some(matches)) => parse_show_validators(matches),
// Program Deployment // Program Deployment
("deploy", Some(deploy_matches)) => Ok(CliCommand::Deploy( ("deploy", Some(matches)) => Ok(CliCommandInfo {
deploy_matches command: CliCommand::Deploy(matches.value_of("program_location").unwrap().to_string()),
.value_of("program_location") require_keypair: true,
.unwrap() }),
.to_string(),
)),
// Stake Commands // Stake Commands
("create-stake-account", Some(matches)) => parse_stake_create_account(pubkey, matches), ("create-stake-account", Some(matches)) => parse_stake_create_account(matches),
("delegate-stake", Some(matches)) => parse_stake_delegate_stake(matches), ("delegate-stake", Some(matches)) => parse_stake_delegate_stake(matches),
("withdraw-stake", Some(matches)) => parse_stake_withdraw_stake(matches), ("withdraw-stake", Some(matches)) => parse_stake_withdraw_stake(matches),
("deactivate-stake", Some(matches)) => parse_stake_deactivate_stake(matches), ("deactivate-stake", Some(matches)) => parse_stake_deactivate_stake(matches),
@ -228,7 +263,7 @@ pub fn parse_command(
("show-storage-account", Some(matches)) => parse_storage_get_account_command(matches), ("show-storage-account", Some(matches)) => parse_storage_get_account_command(matches),
// Validator Info Commands // Validator Info Commands
("validator-info", Some(matches)) => match matches.subcommand() { ("validator-info", Some(matches)) => match matches.subcommand() {
("publish", Some(matches)) => parse_validator_info_command(matches, pubkey), ("publish", Some(matches)) => parse_validator_info_command(matches),
("get", Some(matches)) => parse_get_validator_info_command(matches), ("get", Some(matches)) => parse_get_validator_info_command(matches),
("", None) => { ("", None) => {
eprintln!("{}", matches.usage()); eprintln!("{}", matches.usage());
@ -239,7 +274,7 @@ pub fn parse_command(
_ => unreachable!(), _ => unreachable!(),
}, },
// Vote Commands // Vote Commands
("create-vote-account", Some(matches)) => parse_vote_create_account(pubkey, matches), ("create-vote-account", Some(matches)) => parse_vote_create_account(matches),
("vote-authorize-voter", Some(matches)) => { ("vote-authorize-voter", Some(matches)) => {
parse_vote_authorize(matches, VoteAuthorize::Voter) parse_vote_authorize(matches, VoteAuthorize::Voter)
} }
@ -249,9 +284,12 @@ pub fn parse_command(
("show-vote-account", Some(matches)) => parse_vote_get_account_command(matches), ("show-vote-account", Some(matches)) => parse_vote_get_account_command(matches),
("uptime", Some(matches)) => parse_vote_uptime_command(matches), ("uptime", Some(matches)) => parse_vote_uptime_command(matches),
// Wallet Commands // Wallet Commands
("address", Some(_address_matches)) => Ok(CliCommand::Address), ("address", Some(_matches)) => Ok(CliCommandInfo {
("airdrop", Some(airdrop_matches)) => { command: CliCommand::Address,
let drone_port = airdrop_matches require_keypair: true,
}),
("airdrop", Some(matches)) => {
let drone_port = matches
.value_of("drone_port") .value_of("drone_port")
.unwrap() .unwrap()
.parse() .parse()
@ -272,102 +310,116 @@ pub fn parse_command(
} else { } else {
None None
}; };
let lamports = amount_of(airdrop_matches, "amount", "unit").expect("Invalid amount"); let lamports = amount_of(matches, "amount", "unit").expect("Invalid amount");
let use_lamports_unit = airdrop_matches.value_of("unit").is_some() let use_lamports_unit = matches.value_of("unit").is_some()
&& airdrop_matches.value_of("unit").unwrap() == "lamports"; && matches.value_of("unit").unwrap() == "lamports";
Ok(CliCommand::Airdrop { Ok(CliCommandInfo {
drone_host, command: CliCommand::Airdrop {
drone_port, drone_host,
lamports, drone_port,
use_lamports_unit, lamports,
use_lamports_unit,
},
require_keypair: true,
}) })
} }
("balance", Some(balance_matches)) => { ("balance", Some(matches)) => {
let pubkey = pubkey_of(&balance_matches, "pubkey").unwrap_or(*pubkey); let pubkey = pubkey_of(&matches, "pubkey");
let use_lamports_unit = balance_matches.is_present("lamports"); println!("{:?}", pubkey);
Ok(CliCommand::Balance { Ok(CliCommandInfo {
pubkey, command: CliCommand::Balance {
use_lamports_unit, pubkey,
use_lamports_unit: matches.is_present("lamports"),
},
require_keypair: pubkey.is_none(),
}) })
} }
("cancel", Some(cancel_matches)) => { ("cancel", Some(matches)) => {
let process_id = value_of(cancel_matches, "process_id").unwrap(); let process_id = value_of(matches, "process_id").unwrap();
Ok(CliCommand::Cancel(process_id)) Ok(CliCommandInfo {
command: CliCommand::Cancel(process_id),
require_keypair: true,
})
} }
("confirm", Some(confirm_matches)) => { ("confirm", Some(matches)) => match matches.value_of("signature").unwrap().parse() {
match confirm_matches.value_of("signature").unwrap().parse() { Ok(signature) => Ok(CliCommandInfo {
Ok(signature) => Ok(CliCommand::Confirm(signature)), command: CliCommand::Confirm(signature),
_ => { require_keypair: false,
eprintln!("{}", confirm_matches.usage()); }),
Err(CliError::BadParameter("Invalid signature".to_string())) _ => {
} eprintln!("{}", matches.usage());
Err(CliError::BadParameter("Invalid signature".to_string()))
} }
} },
("pay", Some(pay_matches)) => { ("pay", Some(matches)) => {
let lamports = amount_of(pay_matches, "amount", "unit").expect("Invalid amount"); let lamports = amount_of(matches, "amount", "unit").expect("Invalid amount");
let to = value_of(&pay_matches, "to").unwrap_or(*pubkey); let to = value_of(&matches, "to").unwrap();
let timestamp = if pay_matches.is_present("timestamp") { let timestamp = if matches.is_present("timestamp") {
// Parse input for serde_json // Parse input for serde_json
let date_string = if !pay_matches.value_of("timestamp").unwrap().contains('Z') { let date_string = if !matches.value_of("timestamp").unwrap().contains('Z') {
format!("\"{}Z\"", pay_matches.value_of("timestamp").unwrap()) format!("\"{}Z\"", matches.value_of("timestamp").unwrap())
} else { } else {
format!("\"{}\"", pay_matches.value_of("timestamp").unwrap()) format!("\"{}\"", matches.value_of("timestamp").unwrap())
}; };
Some(serde_json::from_str(&date_string)?) Some(serde_json::from_str(&date_string)?)
} else { } else {
None None
}; };
let timestamp_pubkey = value_of(&pay_matches, "timestamp_pubkey"); let timestamp_pubkey = value_of(&matches, "timestamp_pubkey");
let witnesses = values_of(&pay_matches, "witness"); let witnesses = values_of(&matches, "witness");
let cancelable = if pay_matches.is_present("cancelable") { let cancelable = matches.is_present("cancelable");
Some(*pubkey)
} else {
None
};
Ok(CliCommand::Pay { Ok(CliCommandInfo {
lamports, command: CliCommand::Pay {
to, lamports,
timestamp, to,
timestamp_pubkey, timestamp,
witnesses, timestamp_pubkey,
cancelable, witnesses,
cancelable,
},
require_keypair: true,
}) })
} }
("show-account", Some(matches)) => { ("show-account", Some(matches)) => {
let account_pubkey = pubkey_of(matches, "account_pubkey").unwrap(); let account_pubkey = pubkey_of(matches, "account_pubkey").unwrap();
let output_file = matches.value_of("output_file"); let output_file = matches.value_of("output_file");
let use_lamports_unit = matches.is_present("lamports"); let use_lamports_unit = matches.is_present("lamports");
Ok(CliCommand::ShowAccount { Ok(CliCommandInfo {
pubkey: account_pubkey, command: CliCommand::ShowAccount {
output_file: output_file.map(ToString::to_string), pubkey: account_pubkey,
use_lamports_unit, output_file: output_file.map(ToString::to_string),
use_lamports_unit,
},
require_keypair: false,
}) })
} }
("send-signature", Some(sig_matches)) => { ("send-signature", Some(matches)) => {
let to = value_of(&sig_matches, "to").unwrap(); let to = value_of(&matches, "to").unwrap();
let process_id = value_of(&sig_matches, "process_id").unwrap(); let process_id = value_of(&matches, "process_id").unwrap();
Ok(CliCommand::Witness(to, process_id)) Ok(CliCommandInfo {
command: CliCommand::Witness(to, process_id),
require_keypair: true,
})
} }
("send-timestamp", Some(timestamp_matches)) => { ("send-timestamp", Some(matches)) => {
let to = value_of(&timestamp_matches, "to").unwrap(); let to = value_of(&matches, "to").unwrap();
let process_id = value_of(&timestamp_matches, "process_id").unwrap(); let process_id = value_of(&matches, "process_id").unwrap();
let dt = if timestamp_matches.is_present("datetime") { let dt = if matches.is_present("datetime") {
// Parse input for serde_json // Parse input for serde_json
let date_string = if !timestamp_matches let date_string = if !matches.value_of("datetime").unwrap().contains('Z') {
.value_of("datetime") format!("\"{}Z\"", matches.value_of("datetime").unwrap())
.unwrap()
.contains('Z')
{
format!("\"{}Z\"", timestamp_matches.value_of("datetime").unwrap())
} else { } else {
format!("\"{}\"", timestamp_matches.value_of("datetime").unwrap()) format!("\"{}\"", matches.value_of("datetime").unwrap())
}; };
serde_json::from_str(&date_string)? serde_json::from_str(&date_string)?
} else { } else {
Utc::now() Utc::now()
}; };
Ok(CliCommand::TimeElapsed(to, process_id, dt)) Ok(CliCommandInfo {
command: CliCommand::TimeElapsed(to, process_id, dt),
require_keypair: true,
})
} }
("", None) => { ("", None) => {
eprintln!("{}", matches.usage()); eprintln!("{}", matches.usage());
@ -457,11 +509,13 @@ fn process_airdrop(
} }
fn process_balance( fn process_balance(
pubkey: &Pubkey,
rpc_client: &RpcClient, rpc_client: &RpcClient,
config: &CliConfig,
pubkey: &Option<Pubkey>,
use_lamports_unit: bool, use_lamports_unit: bool,
) -> ProcessResult { ) -> ProcessResult {
let balance = rpc_client.retry_get_balance(pubkey, 5)?; let pubkey = pubkey.unwrap_or(config.keypair.pubkey());
let balance = rpc_client.retry_get_balance(&pubkey, 5)?;
match balance { match balance {
Some(lamports) => Ok(build_balance_message(lamports, use_lamports_unit)), Some(lamports) => Ok(build_balance_message(lamports, use_lamports_unit)),
None => Err( None => Err(
@ -600,7 +654,7 @@ fn process_pay(
timestamp: Option<DateTime<Utc>>, timestamp: Option<DateTime<Utc>>,
timestamp_pubkey: Option<Pubkey>, timestamp_pubkey: Option<Pubkey>,
witnesses: &Option<Vec<Pubkey>>, witnesses: &Option<Vec<Pubkey>>,
cancelable: Option<Pubkey>, cancelable: bool,
) -> ProcessResult { ) -> ProcessResult {
check_unique_pubkeys( check_unique_pubkeys(
(&config.keypair.pubkey(), "cli keypair".to_string()), (&config.keypair.pubkey(), "cli keypair".to_string()),
@ -608,6 +662,12 @@ fn process_pay(
)?; )?;
let (blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; let (blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let cancelable = if cancelable {
Some(config.keypair.pubkey())
} else {
None
};
if timestamp == None && *witnesses == None { if timestamp == None && *witnesses == None {
let mut tx = system_transaction::transfer(&config.keypair, to, lamports, blockhash); let mut tx = system_transaction::transfer(&config.keypair, to, lamports, blockhash);
check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?; check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?;
@ -725,7 +785,9 @@ fn process_witness(
} }
pub fn process_command(config: &CliConfig) -> ProcessResult { pub fn process_command(config: &CliConfig) -> ProcessResult {
println_name_value("Keypair:", &config.keypair_path); if let Some(keypair_path) = &config.keypair_path {
println_name_value("Keypair:", keypair_path);
}
if let CliCommand::Address = config.command { if let CliCommand::Address = config.command {
// Get address of this client // Get address of this client
return Ok(format!("{}", config.keypair.pubkey())); return Ok(format!("{}", config.keypair.pubkey()));
@ -770,16 +832,21 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
// Stake Commands // Stake Commands
// Create stake account // Create stake account
CliCommand::CreateStakeAccount(stake_account_pubkey, authorized, lockup, lamports) => { CliCommand::CreateStakeAccount {
process_create_stake_account( stake_account_pubkey,
&rpc_client, staker,
config, withdrawer,
&stake_account_pubkey, lockup,
&authorized, lamports,
lockup, } => process_create_stake_account(
*lamports, &rpc_client,
) config,
} &stake_account_pubkey,
staker,
withdrawer,
lockup,
*lamports,
),
// Deactivate stake account // Deactivate stake account
CliCommand::DeactivateStake(stake_account_pubkey) => { CliCommand::DeactivateStake(stake_account_pubkey) => {
process_deactivate_stake_account(&rpc_client, config, &stake_account_pubkey) process_deactivate_stake_account(&rpc_client, config, &stake_account_pubkey)
@ -866,16 +933,36 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
process_get_validator_info(&rpc_client, *info_pubkey) process_get_validator_info(&rpc_client, *info_pubkey)
} }
// Publish validator info // Publish validator info
CliCommand::SetValidatorInfo(validator_info, info_pubkey) => { CliCommand::SetValidatorInfo {
process_set_validator_info(&rpc_client, config, &validator_info, *info_pubkey) validator_info,
} force_keybase,
info_pubkey,
} => process_set_validator_info(
&rpc_client,
config,
&validator_info,
*force_keybase,
*info_pubkey,
),
// Vote Commands // Vote Commands
// Create vote account // Create vote account
CliCommand::CreateVoteAccount(vote_account_pubkey, vote_init) => { CliCommand::CreateVoteAccount {
process_create_vote_account(&rpc_client, config, &vote_account_pubkey, &vote_init) vote_account_pubkey,
} node_pubkey,
authorized_voter,
authorized_withdrawer,
commission,
} => process_create_vote_account(
&rpc_client,
config,
&vote_account_pubkey,
&node_pubkey,
authorized_voter,
authorized_withdrawer,
*commission,
),
CliCommand::ShowVoteAccount { CliCommand::ShowVoteAccount {
pubkey: vote_account_pubkey, pubkey: vote_account_pubkey,
use_lamports_unit, use_lamports_unit,
@ -937,7 +1024,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
CliCommand::Balance { CliCommand::Balance {
pubkey, pubkey,
use_lamports_unit, use_lamports_unit,
} => process_balance(&pubkey, &rpc_client, *use_lamports_unit), } => process_balance(&rpc_client, config, &pubkey, *use_lamports_unit),
// Cancel a contract by contract Pubkey // Cancel a contract by contract Pubkey
CliCommand::Cancel(pubkey) => process_cancel(&rpc_client, config, &pubkey), CliCommand::Cancel(pubkey) => process_cancel(&rpc_client, config, &pubkey),
// Confirm the last client transaction by signature // Confirm the last client transaction by signature
@ -1311,78 +1398,7 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
.help("Display balance in lamports instead of SOL"), .help("Display balance in lamports instead of SOL"),
), ),
) )
.subcommand( .validator_info_subcommands()
SubCommand::with_name("validator-info")
.about("Publish/get Validator info on Solana")
.subcommand(
SubCommand::with_name("publish")
.about("Publish Validator info on Solana")
.arg(
Arg::with_name("info_pubkey")
.short("p")
.long("info-pubkey")
.value_name("PUBKEY")
.takes_value(true)
.validator(is_pubkey)
.help("The pubkey of the Validator info account to update"),
)
.arg(
Arg::with_name("name")
.index(1)
.value_name("NAME")
.takes_value(true)
.required(true)
.validator(is_short_field)
.help("Validator name"),
)
.arg(
Arg::with_name("website")
.short("w")
.long("website")
.value_name("URL")
.takes_value(true)
.validator(check_url)
.help("Validator website url"),
)
.arg(
Arg::with_name("keybase_username")
.short("n")
.long("keybase")
.value_name("USERNAME")
.takes_value(true)
.validator(is_short_field)
.help("Validator Keybase username"),
)
.arg(
Arg::with_name("details")
.short("d")
.long("details")
.value_name("DETAILS")
.takes_value(true)
.validator(check_details_length)
.help("Validator description")
)
.arg(
Arg::with_name("force")
.long("force")
.takes_value(false)
.hidden(true) // Don't document this argument to discourage its use
.help("Override keybase username validity check"),
),
)
.subcommand(
SubCommand::with_name("get")
.about("Get and parse Solana Validator info")
.arg(
Arg::with_name("info_pubkey")
.index(1)
.value_name("PUBKEY")
.takes_value(true)
.validator(is_pubkey)
.help("The pubkey of the Validator info account; without this argument, returns all"),
),
)
)
.vote_subcommands() .vote_subcommands()
} }
@ -1416,8 +1432,7 @@ mod tests {
fn test_bad_amount() { fn test_bad_amount() {
let test_commands = app("test", "desc", "version"); let test_commands = app("test", "desc", "version");
let test_bad_airdrop = test_commands.get_matches_from(vec!["test", "airdrop", "notint"]); let test_bad_airdrop = test_commands.get_matches_from(vec!["test", "airdrop", "notint"]);
let pubkey = Pubkey::new_rand(); let _ignored = parse_command(&test_bad_airdrop).unwrap();
let _ignored = parse_command(&pubkey, &test_bad_airdrop).unwrap();
} }
#[test] #[test]
@ -1437,12 +1452,15 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "airdrop", "50", "lamports"]); .get_matches_from(vec!["test", "airdrop", "50", "lamports"]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_airdrop).unwrap(), parse_command(&test_airdrop).unwrap(),
CliCommand::Airdrop { CliCommandInfo {
drone_host: None, command: CliCommand::Airdrop {
drone_port: solana_drone::drone::DRONE_PORT, drone_host: None,
lamports: 50, drone_port: solana_drone::drone::DRONE_PORT,
use_lamports_unit: true, lamports: 50,
use_lamports_unit: true,
},
require_keypair: true,
} }
); );
@ -1456,10 +1474,13 @@ mod tests {
&keypair.pubkey().to_string(), &keypair.pubkey().to_string(),
]); ]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_balance).unwrap(), parse_command(&test_balance).unwrap(),
CliCommand::Balance { CliCommandInfo {
pubkey: keypair.pubkey(), command: CliCommand::Balance {
use_lamports_unit: false pubkey: Some(keypair.pubkey()),
use_lamports_unit: false
},
require_keypair: false
} }
); );
let test_balance = test_commands.clone().get_matches_from(vec![ let test_balance = test_commands.clone().get_matches_from(vec![
@ -1469,10 +1490,27 @@ mod tests {
"--lamports", "--lamports",
]); ]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_balance).unwrap(), parse_command(&test_balance).unwrap(),
CliCommand::Balance { CliCommandInfo {
pubkey: keypair.pubkey(), command: CliCommand::Balance {
use_lamports_unit: true pubkey: Some(keypair.pubkey()),
use_lamports_unit: true
},
require_keypair: false
}
);
let test_balance =
test_commands
.clone()
.get_matches_from(vec!["test", "balance", "--lamports"]);
assert_eq!(
parse_command(&test_balance).unwrap(),
CliCommandInfo {
command: CliCommand::Balance {
pubkey: None,
use_lamports_unit: true
},
require_keypair: true
} }
); );
@ -1482,8 +1520,11 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "cancel", &pubkey_string]); .get_matches_from(vec!["test", "cancel", &pubkey_string]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_cancel).unwrap(), parse_command(&test_cancel).unwrap(),
CliCommand::Cancel(pubkey) CliCommandInfo {
command: CliCommand::Cancel(pubkey),
require_keypair: true
}
); );
// Test Confirm Subcommand // Test Confirm Subcommand
@ -1494,13 +1535,16 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "confirm", &signature_string]); .get_matches_from(vec!["test", "confirm", &signature_string]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_confirm).unwrap(), parse_command(&test_confirm).unwrap(),
CliCommand::Confirm(signature) CliCommandInfo {
command: CliCommand::Confirm(signature),
require_keypair: false
}
); );
let test_bad_signature = test_commands let test_bad_signature = test_commands
.clone() .clone()
.get_matches_from(vec!["test", "confirm", "deadbeef"]); .get_matches_from(vec!["test", "confirm", "deadbeef"]);
assert!(parse_command(&pubkey, &test_bad_signature).is_err()); assert!(parse_command(&test_bad_signature).is_err());
// Test Deploy Subcommand // Test Deploy Subcommand
let test_deploy = let test_deploy =
@ -1508,8 +1552,11 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "deploy", "/Users/test/program.o"]); .get_matches_from(vec!["test", "deploy", "/Users/test/program.o"]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_deploy).unwrap(), parse_command(&test_deploy).unwrap(),
CliCommand::Deploy("/Users/test/program.o".to_string()) CliCommandInfo {
command: CliCommand::Deploy("/Users/test/program.o".to_string()),
require_keypair: true
}
); );
// Test Simple Pay Subcommand // Test Simple Pay Subcommand
@ -1521,14 +1568,17 @@ mod tests {
"lamports", "lamports",
]); ]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_pay).unwrap(), parse_command(&test_pay).unwrap(),
CliCommand::Pay { CliCommandInfo {
lamports: 50, command: CliCommand::Pay {
to: pubkey, lamports: 50,
timestamp: None, to: pubkey,
timestamp_pubkey: None, timestamp: None,
witnesses: None, timestamp_pubkey: None,
cancelable: None witnesses: None,
cancelable: false,
},
require_keypair: true
} }
); );
@ -1545,14 +1595,17 @@ mod tests {
&witness1_string, &witness1_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_pay_multiple_witnesses).unwrap(), parse_command(&test_pay_multiple_witnesses).unwrap(),
CliCommand::Pay { CliCommandInfo {
lamports: 50, command: CliCommand::Pay {
to: pubkey, lamports: 50,
timestamp: None, to: pubkey,
timestamp_pubkey: None, timestamp: None,
witnesses: Some(vec![witness0, witness1]), timestamp_pubkey: None,
cancelable: None witnesses: Some(vec![witness0, witness1]),
cancelable: false,
},
require_keypair: true
} }
); );
let test_pay_single_witness = test_commands.clone().get_matches_from(vec![ let test_pay_single_witness = test_commands.clone().get_matches_from(vec![
@ -1565,14 +1618,17 @@ mod tests {
&witness0_string, &witness0_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_pay_single_witness).unwrap(), parse_command(&test_pay_single_witness).unwrap(),
CliCommand::Pay { CliCommandInfo {
lamports: 50, command: CliCommand::Pay {
to: pubkey, lamports: 50,
timestamp: None, to: pubkey,
timestamp_pubkey: None, timestamp: None,
witnesses: Some(vec![witness0]), timestamp_pubkey: None,
cancelable: None witnesses: Some(vec![witness0]),
cancelable: false,
},
require_keypair: true
} }
); );
@ -1589,14 +1645,17 @@ mod tests {
&witness0_string, &witness0_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_pay_timestamp).unwrap(), parse_command(&test_pay_timestamp).unwrap(),
CliCommand::Pay { CliCommandInfo {
lamports: 50, command: CliCommand::Pay {
to: pubkey, lamports: 50,
timestamp: Some(dt), to: pubkey,
timestamp_pubkey: Some(witness0), timestamp: Some(dt),
witnesses: None, timestamp_pubkey: Some(witness0),
cancelable: None witnesses: None,
cancelable: false,
},
require_keypair: true
} }
); );
@ -1608,8 +1667,11 @@ mod tests {
&pubkey_string, &pubkey_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_send_signature).unwrap(), parse_command(&test_send_signature).unwrap(),
CliCommand::Witness(pubkey, pubkey) CliCommandInfo {
command: CliCommand::Witness(pubkey, pubkey),
require_keypair: true
}
); );
let test_pay_multiple_witnesses = test_commands.clone().get_matches_from(vec![ let test_pay_multiple_witnesses = test_commands.clone().get_matches_from(vec![
"test", "test",
@ -1627,14 +1689,17 @@ mod tests {
&witness1_string, &witness1_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_pay_multiple_witnesses).unwrap(), parse_command(&test_pay_multiple_witnesses).unwrap(),
CliCommand::Pay { CliCommandInfo {
lamports: 50, command: CliCommand::Pay {
to: pubkey, lamports: 50,
timestamp: Some(dt), to: pubkey,
timestamp_pubkey: Some(witness0), timestamp: Some(dt),
witnesses: Some(vec![witness0, witness1]), timestamp_pubkey: Some(witness0),
cancelable: None witnesses: Some(vec![witness0, witness1]),
cancelable: false,
},
require_keypair: true
} }
); );
@ -1648,8 +1713,11 @@ mod tests {
"2018-09-19T17:30:59", "2018-09-19T17:30:59",
]); ]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_send_timestamp).unwrap(), parse_command(&test_send_timestamp).unwrap(),
CliCommand::TimeElapsed(pubkey, pubkey, dt) CliCommandInfo {
command: CliCommand::TimeElapsed(pubkey, pubkey, dt),
require_keypair: true
}
); );
let test_bad_timestamp = test_commands.clone().get_matches_from(vec![ let test_bad_timestamp = test_commands.clone().get_matches_from(vec![
"test", "test",
@ -1659,7 +1727,7 @@ mod tests {
"--date", "--date",
"20180919T17:30:59", "20180919T17:30:59",
]); ]);
assert!(parse_command(&pubkey, &test_bad_timestamp).is_err()); assert!(parse_command(&test_bad_timestamp).is_err());
} }
#[test] #[test]
@ -1675,13 +1743,13 @@ mod tests {
assert_eq!(process_command(&config).unwrap(), pubkey); assert_eq!(process_command(&config).unwrap(), pubkey);
config.command = CliCommand::Balance { config.command = CliCommand::Balance {
pubkey: config.keypair.pubkey(), pubkey: None,
use_lamports_unit: true, use_lamports_unit: true,
}; };
assert_eq!(process_command(&config).unwrap(), "50 lamports"); assert_eq!(process_command(&config).unwrap(), "50 lamports");
config.command = CliCommand::Balance { config.command = CliCommand::Balance {
pubkey: config.keypair.pubkey(), pubkey: None,
use_lamports_unit: false, use_lamports_unit: false,
}; };
assert_eq!(process_command(&config).unwrap(), "0 SOL"); assert_eq!(process_command(&config).unwrap(), "0 SOL");
@ -1696,15 +1764,13 @@ mod tests {
let bob_pubkey = Pubkey::new_rand(); let bob_pubkey = Pubkey::new_rand();
let node_pubkey = Pubkey::new_rand(); let node_pubkey = Pubkey::new_rand();
config.command = CliCommand::CreateVoteAccount( config.command = CliCommand::CreateVoteAccount {
bob_pubkey, vote_account_pubkey: bob_pubkey,
VoteInit { node_pubkey,
node_pubkey, authorized_voter: Some(bob_pubkey),
authorized_voter: bob_pubkey, authorized_withdrawer: Some(bob_pubkey),
authorized_withdrawer: bob_pubkey, commission: 0,
commission: 0, };
},
);
let signature = process_command(&config); let signature = process_command(&config);
assert_eq!(signature.unwrap(), SIGNATURE.to_string()); assert_eq!(signature.unwrap(), SIGNATURE.to_string());
@ -1716,15 +1782,13 @@ mod tests {
let bob_pubkey = Pubkey::new_rand(); let bob_pubkey = Pubkey::new_rand();
let custodian = Pubkey::new_rand(); let custodian = Pubkey::new_rand();
config.command = CliCommand::CreateStakeAccount( config.command = CliCommand::CreateStakeAccount {
bob_pubkey, stake_account_pubkey: bob_pubkey,
Authorized { staker: None,
staker: config.keypair.pubkey(), withdrawer: None,
withdrawer: config.keypair.pubkey(), lockup: Lockup { slot: 0, custodian },
}, lamports: 1234,
Lockup { slot: 0, custodian }, };
1234,
);
let signature = process_command(&config); let signature = process_command(&config);
assert_eq!(signature.unwrap(), SIGNATURE.to_string()); assert_eq!(signature.unwrap(), SIGNATURE.to_string());
@ -1751,7 +1815,7 @@ mod tests {
timestamp: None, timestamp: None,
timestamp_pubkey: None, timestamp_pubkey: None,
witnesses: None, witnesses: None,
cancelable: None, cancelable: false,
}; };
let signature = process_command(&config); let signature = process_command(&config);
assert_eq!(signature.unwrap(), SIGNATURE.to_string()); assert_eq!(signature.unwrap(), SIGNATURE.to_string());
@ -1764,7 +1828,7 @@ mod tests {
timestamp: Some(dt), timestamp: Some(dt),
timestamp_pubkey: Some(config.keypair.pubkey()), timestamp_pubkey: Some(config.keypair.pubkey()),
witnesses: None, witnesses: None,
cancelable: None, cancelable: false,
}; };
let result = process_command(&config); let result = process_command(&config);
let json: Value = serde_json::from_str(&result.unwrap()).unwrap(); let json: Value = serde_json::from_str(&result.unwrap()).unwrap();
@ -1785,7 +1849,7 @@ mod tests {
timestamp: None, timestamp: None,
timestamp_pubkey: None, timestamp_pubkey: None,
witnesses: Some(vec![witness]), witnesses: Some(vec![witness]),
cancelable: Some(config.keypair.pubkey()), cancelable: true,
}; };
let result = process_command(&config); let result = process_command(&config);
let json: Value = serde_json::from_str(&result.unwrap()).unwrap(); let json: Value = serde_json::from_str(&result.unwrap()).unwrap();
@ -1858,20 +1922,18 @@ mod tests {
assert!(process_command(&config).is_err()); assert!(process_command(&config).is_err());
config.command = CliCommand::Balance { config.command = CliCommand::Balance {
pubkey: config.keypair.pubkey(), pubkey: None,
use_lamports_unit: false, use_lamports_unit: false,
}; };
assert!(process_command(&config).is_err()); assert!(process_command(&config).is_err());
config.command = CliCommand::CreateVoteAccount( config.command = CliCommand::CreateVoteAccount {
bob_pubkey, vote_account_pubkey: bob_pubkey,
VoteInit { node_pubkey,
node_pubkey, authorized_voter: Some(bob_pubkey),
authorized_voter: bob_pubkey, authorized_withdrawer: Some(bob_pubkey),
authorized_withdrawer: bob_pubkey, commission: 0,
commission: 0, };
},
);
assert!(process_command(&config).is_err()); assert!(process_command(&config).is_err());
config.command = CliCommand::VoteAuthorize(bob_pubkey, bob_pubkey, VoteAuthorize::Voter); config.command = CliCommand::VoteAuthorize(bob_pubkey, bob_pubkey, VoteAuthorize::Voter);
@ -1889,7 +1951,7 @@ mod tests {
timestamp: None, timestamp: None,
timestamp_pubkey: None, timestamp_pubkey: None,
witnesses: None, witnesses: None,
cancelable: None, cancelable: false,
}; };
assert!(process_command(&config).is_err()); assert!(process_command(&config).is_err());
@ -1899,7 +1961,7 @@ mod tests {
timestamp: Some(dt), timestamp: Some(dt),
timestamp_pubkey: Some(config.keypair.pubkey()), timestamp_pubkey: Some(config.keypair.pubkey()),
witnesses: None, witnesses: None,
cancelable: None, cancelable: false,
}; };
assert!(process_command(&config).is_err()); assert!(process_command(&config).is_err());
@ -1909,7 +1971,7 @@ mod tests {
timestamp: None, timestamp: None,
timestamp_pubkey: None, timestamp_pubkey: None,
witnesses: Some(vec![witness]), witnesses: Some(vec![witness]),
cancelable: Some(config.keypair.pubkey()), cancelable: true,
}; };
assert!(process_command(&config).is_err()); assert!(process_command(&config).is_err());

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
cli::{ cli::{
build_balance_message, check_account_for_fee, CliCommand, CliConfig, CliError, build_balance_message, check_account_for_fee, CliCommand, CliCommandInfo, CliConfig,
ProcessResult, CliError, ProcessResult,
}, },
display::println_name_value, display::println_name_value,
}; };
@ -88,7 +88,7 @@ impl ClusterQuerySubCommands for App<'_, '_> {
} }
} }
pub fn parse_cluster_ping(matches: &ArgMatches<'_>) -> Result<CliCommand, CliError> { pub fn parse_cluster_ping(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
let interval = Duration::from_secs(value_t_or_exit!(matches, "interval", u64)); let interval = Duration::from_secs(value_t_or_exit!(matches, "interval", u64));
let count = if matches.is_present("count") { let count = if matches.is_present("count") {
Some(value_t_or_exit!(matches, "count", u64)) Some(value_t_or_exit!(matches, "count", u64))
@ -96,17 +96,23 @@ pub fn parse_cluster_ping(matches: &ArgMatches<'_>) -> Result<CliCommand, CliErr
None None
}; };
let timeout = Duration::from_secs(value_t_or_exit!(matches, "timeout", u64)); let timeout = Duration::from_secs(value_t_or_exit!(matches, "timeout", u64));
Ok(CliCommand::Ping { Ok(CliCommandInfo {
interval, command: CliCommand::Ping {
count, interval,
timeout, count,
timeout,
},
require_keypair: true,
}) })
} }
pub fn parse_show_validators(matches: &ArgMatches<'_>) -> Result<CliCommand, 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");
Ok(CliCommand::ShowValidators { use_lamports_unit }) Ok(CliCommandInfo {
command: CliCommand::ShowValidators { use_lamports_unit },
require_keypair: false,
})
} }
pub fn process_cluster_version(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult { pub fn process_cluster_version(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
@ -358,68 +364,87 @@ pub fn process_show_validators(rpc_client: &RpcClient, use_lamports_unit: bool)
mod tests { mod tests {
use super::*; use super::*;
use crate::cli::{app, parse_command}; use crate::cli::{app, parse_command};
use solana_sdk::pubkey::Pubkey;
#[test] #[test]
fn test_parse_command() { fn test_parse_command() {
let test_commands = app("test", "desc", "version"); let test_commands = app("test", "desc", "version");
let pubkey = Pubkey::new_rand();
let test_cluster_version = test_commands let test_cluster_version = test_commands
.clone() .clone()
.get_matches_from(vec!["test", "cluster-version"]); .get_matches_from(vec!["test", "cluster-version"]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_cluster_version).unwrap(), parse_command(&test_cluster_version).unwrap(),
CliCommand::ClusterVersion CliCommandInfo {
command: CliCommand::ClusterVersion,
require_keypair: false
}
); );
let test_fees = test_commands.clone().get_matches_from(vec!["test", "fees"]); let test_fees = test_commands.clone().get_matches_from(vec!["test", "fees"]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_fees).unwrap(), parse_command(&test_fees).unwrap(),
CliCommand::Fees CliCommandInfo {
command: CliCommand::Fees,
require_keypair: false
}
); );
let test_get_epoch_info = test_commands let test_get_epoch_info = test_commands
.clone() .clone()
.get_matches_from(vec!["test", "get-epoch-info"]); .get_matches_from(vec!["test", "get-epoch-info"]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_get_epoch_info).unwrap(), parse_command(&test_get_epoch_info).unwrap(),
CliCommand::GetEpochInfo CliCommandInfo {
command: CliCommand::GetEpochInfo,
require_keypair: false
}
); );
let test_get_genesis_blockhash = test_commands let test_get_genesis_blockhash = test_commands
.clone() .clone()
.get_matches_from(vec!["test", "get-genesis-blockhash"]); .get_matches_from(vec!["test", "get-genesis-blockhash"]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_get_genesis_blockhash).unwrap(), parse_command(&test_get_genesis_blockhash).unwrap(),
CliCommand::GetGenesisBlockhash CliCommandInfo {
command: CliCommand::GetGenesisBlockhash,
require_keypair: false
}
); );
let test_get_slot = test_commands let test_get_slot = test_commands
.clone() .clone()
.get_matches_from(vec!["test", "get-slot"]); .get_matches_from(vec!["test", "get-slot"]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_get_slot).unwrap(), parse_command(&test_get_slot).unwrap(),
CliCommand::GetSlot CliCommandInfo {
command: CliCommand::GetSlot,
require_keypair: false
}
); );
let test_transaction_count = test_commands let test_transaction_count = test_commands
.clone() .clone()
.get_matches_from(vec!["test", "get-transaction-count"]); .get_matches_from(vec!["test", "get-transaction-count"]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_transaction_count).unwrap(), parse_command(&test_transaction_count).unwrap(),
CliCommand::GetTransactionCount CliCommandInfo {
command: CliCommand::GetTransactionCount,
require_keypair: false
}
); );
let test_ping = test_commands let test_ping = test_commands
.clone() .clone()
.get_matches_from(vec!["test", "ping", "-i", "1", "-c", "2", "-t", "3"]); .get_matches_from(vec!["test", "ping", "-i", "1", "-c", "2", "-t", "3"]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_ping).unwrap(), parse_command(&test_ping).unwrap(),
CliCommand::Ping { CliCommandInfo {
interval: Duration::from_secs(1), command: CliCommand::Ping {
count: Some(2), interval: Duration::from_secs(1),
timeout: Duration::from_secs(3), count: Some(2),
timeout: Duration::from_secs(3),
},
require_keypair: true
} }
); );
} }

View File

@ -1,12 +1,12 @@
use clap::{crate_description, crate_name, crate_version, Arg, ArgGroup, ArgMatches, SubCommand}; use clap::{crate_description, crate_name, crate_version, Arg, ArgGroup, ArgMatches, SubCommand};
use console::style; use console::style;
use solana_cli::{ use solana_cli::{
cli::{app, parse_command, process_command, CliConfig, CliError}, cli::{app, parse_command, process_command, CliCommandInfo, CliConfig, CliError},
config::{self, Config}, config::{self, Config},
display::{println_name_value, println_name_value_or}, display::{println_name_value, println_name_value_or},
input_validators::is_url, input_validators::is_url,
}; };
use solana_sdk::signature::{read_keypair_file, KeypairUtil}; use solana_sdk::signature::read_keypair_file;
use std::error; use std::error;
fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error>> { fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error>> {
@ -18,7 +18,7 @@ fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error
if let Some(field) = subcommand_matches.value_of("specific_setting") { if let Some(field) = subcommand_matches.value_of("specific_setting") {
let (value, default_value) = match field { let (value, default_value) = match field {
"url" => (config.url, default_cli_config.json_rpc_url), "url" => (config.url, default_cli_config.json_rpc_url),
"keypair" => (config.keypair, default_cli_config.keypair_path), "keypair" => (config.keypair, default_cli_config.keypair_path.unwrap()),
_ => unreachable!(), _ => unreachable!(),
}; };
println_name_value_or(&format!("* {}:", field), &value, &default_value); println_name_value_or(&format!("* {}:", field), &value, &default_value);
@ -28,7 +28,7 @@ fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error
println_name_value_or( println_name_value_or(
"* keypair:", "* keypair:",
&config.keypair, &config.keypair,
&default_cli_config.keypair_path, &default_cli_config.keypair_path.unwrap(),
); );
} }
} else { } else {
@ -80,34 +80,44 @@ pub fn parse_args(matches: &ArgMatches<'_>) -> Result<CliConfig, Box<dyn error::
default.json_rpc_url default.json_rpc_url
}; };
let keypair_path = if matches.is_present("keypair") { let CliCommandInfo {
matches.value_of("keypair").unwrap().to_string() command,
} else if config.keypair != "" { require_keypair,
config.keypair } = parse_command(&matches)?;
let (keypair, keypair_path) = if require_keypair {
let keypair_path = if matches.is_present("keypair") {
matches.value_of("keypair").unwrap().to_string()
} else if config.keypair != "" {
config.keypair
} else {
let default = CliConfig::default();
let maybe_keypair_path = default.keypair_path.unwrap();
if !std::path::Path::new(&maybe_keypair_path).exists() {
return Err(CliError::KeypairFileNotFound(
"Generate a new keypair with `solana-keygen new`".to_string(),
)
.into());
}
maybe_keypair_path
};
let keypair = read_keypair_file(&keypair_path).or_else(|err| {
Err(CliError::BadParameter(format!(
"{}: Unable to open keypair file: {}",
err, keypair_path
)))
})?;
(keypair, Some(keypair_path.to_string()))
} else { } else {
let default = CliConfig::default(); let default = CliConfig::default();
if !std::path::Path::new(&default.keypair_path).exists() { (default.keypair, None)
return Err(CliError::KeypairFileNotFound(
"Generate a new keypair with `solana-keygen new`".to_string(),
)
.into());
}
default.keypair_path
}; };
let keypair = read_keypair_file(&keypair_path).or_else(|err| {
Err(CliError::BadParameter(format!(
"{}: Unable to open keypair file: {}",
err, keypair_path
)))
})?;
let command = parse_command(&keypair.pubkey(), &matches)?;
Ok(CliConfig { Ok(CliConfig {
command, command,
json_rpc_url, json_rpc_url,
keypair, keypair,
keypair_path: keypair_path.to_string(), keypair_path,
rpc_client: None, rpc_client: None,
}) })
} }

View File

@ -1,7 +1,8 @@
use crate::{ use crate::{
cli::{ cli::{
build_balance_message, check_account_for_fee, check_unique_pubkeys, build_balance_message, check_account_for_fee, check_unique_pubkeys,
log_instruction_custom_error, CliCommand, CliConfig, CliError, ProcessResult, log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError,
ProcessResult,
}, },
input_parsers::*, input_parsers::*,
input_validators::*, input_validators::*,
@ -251,83 +252,95 @@ impl StakeSubCommands for App<'_, '_> {
} }
} }
pub fn parse_stake_create_account( pub fn parse_stake_create_account(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
pubkey: &Pubkey,
matches: &ArgMatches<'_>,
) -> Result<CliCommand, CliError> {
let stake_account_pubkey = pubkey_of(matches, "stake_account_pubkey").unwrap(); let stake_account_pubkey = pubkey_of(matches, "stake_account_pubkey").unwrap();
let slot = value_of(&matches, "lockup").unwrap_or(0); let slot = value_of(&matches, "lockup").unwrap_or(0);
let custodian = pubkey_of(matches, "custodian").unwrap_or_default(); let custodian = pubkey_of(matches, "custodian").unwrap_or_default();
let staker = pubkey_of(matches, "authorized_staker").unwrap_or(*pubkey); // defaults to config let staker = pubkey_of(matches, "authorized_staker");
let withdrawer = pubkey_of(matches, "authorized_withdrawer").unwrap_or(*pubkey); // defaults to config let withdrawer = pubkey_of(matches, "authorized_withdrawer");
let lamports = amount_of(matches, "amount", "unit").expect("Invalid amount"); let lamports = amount_of(matches, "amount", "unit").expect("Invalid amount");
Ok(CliCommand::CreateStakeAccount( Ok(CliCommandInfo {
stake_account_pubkey, command: CliCommand::CreateStakeAccount {
Authorized { staker, withdrawer }, stake_account_pubkey,
Lockup { custodian, slot }, staker,
lamports, withdrawer,
)) lockup: Lockup { custodian, slot },
lamports,
},
require_keypair: true,
})
} }
pub fn parse_stake_delegate_stake(matches: &ArgMatches<'_>) -> Result<CliCommand, CliError> { pub fn parse_stake_delegate_stake(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
let stake_account_pubkey = pubkey_of(matches, "stake_account_pubkey").unwrap(); let stake_account_pubkey = pubkey_of(matches, "stake_account_pubkey").unwrap();
let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap(); let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap();
let force = matches.is_present("force"); let force = matches.is_present("force");
Ok(CliCommand::DelegateStake( Ok(CliCommandInfo {
stake_account_pubkey, command: CliCommand::DelegateStake(stake_account_pubkey, vote_account_pubkey, force),
vote_account_pubkey, require_keypair: true,
force, })
))
} }
pub fn parse_stake_authorize( pub fn parse_stake_authorize(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
stake_authorize: StakeAuthorize, stake_authorize: StakeAuthorize,
) -> Result<CliCommand, CliError> { ) -> Result<CliCommandInfo, CliError> {
let stake_account_pubkey = pubkey_of(matches, "stake_account_pubkey").unwrap(); let stake_account_pubkey = pubkey_of(matches, "stake_account_pubkey").unwrap();
let authorized_pubkey = pubkey_of(matches, "authorized_pubkey").unwrap(); let authorized_pubkey = pubkey_of(matches, "authorized_pubkey").unwrap();
Ok(CliCommand::StakeAuthorize( Ok(CliCommandInfo {
stake_account_pubkey, command: CliCommand::StakeAuthorize(
authorized_pubkey, stake_account_pubkey,
stake_authorize, authorized_pubkey,
)) stake_authorize,
),
require_keypair: true,
})
} }
pub fn parse_redeem_vote_credits(matches: &ArgMatches<'_>) -> Result<CliCommand, CliError> { pub fn parse_redeem_vote_credits(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
let stake_account_pubkey = pubkey_of(matches, "stake_account_pubkey").unwrap(); let stake_account_pubkey = pubkey_of(matches, "stake_account_pubkey").unwrap();
let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap(); let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap();
Ok(CliCommand::RedeemVoteCredits( Ok(CliCommandInfo {
stake_account_pubkey, command: CliCommand::RedeemVoteCredits(stake_account_pubkey, vote_account_pubkey),
vote_account_pubkey, require_keypair: true,
)) })
} }
pub fn parse_stake_deactivate_stake(matches: &ArgMatches<'_>) -> Result<CliCommand, CliError> { pub fn parse_stake_deactivate_stake(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
let stake_account_pubkey = pubkey_of(matches, "stake_account_pubkey").unwrap(); let stake_account_pubkey = pubkey_of(matches, "stake_account_pubkey").unwrap();
Ok(CliCommand::DeactivateStake(stake_account_pubkey)) Ok(CliCommandInfo {
command: CliCommand::DeactivateStake(stake_account_pubkey),
require_keypair: true,
})
} }
pub fn parse_stake_withdraw_stake(matches: &ArgMatches<'_>) -> Result<CliCommand, CliError> { pub fn parse_stake_withdraw_stake(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
let stake_account_pubkey = pubkey_of(matches, "stake_account_pubkey").unwrap(); let stake_account_pubkey = pubkey_of(matches, "stake_account_pubkey").unwrap();
let destination_account_pubkey = pubkey_of(matches, "destination_account_pubkey").unwrap(); let destination_account_pubkey = pubkey_of(matches, "destination_account_pubkey").unwrap();
let lamports = amount_of(matches, "amount", "unit").expect("Invalid amount"); let lamports = amount_of(matches, "amount", "unit").expect("Invalid amount");
Ok(CliCommand::WithdrawStake( Ok(CliCommandInfo {
stake_account_pubkey, command: CliCommand::WithdrawStake(
destination_account_pubkey, stake_account_pubkey,
lamports, destination_account_pubkey,
)) lamports,
),
require_keypair: true,
})
} }
pub fn parse_show_stake_account(matches: &ArgMatches<'_>) -> Result<CliCommand, CliError> { pub fn parse_show_stake_account(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
let stake_account_pubkey = pubkey_of(matches, "stake_account_pubkey").unwrap(); let stake_account_pubkey = pubkey_of(matches, "stake_account_pubkey").unwrap();
let use_lamports_unit = matches.is_present("lamports"); let use_lamports_unit = matches.is_present("lamports");
Ok(CliCommand::ShowStakeAccount { Ok(CliCommandInfo {
pubkey: stake_account_pubkey, command: CliCommand::ShowStakeAccount {
use_lamports_unit, pubkey: stake_account_pubkey,
use_lamports_unit,
},
require_keypair: false,
}) })
} }
@ -335,7 +348,8 @@ pub fn process_create_stake_account(
rpc_client: &RpcClient, rpc_client: &RpcClient,
config: &CliConfig, config: &CliConfig,
stake_account_pubkey: &Pubkey, stake_account_pubkey: &Pubkey,
authorized: &Authorized, staker: &Option<Pubkey>,
withdrawer: &Option<Pubkey>,
lockup: &Lockup, lockup: &Lockup,
lamports: u64, lamports: u64,
) -> ProcessResult { ) -> ProcessResult {
@ -363,10 +377,16 @@ pub fn process_create_stake_account(
.into()); .into());
} }
let authorized = Authorized {
staker: staker.unwrap_or(config.keypair.pubkey()),
withdrawer: withdrawer.unwrap_or(config.keypair.pubkey()),
};
println!("{:?}", authorized);
let ixs = stake_instruction::create_stake_account_with_lockup( let ixs = stake_instruction::create_stake_account_with_lockup(
&config.keypair.pubkey(), &config.keypair.pubkey(),
stake_account_pubkey, stake_account_pubkey,
authorized, &authorized,
lockup, lockup,
lamports, lamports,
); );
@ -636,8 +656,11 @@ mod tests {
&pubkey_string, &pubkey_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_authorize_staker).unwrap(), parse_command(&test_authorize_staker).unwrap(),
CliCommand::StakeAuthorize(pubkey, pubkey, StakeAuthorize::Staker) CliCommandInfo {
command: CliCommand::StakeAuthorize(pubkey, pubkey, StakeAuthorize::Staker),
require_keypair: true
}
); );
let test_authorize_withdrawer = test_commands.clone().get_matches_from(vec![ let test_authorize_withdrawer = test_commands.clone().get_matches_from(vec![
"test", "test",
@ -646,8 +669,11 @@ mod tests {
&pubkey_string, &pubkey_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_authorize_withdrawer).unwrap(), parse_command(&test_authorize_withdrawer).unwrap(),
CliCommand::StakeAuthorize(pubkey, pubkey, StakeAuthorize::Withdrawer) CliCommandInfo {
command: CliCommand::StakeAuthorize(pubkey, pubkey, StakeAuthorize::Withdrawer),
require_keypair: true
}
); );
// Test CreateStakeAccount SubCommand // Test CreateStakeAccount SubCommand
@ -671,19 +697,20 @@ mod tests {
"lamports", "lamports",
]); ]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_create_stake_account).unwrap(), parse_command(&test_create_stake_account).unwrap(),
CliCommand::CreateStakeAccount( CliCommandInfo {
pubkey, command: CliCommand::CreateStakeAccount {
Authorized { stake_account_pubkey: pubkey,
staker: authorized, staker: Some(authorized),
withdrawer: authorized, withdrawer: Some(authorized),
lockup: Lockup {
slot: 43,
custodian,
},
lamports: 50
}, },
Lockup { require_keypair: true
slot: 43, }
custodian,
},
50
)
); );
let test_create_stake_account2 = test_commands.clone().get_matches_from(vec![ let test_create_stake_account2 = test_commands.clone().get_matches_from(vec![
"test", "test",
@ -693,19 +720,20 @@ mod tests {
"lamports", "lamports",
]); ]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_create_stake_account2).unwrap(), parse_command(&test_create_stake_account2).unwrap(),
CliCommand::CreateStakeAccount( CliCommandInfo {
pubkey, command: CliCommand::CreateStakeAccount {
Authorized { stake_account_pubkey: pubkey,
staker: pubkey, staker: None,
withdrawer: pubkey, withdrawer: None,
lockup: Lockup {
slot: 0,
custodian: Pubkey::default(),
},
lamports: 50
}, },
Lockup { require_keypair: true
slot: 0, }
custodian: Pubkey::default(),
},
50
)
); );
// Test DelegateStake Subcommand // Test DelegateStake Subcommand
@ -718,8 +746,11 @@ mod tests {
&pubkey_string, &pubkey_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_delegate_stake).unwrap(), parse_command(&test_delegate_stake).unwrap(),
CliCommand::DelegateStake(stake_pubkey, pubkey, false,) CliCommandInfo {
command: CliCommand::DelegateStake(stake_pubkey, pubkey, false),
require_keypair: true
}
); );
let test_delegate_stake = test_commands.clone().get_matches_from(vec![ let test_delegate_stake = test_commands.clone().get_matches_from(vec![
@ -730,8 +761,11 @@ mod tests {
&pubkey_string, &pubkey_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_delegate_stake).unwrap(), parse_command(&test_delegate_stake).unwrap(),
CliCommand::DelegateStake(stake_pubkey, pubkey, true) CliCommandInfo {
command: CliCommand::DelegateStake(stake_pubkey, pubkey, true),
require_keypair: true
}
); );
// Test WithdrawStake Subcommand // Test WithdrawStake Subcommand
@ -745,8 +779,11 @@ mod tests {
]); ]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_withdraw_stake).unwrap(), parse_command(&test_withdraw_stake).unwrap(),
CliCommand::WithdrawStake(stake_pubkey, pubkey, 42) CliCommandInfo {
command: CliCommand::WithdrawStake(stake_pubkey, pubkey, 42),
require_keypair: true
}
); );
// Test DeactivateStake Subcommand // Test DeactivateStake Subcommand
@ -756,8 +793,11 @@ mod tests {
&stake_pubkey_string, &stake_pubkey_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_deactivate_stake).unwrap(), parse_command(&test_deactivate_stake).unwrap(),
CliCommand::DeactivateStake(stake_pubkey) CliCommandInfo {
command: CliCommand::DeactivateStake(stake_pubkey),
require_keypair: true
}
); );
} }
// TODO: Add process tests // TODO: Add process tests

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
cli::{ cli::{
check_account_for_fee, check_unique_pubkeys, log_instruction_custom_error, CliCommand, check_account_for_fee, check_unique_pubkeys, log_instruction_custom_error, CliCommand,
CliConfig, CliError, ProcessResult, CliCommandInfo, CliConfig, CliError, ProcessResult,
}, },
input_parsers::*, input_parsers::*,
input_validators::*, input_validators::*,
@ -100,40 +100,54 @@ impl StorageSubCommands for App<'_, '_> {
pub fn parse_storage_create_archiver_account( pub fn parse_storage_create_archiver_account(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
) -> Result<CliCommand, CliError> { ) -> Result<CliCommandInfo, CliError> {
let account_owner = pubkey_of(matches, "storage_account_owner").unwrap(); let account_owner = pubkey_of(matches, "storage_account_owner").unwrap();
let storage_account_pubkey = pubkey_of(matches, "storage_account_pubkey").unwrap(); let storage_account_pubkey = pubkey_of(matches, "storage_account_pubkey").unwrap();
Ok(CliCommand::CreateStorageAccount { Ok(CliCommandInfo {
account_owner, command: CliCommand::CreateStorageAccount {
storage_account_pubkey, account_owner,
account_type: StorageAccountType::Archiver, storage_account_pubkey,
account_type: StorageAccountType::Archiver,
},
require_keypair: true,
}) })
} }
pub fn parse_storage_create_validator_account( pub fn parse_storage_create_validator_account(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
) -> Result<CliCommand, CliError> { ) -> Result<CliCommandInfo, CliError> {
let account_owner = pubkey_of(matches, "storage_account_owner").unwrap(); let account_owner = pubkey_of(matches, "storage_account_owner").unwrap();
let storage_account_pubkey = pubkey_of(matches, "storage_account_pubkey").unwrap(); let storage_account_pubkey = pubkey_of(matches, "storage_account_pubkey").unwrap();
Ok(CliCommand::CreateStorageAccount { Ok(CliCommandInfo {
account_owner, command: CliCommand::CreateStorageAccount {
storage_account_pubkey, account_owner,
account_type: StorageAccountType::Validator, storage_account_pubkey,
account_type: StorageAccountType::Validator,
},
require_keypair: true,
}) })
} }
pub fn parse_storage_claim_reward(matches: &ArgMatches<'_>) -> Result<CliCommand, CliError> { pub fn parse_storage_claim_reward(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
let node_account_pubkey = pubkey_of(matches, "node_account_pubkey").unwrap(); let node_account_pubkey = pubkey_of(matches, "node_account_pubkey").unwrap();
let storage_account_pubkey = pubkey_of(matches, "storage_account_pubkey").unwrap(); let storage_account_pubkey = pubkey_of(matches, "storage_account_pubkey").unwrap();
Ok(CliCommand::ClaimStorageReward { Ok(CliCommandInfo {
node_account_pubkey, command: CliCommand::ClaimStorageReward {
storage_account_pubkey, node_account_pubkey,
storage_account_pubkey,
},
require_keypair: true,
}) })
} }
pub fn parse_storage_get_account_command(matches: &ArgMatches<'_>) -> Result<CliCommand, CliError> { pub fn parse_storage_get_account_command(
matches: &ArgMatches<'_>,
) -> Result<CliCommandInfo, CliError> {
let storage_account_pubkey = pubkey_of(matches, "storage_account_pubkey").unwrap(); let storage_account_pubkey = pubkey_of(matches, "storage_account_pubkey").unwrap();
Ok(CliCommand::ShowStorageAccount(storage_account_pubkey)) Ok(CliCommandInfo {
command: CliCommand::ShowStorageAccount(storage_account_pubkey),
require_keypair: false,
})
} }
pub fn process_create_storage_account( pub fn process_create_storage_account(
@ -228,11 +242,14 @@ mod tests {
&storage_account_string, &storage_account_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_create_archiver_storage_account).unwrap(), parse_command(&test_create_archiver_storage_account).unwrap(),
CliCommand::CreateStorageAccount { CliCommandInfo {
account_owner: pubkey, command: CliCommand::CreateStorageAccount {
storage_account_pubkey, account_owner: pubkey,
account_type: StorageAccountType::Archiver, storage_account_pubkey,
account_type: StorageAccountType::Archiver,
},
require_keypair: true
} }
); );
@ -243,11 +260,14 @@ mod tests {
&storage_account_string, &storage_account_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_create_validator_storage_account).unwrap(), parse_command(&test_create_validator_storage_account).unwrap(),
CliCommand::CreateStorageAccount { CliCommandInfo {
account_owner: pubkey, command: CliCommand::CreateStorageAccount {
storage_account_pubkey, account_owner: pubkey,
account_type: StorageAccountType::Validator, storage_account_pubkey,
account_type: StorageAccountType::Validator,
},
require_keypair: true
} }
); );
@ -258,10 +278,13 @@ mod tests {
&storage_account_string, &storage_account_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_claim_storage_reward).unwrap(), parse_command(&test_claim_storage_reward).unwrap(),
CliCommand::ClaimStorageReward { CliCommandInfo {
node_account_pubkey: pubkey, command: CliCommand::ClaimStorageReward {
storage_account_pubkey, node_account_pubkey: pubkey,
storage_account_pubkey,
},
require_keypair: true
} }
); );
} }

View File

@ -1,9 +1,10 @@
use crate::{ use crate::{
cli::{check_account_for_fee, CliCommand, CliConfig, CliError, ProcessResult}, cli::{check_account_for_fee, CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult},
input_validators::is_url, input_parsers::pubkey_of,
input_validators::{is_pubkey, is_url},
}; };
use bincode::deserialize; use bincode::deserialize;
use clap::ArgMatches; use clap::{App, Arg, ArgMatches, SubCommand};
use reqwest::Client; use reqwest::Client;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use serde_json::{Map, Value}; use serde_json::{Map, Value};
@ -142,28 +143,123 @@ fn parse_validator_info(
} }
} }
fn parse_info_pubkey(matches: &ArgMatches<'_>) -> Result<Option<Pubkey>, CliError> { pub trait ValidatorInfoSubCommands {
let info_pubkey = if let Some(pubkey) = matches.value_of("info_pubkey") { fn validator_info_subcommands(self) -> Self;
Some(pubkey.parse::<Pubkey>().map_err(|err| {
CliError::BadParameter(format!("Invalid validator info pubkey: {:?}", err))
})?)
} else {
None
};
Ok(info_pubkey)
} }
pub fn parse_validator_info_command( impl ValidatorInfoSubCommands for App<'_, '_> {
matches: &ArgMatches<'_>, fn validator_info_subcommands(self) -> Self {
validator_pubkey: &Pubkey, self.subcommand(
) -> Result<CliCommand, CliError> { SubCommand::with_name("validator-info")
let info_pubkey = parse_info_pubkey(matches)?; .about("Publish/get Validator info on Solana")
.subcommand(
SubCommand::with_name("publish")
.about("Publish Validator info on Solana")
.arg(
Arg::with_name("info_pubkey")
.short("p")
.long("info-pubkey")
.value_name("PUBKEY")
.takes_value(true)
.validator(is_pubkey)
.help("The pubkey of the Validator info account to update"),
)
.arg(
Arg::with_name("name")
.index(1)
.value_name("NAME")
.takes_value(true)
.required(true)
.validator(is_short_field)
.help("Validator name"),
)
.arg(
Arg::with_name("website")
.short("w")
.long("website")
.value_name("URL")
.takes_value(true)
.validator(check_url)
.help("Validator website url"),
)
.arg(
Arg::with_name("keybase_username")
.short("n")
.long("keybase")
.value_name("USERNAME")
.takes_value(true)
.validator(is_short_field)
.help("Validator Keybase username"),
)
.arg(
Arg::with_name("details")
.short("d")
.long("details")
.value_name("DETAILS")
.takes_value(true)
.validator(check_details_length)
.help("Validator description")
)
.arg(
Arg::with_name("force")
.long("force")
.takes_value(false)
.hidden(true) // Don't document this argument to discourage its use
.help("Override keybase username validity check"),
),
)
.subcommand(
SubCommand::with_name("get")
.about("Get and parse Solana Validator info")
.arg(
Arg::with_name("info_pubkey")
.index(1)
.value_name("PUBKEY")
.takes_value(true)
.validator(is_pubkey)
.help("The pubkey of the Validator info account; without this argument, returns all"),
),
)
)
}
}
pub fn parse_validator_info_command(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
let info_pubkey = pubkey_of(matches, "info_pubkey");
// Prepare validator info // Prepare validator info
let validator_info = parse_args(&matches); let validator_info = parse_args(&matches);
Ok(CliCommandInfo {
command: CliCommand::SetValidatorInfo {
validator_info,
force_keybase: matches.is_present("force"),
info_pubkey,
},
require_keypair: true,
})
}
pub fn parse_get_validator_info_command(
matches: &ArgMatches<'_>,
) -> Result<CliCommandInfo, CliError> {
let info_pubkey = pubkey_of(matches, "info_pubkey");
Ok(CliCommandInfo {
command: CliCommand::GetValidatorInfo(info_pubkey),
require_keypair: false,
})
}
pub fn process_set_validator_info(
rpc_client: &RpcClient,
config: &CliConfig,
validator_info: &Value,
force_keybase: bool,
info_pubkey: Option<Pubkey>,
) -> ProcessResult {
// Validate keybase username
if let Some(string) = validator_info.get("keybaseUsername") { if let Some(string) = validator_info.get("keybaseUsername") {
let result = verify_keybase(&validator_pubkey, &string); let result = verify_keybase(&config.keypair.pubkey(), &string);
if result.is_err() { if result.is_err() {
if matches.is_present("force") { if force_keybase {
println!("--force supplied, ignoring: {:?}", result); println!("--force supplied, ignoring: {:?}", result);
} else { } else {
result.map_err(|err| { result.map_err(|err| {
@ -176,20 +272,6 @@ pub fn parse_validator_info_command(
let validator_info = ValidatorInfo { let validator_info = ValidatorInfo {
info: validator_string, info: validator_string,
}; };
Ok(CliCommand::SetValidatorInfo(validator_info, info_pubkey))
}
pub fn parse_get_validator_info_command(matches: &ArgMatches<'_>) -> Result<CliCommand, CliError> {
let info_pubkey = parse_info_pubkey(matches)?;
Ok(CliCommand::GetValidatorInfo(info_pubkey))
}
pub fn process_set_validator_info(
rpc_client: &RpcClient,
config: &CliConfig,
validator_info: &ValidatorInfo,
info_pubkey: Option<Pubkey>,
) -> ProcessResult {
// Check for existing validator-info account // Check for existing validator-info account
let all_config = rpc_client.get_program_accounts(&solana_config_api::id())?; let all_config = rpc_client.get_program_accounts(&solana_config_api::id())?;
let existing_account = all_config let existing_account = all_config
@ -239,7 +321,7 @@ pub fn process_set_validator_info(
&info_keypair.pubkey(), &info_keypair.pubkey(),
true, true,
keys, keys,
validator_info, &validator_info,
)]); )]);
let signers = vec![&config.keypair, &info_keypair]; let signers = vec![&config.keypair, &info_keypair];
let message = Message::new(instructions); let message = Message::new(instructions);
@ -254,7 +336,7 @@ pub fn process_set_validator_info(
&info_pubkey, &info_pubkey,
false, false,
keys, keys,
validator_info, &validator_info,
)]; )];
let message = Message::new_with_payer(instructions, Some(&config.keypair.pubkey())); let message = Message::new_with_payer(instructions, Some(&config.keypair.pubkey()));
let signers = vec![&config.keypair]; let signers = vec![&config.keypair];

View File

@ -1,7 +1,8 @@
use crate::{ use crate::{
cli::{ cli::{
build_balance_message, check_account_for_fee, check_unique_pubkeys, build_balance_message, check_account_for_fee, check_unique_pubkeys,
log_instruction_custom_error, CliCommand, CliConfig, CliError, ProcessResult, log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError,
ProcessResult,
}, },
input_parsers::*, input_parsers::*,
input_validators::*, input_validators::*,
@ -159,51 +160,57 @@ impl VoteSubCommands for App<'_, '_> {
} }
} }
pub fn parse_vote_create_account( pub fn parse_vote_create_account(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
pubkey: &Pubkey,
matches: &ArgMatches<'_>,
) -> Result<CliCommand, CliError> {
let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap(); let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap();
let node_pubkey = pubkey_of(matches, "node_pubkey").unwrap(); let node_pubkey = pubkey_of(matches, "node_pubkey").unwrap();
let commission = value_of(&matches, "commission").unwrap_or(0); let commission = value_of(&matches, "commission").unwrap_or(0);
let authorized_voter = pubkey_of(matches, "authorized_voter").unwrap_or(vote_account_pubkey); let authorized_voter = pubkey_of(matches, "authorized_voter");
let authorized_withdrawer = pubkey_of(matches, "authorized_withdrawer").unwrap_or(*pubkey); let authorized_withdrawer = pubkey_of(matches, "authorized_withdrawer");
Ok(CliCommand::CreateVoteAccount( Ok(CliCommandInfo {
vote_account_pubkey, command: CliCommand::CreateVoteAccount {
VoteInit { vote_account_pubkey,
node_pubkey, node_pubkey,
authorized_voter, authorized_voter,
authorized_withdrawer, authorized_withdrawer,
commission, commission,
}, },
)) require_keypair: true,
})
} }
pub fn parse_vote_authorize( pub fn parse_vote_authorize(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
vote_authorize: VoteAuthorize, vote_authorize: VoteAuthorize,
) -> Result<CliCommand, CliError> { ) -> Result<CliCommandInfo, CliError> {
let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap(); let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap();
let new_authorized_pubkey = pubkey_of(matches, "new_authorized_pubkey").unwrap(); let new_authorized_pubkey = pubkey_of(matches, "new_authorized_pubkey").unwrap();
Ok(CliCommand::VoteAuthorize( Ok(CliCommandInfo {
vote_account_pubkey, command: CliCommand::VoteAuthorize(
new_authorized_pubkey, vote_account_pubkey,
vote_authorize, new_authorized_pubkey,
)) vote_authorize,
} ),
require_keypair: true,
pub fn parse_vote_get_account_command(matches: &ArgMatches<'_>) -> Result<CliCommand, CliError> {
let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap();
let use_lamports_unit = matches.is_present("lamports");
Ok(CliCommand::ShowVoteAccount {
pubkey: vote_account_pubkey,
use_lamports_unit,
}) })
} }
pub fn parse_vote_uptime_command(matches: &ArgMatches<'_>) -> Result<CliCommand, CliError> { pub fn parse_vote_get_account_command(
matches: &ArgMatches<'_>,
) -> Result<CliCommandInfo, CliError> {
let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap();
let use_lamports_unit = matches.is_present("lamports");
Ok(CliCommandInfo {
command: CliCommand::ShowVoteAccount {
pubkey: vote_account_pubkey,
use_lamports_unit,
},
require_keypair: false,
})
}
pub fn parse_vote_uptime_command(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap(); let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap();
let aggregate = matches.is_present("aggregate"); let aggregate = matches.is_present("aggregate");
let span = if matches.is_present("span") { let span = if matches.is_present("span") {
@ -211,10 +218,13 @@ pub fn parse_vote_uptime_command(matches: &ArgMatches<'_>) -> Result<CliCommand,
} else { } else {
None None
}; };
Ok(CliCommand::Uptime { Ok(CliCommandInfo {
pubkey: vote_account_pubkey, command: CliCommand::Uptime {
aggregate, pubkey: vote_account_pubkey,
span, aggregate,
span,
},
require_keypair: false,
}) })
} }
@ -222,11 +232,14 @@ pub fn process_create_vote_account(
rpc_client: &RpcClient, rpc_client: &RpcClient,
config: &CliConfig, config: &CliConfig,
vote_account_pubkey: &Pubkey, vote_account_pubkey: &Pubkey,
vote_init: &VoteInit, node_pubkey: &Pubkey,
authorized_voter: &Option<Pubkey>,
authorized_withdrawer: &Option<Pubkey>,
commission: u8,
) -> ProcessResult { ) -> ProcessResult {
check_unique_pubkeys( check_unique_pubkeys(
(vote_account_pubkey, "vote_account_pubkey".to_string()), (vote_account_pubkey, "vote_account_pubkey".to_string()),
(&vote_init.node_pubkey, "node_pubkey".to_string()), (&node_pubkey, "node_pubkey".to_string()),
)?; )?;
check_unique_pubkeys( check_unique_pubkeys(
(&config.keypair.pubkey(), "cli keypair".to_string()), (&config.keypair.pubkey(), "cli keypair".to_string()),
@ -239,10 +252,16 @@ pub fn process_create_vote_account(
} else { } else {
1 1
}; };
let vote_init = VoteInit {
node_pubkey: *node_pubkey,
authorized_voter: authorized_voter.unwrap_or(*vote_account_pubkey),
authorized_withdrawer: authorized_withdrawer.unwrap_or(config.keypair.pubkey()),
commission,
};
let ixs = vote_instruction::create_account( let ixs = vote_instruction::create_account(
&config.keypair.pubkey(), &config.keypair.pubkey(),
vote_account_pubkey, vote_account_pubkey,
vote_init, &vote_init,
lamports, lamports,
); );
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
@ -441,8 +460,11 @@ mod tests {
&pubkey_string, &pubkey_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_authorize_voter).unwrap(), parse_command(&test_authorize_voter).unwrap(),
CliCommand::VoteAuthorize(pubkey, pubkey, VoteAuthorize::Voter) CliCommandInfo {
command: CliCommand::VoteAuthorize(pubkey, pubkey, VoteAuthorize::Voter),
require_keypair: true
}
); );
// Test CreateVoteAccount SubCommand // Test CreateVoteAccount SubCommand
@ -457,16 +479,17 @@ mod tests {
"10", "10",
]); ]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_create_vote_account).unwrap(), parse_command(&test_create_vote_account).unwrap(),
CliCommand::CreateVoteAccount( CliCommandInfo {
pubkey, command: CliCommand::CreateVoteAccount {
VoteInit { vote_account_pubkey: pubkey,
node_pubkey, node_pubkey,
authorized_voter: pubkey, authorized_voter: None,
authorized_withdrawer: pubkey, authorized_withdrawer: None,
commission: 10 commission: 10,
} },
) require_keypair: true
}
); );
let test_create_vote_account2 = test_commands.clone().get_matches_from(vec![ let test_create_vote_account2 = test_commands.clone().get_matches_from(vec![
"test", "test",
@ -475,16 +498,17 @@ mod tests {
&node_pubkey_string, &node_pubkey_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_create_vote_account2).unwrap(), parse_command(&test_create_vote_account2).unwrap(),
CliCommand::CreateVoteAccount( CliCommandInfo {
pubkey, command: CliCommand::CreateVoteAccount {
VoteInit { vote_account_pubkey: pubkey,
node_pubkey, node_pubkey,
authorized_voter: pubkey, authorized_voter: None,
authorized_withdrawer: pubkey, authorized_withdrawer: None,
commission: 0 commission: 0,
} },
) require_keypair: true
}
); );
// test init with an authed voter // test init with an authed voter
let authed = Pubkey::new_rand(); let authed = Pubkey::new_rand();
@ -497,16 +521,17 @@ mod tests {
&authed.to_string(), &authed.to_string(),
]); ]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_create_vote_account3).unwrap(), parse_command(&test_create_vote_account3).unwrap(),
CliCommand::CreateVoteAccount( CliCommandInfo {
pubkey, command: CliCommand::CreateVoteAccount {
VoteInit { vote_account_pubkey: pubkey,
node_pubkey, node_pubkey,
authorized_voter: authed, authorized_voter: Some(authed),
authorized_withdrawer: pubkey, authorized_withdrawer: None,
commission: 0 commission: 0
} },
) require_keypair: true
}
); );
// test init with an authed withdrawer // test init with an authed withdrawer
let test_create_vote_account4 = test_commands.clone().get_matches_from(vec![ let test_create_vote_account4 = test_commands.clone().get_matches_from(vec![
@ -518,16 +543,17 @@ mod tests {
&authed.to_string(), &authed.to_string(),
]); ]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_create_vote_account4).unwrap(), parse_command(&test_create_vote_account4).unwrap(),
CliCommand::CreateVoteAccount( CliCommandInfo {
pubkey, command: CliCommand::CreateVoteAccount {
VoteInit { vote_account_pubkey: pubkey,
node_pubkey, node_pubkey,
authorized_voter: pubkey, authorized_voter: None,
authorized_withdrawer: authed, authorized_withdrawer: Some(authed),
commission: 0 commission: 0
} },
) require_keypair: true
}
); );
// Test Uptime Subcommand // Test Uptime Subcommand
@ -541,11 +567,14 @@ mod tests {
"--aggregate", "--aggregate",
]); ]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &matches).unwrap(), parse_command(&matches).unwrap(),
CliCommand::Uptime { CliCommandInfo {
pubkey, command: CliCommand::Uptime {
aggregate: true, pubkey,
span: Some(4) aggregate: true,
span: Some(4)
},
require_keypair: false
} }
); );
} }

View File

@ -70,7 +70,7 @@ fn test_cli_timestamp_tx() {
timestamp: Some(dt), timestamp: Some(dt),
timestamp_pubkey: Some(config_witness.keypair.pubkey()), timestamp_pubkey: Some(config_witness.keypair.pubkey()),
witnesses: None, witnesses: None,
cancelable: None, cancelable: false,
}; };
let sig_response = process_command(&config_payer); let sig_response = process_command(&config_payer);
@ -137,7 +137,7 @@ fn test_cli_witness_tx() {
timestamp: None, timestamp: None,
timestamp_pubkey: None, timestamp_pubkey: None,
witnesses: Some(vec![config_witness.keypair.pubkey()]), witnesses: Some(vec![config_witness.keypair.pubkey()]),
cancelable: None, cancelable: false,
}; };
let sig_response = process_command(&config_payer); let sig_response = process_command(&config_payer);
@ -197,7 +197,7 @@ fn test_cli_cancel_tx() {
timestamp: None, timestamp: None,
timestamp_pubkey: None, timestamp_pubkey: None,
witnesses: Some(vec![config_witness.keypair.pubkey()]), witnesses: Some(vec![config_witness.keypair.pubkey()]),
cancelable: Some(config_payer.keypair.pubkey()), cancelable: true,
}; };
let sig_response = process_command(&config_payer).unwrap(); let sig_response = process_command(&config_payer).unwrap();