diff --git a/Cargo.lock b/Cargo.lock index ba2c7ac07f..e2bfa2375a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3539,6 +3539,7 @@ dependencies = [ "solana-budget-program", "solana-clap-utils", "solana-cli-config", + "solana-cli-output", "solana-client", "solana-config-program", "solana-core", @@ -3569,6 +3570,26 @@ dependencies = [ "url 2.1.1", ] +[[package]] +name = "solana-cli-output" +version = "1.3.13" +dependencies = [ + "Inflector", + "chrono", + "console", + "humantime 2.0.1", + "indicatif", + "serde", + "serde_derive", + "serde_json", + "solana-clap-utils", + "solana-client", + "solana-sdk 1.3.13", + "solana-stake-program", + "solana-transaction-status", + "solana-vote-program", +] + [[package]] name = "solana-client" version = "1.3.13" @@ -3576,6 +3597,7 @@ dependencies = [ "assert_matches", "bincode", "bs58", + "clap", "indicatif", "jsonrpc-core", "jsonrpc-http-server", @@ -3586,6 +3608,7 @@ dependencies = [ "serde_derive", "serde_json", "solana-account-decoder", + "solana-clap-utils", "solana-logger 1.3.13", "solana-net-utils", "solana-sdk 1.3.13", @@ -3976,7 +3999,7 @@ dependencies = [ "serde_yaml", "signal-hook", "solana-clap-utils", - "solana-cli", + "solana-cli-output", "solana-ledger", "solana-logger 1.3.13", "solana-measure", @@ -4483,6 +4506,7 @@ dependencies = [ "solana-clap-utils", "solana-cli", "solana-cli-config", + "solana-cli-output", "solana-client", "solana-logger 1.3.13", "solana-metrics", @@ -4723,8 +4747,8 @@ dependencies = [ "humantime 2.0.1", "log 0.4.8", "solana-clap-utils", - "solana-cli", "solana-cli-config", + "solana-cli-output", "solana-client", "solana-logger 1.3.13", "solana-metrics", diff --git a/Cargo.toml b/Cargo.toml index 63d78a653d..dedbd16717 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ members = [ "banks-server", "clap-utils", "cli-config", + "cli-output", "client", "core", "dos", diff --git a/clap-utils/src/fee_payer.rs b/clap-utils/src/fee_payer.rs new file mode 100644 index 0000000000..4031ab4e88 --- /dev/null +++ b/clap-utils/src/fee_payer.rs @@ -0,0 +1,19 @@ +use crate::{input_validators, ArgConstant}; +use clap::Arg; + +pub const FEE_PAYER_ARG: ArgConstant<'static> = ArgConstant { + name: "fee_payer", + long: "fee-payer", + help: "Specify the fee-payer account. This may be a keypair file, the ASK keyword \n\ + or the pubkey of an offline signer, provided an appropriate --signer argument \n\ + is also passed. Defaults to the client keypair.", +}; + +pub fn fee_payer_arg<'a, 'b>() -> Arg<'a, 'b> { + Arg::with_name(FEE_PAYER_ARG.name) + .long(FEE_PAYER_ARG.long) + .takes_value(true) + .value_name("KEYPAIR") + .validator(input_validators::is_valid_signer) + .help(FEE_PAYER_ARG.help) +} diff --git a/clap-utils/src/keypair.rs b/clap-utils/src/keypair.rs index 1b76bd2337..5d65d88190 100644 --- a/clap-utils/src/keypair.rs +++ b/clap-utils/src/keypair.rs @@ -11,6 +11,7 @@ use solana_remote_wallet::{ remote_wallet::{maybe_wallet_manager, RemoteWalletError, RemoteWalletManager}, }; use solana_sdk::{ + hash::Hash, pubkey::Pubkey, signature::{ keypair_from_seed, keypair_from_seed_phrase_and_passphrase, read_keypair, @@ -25,6 +26,81 @@ use std::{ sync::Arc, }; +pub struct SignOnly { + pub blockhash: Hash, + pub present_signers: Vec<(Pubkey, Signature)>, + pub absent_signers: Vec, + pub bad_signers: Vec, +} + +impl SignOnly { + pub fn has_all_signers(&self) -> bool { + self.absent_signers.is_empty() && self.bad_signers.is_empty() + } + + pub fn presigner_of(&self, pubkey: &Pubkey) -> Option { + presigner_from_pubkey_sigs(pubkey, &self.present_signers) + } +} +pub type CliSigners = Vec>; +pub type SignerIndex = usize; +pub struct CliSignerInfo { + pub signers: CliSigners, +} + +impl CliSignerInfo { + pub fn index_of(&self, pubkey: Option) -> Option { + if let Some(pubkey) = pubkey { + self.signers + .iter() + .position(|signer| signer.pubkey() == pubkey) + } else { + Some(0) + } + } +} + +pub struct DefaultSigner { + pub arg_name: String, + pub path: String, +} + +impl DefaultSigner { + pub fn generate_unique_signers( + &self, + bulk_signers: Vec>>, + matches: &ArgMatches<'_>, + wallet_manager: &mut Option>, + ) -> Result> { + let mut unique_signers = vec![]; + + // Determine if the default signer is needed + if bulk_signers.iter().any(|signer| signer.is_none()) { + let default_signer = self.signer_from_path(matches, wallet_manager)?; + unique_signers.push(default_signer); + } + + for signer in bulk_signers.into_iter() { + if let Some(signer) = signer { + if !unique_signers.iter().any(|s| s == &signer) { + unique_signers.push(signer); + } + } + } + Ok(CliSignerInfo { + signers: unique_signers, + }) + } + + pub fn signer_from_path( + &self, + matches: &ArgMatches, + wallet_manager: &mut Option>, + ) -> Result, Box> { + signer_from_path(matches, &self.path, &self.arg_name, wallet_manager) + } +} + pub enum KeypairUrl { Ask, Filepath(String), diff --git a/clap-utils/src/lib.rs b/clap-utils/src/lib.rs index ab51ffa627..38f78fe4e2 100644 --- a/clap-utils/src/lib.rs +++ b/clap-utils/src/lib.rs @@ -24,7 +24,9 @@ impl std::fmt::Debug for DisplayError { } pub mod commitment; +pub mod fee_payer; pub mod input_parsers; pub mod input_validators; pub mod keypair; +pub mod nonce; pub mod offline; diff --git a/clap-utils/src/nonce.rs b/clap-utils/src/nonce.rs new file mode 100644 index 0000000000..fd5ff2ef71 --- /dev/null +++ b/clap-utils/src/nonce.rs @@ -0,0 +1,47 @@ +use crate::{input_validators::*, offline::BLOCKHASH_ARG, ArgConstant}; +use clap::{App, Arg}; + +pub const NONCE_ARG: ArgConstant<'static> = ArgConstant { + name: "nonce", + long: "nonce", + help: "Provide the nonce account to use when creating a nonced \n\ + transaction. Nonced transactions are useful when a transaction \n\ + requires a lengthy signing process. Learn more about nonced \n\ + transactions at https://docs.solana.com/offline-signing/durable-nonce", +}; + +pub const NONCE_AUTHORITY_ARG: ArgConstant<'static> = ArgConstant { + name: "nonce_authority", + long: "nonce-authority", + help: "Provide the nonce authority keypair to use when signing a nonced transaction", +}; + +fn nonce_arg<'a, 'b>() -> Arg<'a, 'b> { + Arg::with_name(NONCE_ARG.name) + .long(NONCE_ARG.long) + .takes_value(true) + .value_name("PUBKEY") + .requires(BLOCKHASH_ARG.name) + .validator(is_valid_pubkey) + .help(NONCE_ARG.help) +} + +pub fn nonce_authority_arg<'a, 'b>() -> Arg<'a, 'b> { + Arg::with_name(NONCE_AUTHORITY_ARG.name) + .long(NONCE_AUTHORITY_ARG.long) + .takes_value(true) + .value_name("KEYPAIR") + .validator(is_valid_signer) + .help(NONCE_AUTHORITY_ARG.help) +} + +pub trait NonceArgs { + fn nonce_args(self) -> Self; +} + +impl NonceArgs for App<'_, '_> { + fn nonce_args(self) -> Self { + self.arg(nonce_arg()) + .arg(nonce_authority_arg().requires(NONCE_ARG.name)) + } +} diff --git a/clap-utils/src/offline.rs b/clap-utils/src/offline.rs index b0533a28de..81dce6ecf4 100644 --- a/clap-utils/src/offline.rs +++ b/clap-utils/src/offline.rs @@ -1,4 +1,5 @@ -use crate::ArgConstant; +use crate::{input_validators::*, ArgConstant}; +use clap::{App, Arg}; pub const BLOCKHASH_ARG: ArgConstant<'static> = ArgConstant { name: "blockhash", @@ -17,3 +18,43 @@ pub const SIGNER_ARG: ArgConstant<'static> = ArgConstant { long: "signer", help: "Provide a public-key/signature pair for the transaction", }; + +pub fn blockhash_arg<'a, 'b>() -> Arg<'a, 'b> { + Arg::with_name(BLOCKHASH_ARG.name) + .long(BLOCKHASH_ARG.long) + .takes_value(true) + .value_name("BLOCKHASH") + .validator(is_hash) + .help(BLOCKHASH_ARG.help) +} + +pub fn sign_only_arg<'a, 'b>() -> Arg<'a, 'b> { + Arg::with_name(SIGN_ONLY_ARG.name) + .long(SIGN_ONLY_ARG.long) + .takes_value(false) + .requires(BLOCKHASH_ARG.name) + .help(SIGN_ONLY_ARG.help) +} + +fn signer_arg<'a, 'b>() -> Arg<'a, 'b> { + Arg::with_name(SIGNER_ARG.name) + .long(SIGNER_ARG.long) + .takes_value(true) + .value_name("PUBKEY=SIGNATURE") + .validator(is_pubkey_sig) + .requires(BLOCKHASH_ARG.name) + .multiple(true) + .help(SIGNER_ARG.help) +} + +pub trait OfflineArgs { + fn offline_args(self) -> Self; +} + +impl OfflineArgs for App<'_, '_> { + fn offline_args(self) -> Self { + self.arg(blockhash_arg()) + .arg(sign_only_arg()) + .arg(signer_arg()) + } +} diff --git a/cli-output/Cargo.toml b/cli-output/Cargo.toml new file mode 100644 index 0000000000..1967730b18 --- /dev/null +++ b/cli-output/Cargo.toml @@ -0,0 +1,28 @@ +[package] +authors = ["Solana Maintainers "] +edition = "2018" +name = "solana-cli-output" +description = "Blockchain, Rebuilt for Scale" +version = "1.3.13" +repository = "https://github.com/solana-labs/solana" +license = "Apache-2.0" +homepage = "https://solana.com/" + +[dependencies] +chrono = { version = "0.4.11", features = ["serde"] } +console = "0.11.3" +humantime = "2.0.1" +Inflector = "0.11.4" +indicatif = "0.15.0" +serde = "1.0.112" +serde_derive = "1.0.103" +serde_json = "1.0.56" +solana-clap-utils = { path = "../clap-utils", version = "1.3.13" } +solana-client = { path = "../client", version = "1.3.13" } +solana-sdk = { path = "../sdk", version = "1.3.13" } +solana-stake-program = { path = "../programs/stake", version = "1.3.13" } +solana-transaction-status = { path = "../transaction-status", version = "1.3.13" } +solana-vote-program = { path = "../programs/vote", version = "1.3.13" } + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/cli/src/cli_output.rs b/cli-output/src/cli_output.rs similarity index 87% rename from cli/src/cli_output.rs rename to cli-output/src/cli_output.rs index 895851d26f..f5f3f20c3a 100644 --- a/cli/src/cli_output.rs +++ b/cli-output/src/cli_output.rs @@ -1,20 +1,22 @@ -use crate::{ - cli::build_balance_message, - display::{format_labeled_address, writeln_name_value}, -}; +use crate::display::{build_balance_message, format_labeled_address, writeln_name_value}; use chrono::{DateTime, NaiveDateTime, SecondsFormat, Utc}; use console::{style, Emoji}; use inflector::cases::titlecase::to_title_case; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use serde_json::{Map, Value}; +use solana_clap_utils::keypair::SignOnly; use solana_client::rpc_response::{ RpcAccountBalance, RpcKeyedAccount, RpcSupply, RpcVoteAccountInfo, }; use solana_sdk::{ clock::{self, Epoch, Slot, UnixTimestamp}, epoch_info::EpochInfo, + hash::Hash, native_token::lamports_to_sol, + pubkey::Pubkey, + signature::Signature, stake_history::StakeHistoryEntry, + transaction::Transaction, }; use solana_stake_program::stake_state::{Authorized, Lockup}; use solana_vote_program::{ @@ -24,6 +26,7 @@ use solana_vote_program::{ use std::{ collections::{BTreeMap, HashMap}, fmt, + str::FromStr, time::Duration, }; @@ -1145,3 +1148,149 @@ impl fmt::Display for CliFees { Ok(()) } } + +pub fn return_signers( + tx: &Transaction, + output_format: &OutputFormat, +) -> Result> { + let verify_results = tx.verify_with_results(); + let mut signers = Vec::new(); + let mut absent = Vec::new(); + let mut bad_sig = Vec::new(); + tx.signatures + .iter() + .zip(tx.message.account_keys.iter()) + .zip(verify_results.into_iter()) + .for_each(|((sig, key), res)| { + if res { + signers.push(format!("{}={}", key, sig)) + } else if *sig == Signature::default() { + absent.push(key.to_string()); + } else { + bad_sig.push(key.to_string()); + } + }); + + let cli_command = CliSignOnlyData { + blockhash: tx.message.recent_blockhash.to_string(), + signers, + absent, + bad_sig, + }; + + Ok(output_format.formatted_string(&cli_command)) +} + +pub fn parse_sign_only_reply_string(reply: &str) -> SignOnly { + let object: Value = serde_json::from_str(&reply).unwrap(); + let blockhash_str = object.get("blockhash").unwrap().as_str().unwrap(); + let blockhash = blockhash_str.parse::().unwrap(); + let mut present_signers: Vec<(Pubkey, Signature)> = Vec::new(); + let signer_strings = object.get("signers"); + if let Some(sig_strings) = signer_strings { + present_signers = sig_strings + .as_array() + .unwrap() + .iter() + .map(|signer_string| { + let mut signer = signer_string.as_str().unwrap().split('='); + let key = Pubkey::from_str(signer.next().unwrap()).unwrap(); + let sig = Signature::from_str(signer.next().unwrap()).unwrap(); + (key, sig) + }) + .collect(); + } + let mut absent_signers: Vec = Vec::new(); + let signer_strings = object.get("absent"); + if let Some(sig_strings) = signer_strings { + absent_signers = sig_strings + .as_array() + .unwrap() + .iter() + .map(|val| { + let s = val.as_str().unwrap(); + Pubkey::from_str(s).unwrap() + }) + .collect(); + } + let mut bad_signers: Vec = Vec::new(); + let signer_strings = object.get("badSig"); + if let Some(sig_strings) = signer_strings { + bad_signers = sig_strings + .as_array() + .unwrap() + .iter() + .map(|val| { + let s = val.as_str().unwrap(); + Pubkey::from_str(s).unwrap() + }) + .collect(); + } + + SignOnly { + blockhash, + present_signers, + absent_signers, + bad_signers, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use solana_sdk::{ + message::Message, + pubkey::Pubkey, + signature::{keypair_from_seed, NullSigner, Signature, Signer, SignerError}, + system_instruction, + transaction::Transaction, + }; + + #[test] + fn test_return_signers() { + struct BadSigner { + pubkey: Pubkey, + } + + impl BadSigner { + pub fn new(pubkey: Pubkey) -> Self { + Self { pubkey } + } + } + + impl Signer for BadSigner { + fn try_pubkey(&self) -> Result { + Ok(self.pubkey) + } + + fn try_sign_message(&self, _message: &[u8]) -> Result { + Ok(Signature::new(&[1u8; 64])) + } + } + + let present: Box = Box::new(keypair_from_seed(&[2u8; 32]).unwrap()); + let absent: Box = Box::new(NullSigner::new(&Pubkey::new(&[3u8; 32]))); + let bad: Box = Box::new(BadSigner::new(Pubkey::new(&[4u8; 32]))); + let to = Pubkey::new(&[5u8; 32]); + let nonce = Pubkey::new(&[6u8; 32]); + let from = present.pubkey(); + let fee_payer = absent.pubkey(); + let nonce_auth = bad.pubkey(); + let mut tx = Transaction::new_unsigned(Message::new_with_nonce( + vec![system_instruction::transfer(&from, &to, 42)], + Some(&fee_payer), + &nonce, + &nonce_auth, + )); + + let signers = vec![present.as_ref(), absent.as_ref(), bad.as_ref()]; + let blockhash = Hash::new(&[7u8; 32]); + tx.try_partial_sign(&signers, blockhash).unwrap(); + let res = return_signers(&tx, &OutputFormat::JsonCompact).unwrap(); + let sign_only = parse_sign_only_reply_string(&res); + assert_eq!(sign_only.blockhash, blockhash); + assert_eq!(sign_only.present_signers[0].0, present.pubkey()); + assert_eq!(sign_only.absent_signers[0], absent.pubkey()); + assert_eq!(sign_only.bad_signers[0], bad.pubkey()); + } +} diff --git a/cli/src/display.rs b/cli-output/src/display.rs similarity index 92% rename from cli/src/display.rs rename to cli-output/src/display.rs index 977abfc4b7..98bf0f2e94 100644 --- a/cli/src/display.rs +++ b/cli-output/src/display.rs @@ -1,4 +1,3 @@ -use crate::cli::SettingType; use console::style; use indicatif::{ProgressBar, ProgressStyle}; use solana_sdk::{ @@ -8,6 +7,24 @@ use solana_sdk::{ use solana_transaction_status::UiTransactionStatusMeta; use std::{collections::HashMap, fmt, io}; +pub fn build_balance_message(lamports: u64, use_lamports_unit: bool, show_unit: bool) -> String { + if use_lamports_unit { + let ess = if lamports == 1 { "" } else { "s" }; + let unit = if show_unit { + format!(" lamport{}", ess) + } else { + "".to_string() + }; + format!("{:?}{}", lamports, unit) + } else { + let sol = lamports_to_sol(lamports); + let sol_str = format!("{:.9}", sol); + let pretty_sol = sol_str.trim_end_matches('0').trim_end_matches('.'); + let unit = if show_unit { " SOL" } else { "" }; + format!("{}{}", pretty_sol, unit) + } +} + // Pretty print a "name value" pub fn println_name_value(name: &str, value: &str) { let styled_value = if value == "" { @@ -40,21 +57,6 @@ pub fn format_labeled_address(pubkey: &str, address_labels: &HashMap "", - SettingType::Computed => "(computed)", - SettingType::SystemDefault => "(default)", - }; - - println!( - "{} {} {}", - style(name).bold(), - style(value), - style(description).italic(), - ); -} - pub fn println_signers( blockhash: &Hash, signers: &[String], diff --git a/cli-output/src/lib.rs b/cli-output/src/lib.rs new file mode 100644 index 0000000000..db82ff080e --- /dev/null +++ b/cli-output/src/lib.rs @@ -0,0 +1,3 @@ +mod cli_output; +pub mod display; +pub use cli_output::*; diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 800033334f..b3242b82cd 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -31,6 +31,7 @@ solana-account-decoder = { path = "../account-decoder", version = "1.3.13" } solana-budget-program = { path = "../programs/budget", version = "1.3.13" } solana-clap-utils = { path = "../clap-utils", version = "1.3.13" } solana-cli-config = { path = "../cli-config", version = "1.3.13" } +solana-cli-output = { path = "../cli-output", version = "1.3.13" } solana-client = { path = "../client", version = "1.3.13" } solana-config-program = { path = "../programs/config", version = "1.3.13" } solana-faucet = { path = "../faucet", version = "1.3.13" } diff --git a/cli/src/cli.rs b/cli/src/cli.rs index 83e45e31f8..3125c91427 100644 --- a/cli/src/cli.rs +++ b/cli/src/cli.rs @@ -1,14 +1,5 @@ use crate::{ - checks::*, - cli_output::{CliAccount, CliSignOnlyData, CliSignature, OutputFormat}, - cluster_query::*, - display::{new_spinner_progress_bar, println_name_value, println_transaction}, - nonce::{self, *}, - offline::{blockhash_query::BlockhashQuery, *}, - spend_utils::*, - stake::*, - validator_info::*, - vote::*, + checks::*, cluster_query::*, nonce::*, spend_utils::*, stake::*, validator_info::*, vote::*, }; use chrono::prelude::*; use clap::{value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand}; @@ -18,11 +9,25 @@ use serde_json::{self, json, Value}; use solana_account_decoder::{UiAccount, UiAccountEncoding}; use solana_budget_program::budget_instruction::{self, BudgetError}; use solana_clap_utils::{ - commitment::commitment_arg_with_default, input_parsers::*, input_validators::*, - keypair::signer_from_path, offline::SIGN_ONLY_ARG, ArgConstant, + self, + commitment::commitment_arg_with_default, + fee_payer::{fee_payer_arg, FEE_PAYER_ARG}, + input_parsers::*, + input_validators::*, + keypair::*, + nonce::*, + offline::*, +}; +use solana_cli_output::{ + display::{ + build_balance_message, new_spinner_progress_bar, println_name_value, println_transaction, + }, + return_signers, CliAccount, CliSignature, OutputFormat, }; use solana_client::{ + blockhash_query::BlockhashQuery, client_error::{ClientError, ClientErrorKind, Result as ClientResult}, + nonce_utils, rpc_client::RpcClient, rpc_config::{RpcLargestAccountsFilter, RpcSendTransactionConfig}, rpc_response::{Response, RpcKeyedAccount}, @@ -37,12 +42,10 @@ use solana_sdk::{ clock::{Epoch, Slot, DEFAULT_TICKS_PER_SECOND}, commitment_config::CommitmentConfig, decode_error::DecodeError, - fee_calculator::FeeCalculator, hash::Hash, instruction::InstructionError, loader_instruction, message::Message, - native_token::lamports_to_sol, pubkey::{Pubkey, MAX_SEED_LEN}, signature::{Keypair, Signature, Signer, SignerError}, signers::Signers, @@ -71,97 +74,9 @@ use std::{ use thiserror::Error; use url::Url; -pub type CliSigners = Vec>; -pub type SignerIndex = usize; -pub(crate) struct CliSignerInfo { - pub signers: CliSigners, -} - -impl CliSignerInfo { - pub(crate) fn index_of(&self, pubkey: Option) -> Option { - if let Some(pubkey) = pubkey { - self.signers - .iter() - .position(|signer| signer.pubkey() == pubkey) - } else { - Some(0) - } - } -} - -pub(crate) fn generate_unique_signers( - bulk_signers: Vec>>, - matches: &ArgMatches<'_>, - default_signer_path: &str, - wallet_manager: &mut Option>, -) -> Result> { - let mut unique_signers = vec![]; - - // Determine if the default signer is needed - if bulk_signers.iter().any(|signer| signer.is_none()) { - let default_signer = - signer_from_path(matches, default_signer_path, "keypair", wallet_manager)?; - unique_signers.push(default_signer); - } - - for signer in bulk_signers.into_iter() { - if let Some(signer) = signer { - if !unique_signers.iter().any(|s| s == &signer) { - unique_signers.push(signer); - } - } - } - Ok(CliSignerInfo { - signers: unique_signers, - }) -} - const DATA_CHUNK_SIZE: usize = 229; // Keep program chunks under PACKET_DATA_SIZE pub const DEFAULT_RPC_TIMEOUT_SECONDS: &str = "30"; -pub const FEE_PAYER_ARG: ArgConstant<'static> = ArgConstant { - name: "fee_payer", - long: "fee-payer", - help: "Specify the fee-payer account. This may be a keypair file, the ASK keyword \n\ - or the pubkey of an offline signer, provided an appropriate --signer argument \n\ - is also passed. Defaults to the client keypair.", -}; - -pub fn fee_payer_arg<'a, 'b>() -> Arg<'a, 'b> { - Arg::with_name(FEE_PAYER_ARG.name) - .long(FEE_PAYER_ARG.long) - .takes_value(true) - .value_name("KEYPAIR") - .validator(is_valid_signer) - .help(FEE_PAYER_ARG.help) -} - -#[derive(Debug)] -pub struct KeypairEq(Keypair); - -impl From for KeypairEq { - fn from(keypair: Keypair) -> Self { - Self(keypair) - } -} - -impl PartialEq for KeypairEq { - fn eq(&self, other: &Self) -> bool { - self.pubkey() == other.pubkey() - } -} - -impl std::ops::Deref for KeypairEq { - type Target = Keypair; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -pub fn nonce_authority_arg<'a, 'b>() -> Arg<'a, 'b> { - nonce::nonce_authority_arg().requires(NONCE_ARG.name) -} - #[derive(Default, Debug, PartialEq)] pub struct PayCommand { pub amount: SpendAmount, @@ -467,7 +382,7 @@ pub enum CliError { #[error("insufficient funds for spend ({0} SOL) and fee ({1} SOL)")] InsufficientFundsForSpendAndFee(f64, f64), #[error(transparent)] - InvalidNonce(CliNonceError), + InvalidNonce(nonce_utils::Error), #[error("dynamic program error: {0}")] DynamicProgramError(String), #[error("rpc request error: {0}")] @@ -482,10 +397,10 @@ impl From> for CliError { } } -impl From for CliError { - fn from(error: CliNonceError) -> Self { +impl From for CliError { + fn from(error: nonce_utils::Error) -> Self { match error { - CliNonceError::Client(client_error) => Self::RpcRequestError(client_error), + nonce_utils::Error::Client(client_error) => Self::RpcRequestError(client_error), _ => Self::InvalidNonce(error), } } @@ -622,7 +537,7 @@ impl Default for CliConfig<'_> { pub fn parse_command( matches: &ArgMatches<'_>, - default_signer_path: &str, + default_signer: &DefaultSigner, wallet_manager: &mut Option>, ) -> Result> { let response = match matches.subcommand() { @@ -637,7 +552,7 @@ pub fn parse_command( signers: vec![], }), ("create-address-with-seed", Some(matches)) => { - parse_create_address_with_seed(matches, default_signer_path, wallet_manager) + parse_create_address_with_seed(matches, default_signer, wallet_manager) } ("fees", Some(_matches)) => Ok(CliCommandInfo { command: CliCommand::Fees, @@ -665,7 +580,7 @@ pub fn parse_command( command: CliCommand::LeaderSchedule, signers: vec![], }), - ("ping", Some(matches)) => parse_cluster_ping(matches, default_signer_path, wallet_manager), + ("ping", Some(matches)) => parse_cluster_ping(matches, default_signer, wallet_manager), ("live-slots", Some(_matches)) => Ok(CliCommandInfo { command: CliCommand::LiveSlots, signers: vec![], @@ -682,28 +597,21 @@ pub fn parse_command( } // Nonce Commands ("authorize-nonce-account", Some(matches)) => { - parse_authorize_nonce_account(matches, default_signer_path, wallet_manager) + parse_authorize_nonce_account(matches, default_signer, wallet_manager) } ("create-nonce-account", Some(matches)) => { - parse_nonce_create_account(matches, default_signer_path, wallet_manager) + parse_nonce_create_account(matches, default_signer, wallet_manager) } ("nonce", Some(matches)) => parse_get_nonce(matches, wallet_manager), - ("new-nonce", Some(matches)) => { - parse_new_nonce(matches, default_signer_path, wallet_manager) - } + ("new-nonce", Some(matches)) => parse_new_nonce(matches, default_signer, wallet_manager), ("nonce-account", Some(matches)) => parse_show_nonce_account(matches, wallet_manager), ("withdraw-from-nonce-account", Some(matches)) => { - parse_withdraw_from_nonce_account(matches, default_signer_path, wallet_manager) + parse_withdraw_from_nonce_account(matches, default_signer, wallet_manager) } // Program Deployment ("deploy", Some(matches)) => { let (address_signer, _address) = signer_of(matches, "address_signer", wallet_manager)?; - let mut signers = vec![signer_from_path( - matches, - default_signer_path, - "keypair", - wallet_manager, - )?]; + let mut signers = vec![default_signer.signer_from_path(matches, wallet_manager)?]; let address = address_signer.map(|signer| { signers.push(signer); 1 @@ -721,74 +629,69 @@ pub fn parse_command( } // Stake Commands ("create-stake-account", Some(matches)) => { - parse_stake_create_account(matches, default_signer_path, wallet_manager) + parse_stake_create_account(matches, default_signer, wallet_manager) } ("delegate-stake", Some(matches)) => { - parse_stake_delegate_stake(matches, default_signer_path, wallet_manager) + parse_stake_delegate_stake(matches, default_signer, wallet_manager) } ("withdraw-stake", Some(matches)) => { - parse_stake_withdraw_stake(matches, default_signer_path, wallet_manager) + parse_stake_withdraw_stake(matches, default_signer, wallet_manager) } ("deactivate-stake", Some(matches)) => { - parse_stake_deactivate_stake(matches, default_signer_path, wallet_manager) + parse_stake_deactivate_stake(matches, default_signer, wallet_manager) } ("split-stake", Some(matches)) => { - parse_split_stake(matches, default_signer_path, wallet_manager) + parse_split_stake(matches, default_signer, wallet_manager) } ("merge-stake", Some(matches)) => { - parse_merge_stake(matches, default_signer_path, wallet_manager) + parse_merge_stake(matches, default_signer, wallet_manager) } ("stake-authorize", Some(matches)) => { - parse_stake_authorize(matches, default_signer_path, wallet_manager) + parse_stake_authorize(matches, default_signer, wallet_manager) } ("stake-set-lockup", Some(matches)) => { - parse_stake_set_lockup(matches, default_signer_path, wallet_manager) + parse_stake_set_lockup(matches, default_signer, wallet_manager) } ("stake-account", Some(matches)) => parse_show_stake_account(matches, wallet_manager), ("stake-history", Some(matches)) => parse_show_stake_history(matches), // Validator Info Commands ("validator-info", Some(matches)) => match matches.subcommand() { ("publish", Some(matches)) => { - parse_validator_info_command(matches, default_signer_path, wallet_manager) + parse_validator_info_command(matches, default_signer, wallet_manager) } ("get", Some(matches)) => parse_get_validator_info_command(matches), _ => unreachable!(), }, // Vote Commands ("create-vote-account", Some(matches)) => { - parse_create_vote_account(matches, default_signer_path, wallet_manager) + parse_create_vote_account(matches, default_signer, wallet_manager) } ("vote-update-validator", Some(matches)) => { - parse_vote_update_validator(matches, default_signer_path, wallet_manager) + parse_vote_update_validator(matches, default_signer, wallet_manager) } ("vote-update-commission", Some(matches)) => { - parse_vote_update_commission(matches, default_signer_path, wallet_manager) + parse_vote_update_commission(matches, default_signer, wallet_manager) } ("vote-authorize-voter", Some(matches)) => parse_vote_authorize( matches, - default_signer_path, + default_signer, wallet_manager, VoteAuthorize::Voter, ), ("vote-authorize-withdrawer", Some(matches)) => parse_vote_authorize( matches, - default_signer_path, + default_signer, wallet_manager, VoteAuthorize::Withdrawer, ), ("vote-account", Some(matches)) => parse_vote_get_account_command(matches, wallet_manager), ("withdraw-from-vote-account", Some(matches)) => { - parse_withdraw_from_vote_account(matches, default_signer_path, wallet_manager) + parse_withdraw_from_vote_account(matches, default_signer, wallet_manager) } // Wallet Commands ("address", Some(matches)) => Ok(CliCommandInfo { command: CliCommand::Address, - signers: vec![signer_from_path( - matches, - default_signer_path, - "keypair", - wallet_manager, - )?], + signers: vec![default_signer.signer_from_path(matches, wallet_manager)?], }), ("airdrop", Some(matches)) => { let faucet_port = matches @@ -809,12 +712,7 @@ pub fn parse_command( let signers = if pubkey.is_some() { vec![] } else { - vec![signer_from_path( - matches, - default_signer_path, - "keypair", - wallet_manager, - )?] + vec![default_signer.signer_from_path(matches, wallet_manager)?] }; let lamports = lamports_of_sol(matches, "amount").unwrap(); Ok(CliCommandInfo { @@ -832,12 +730,7 @@ pub fn parse_command( let signers = if pubkey.is_some() { vec![] } else { - vec![signer_from_path( - matches, - default_signer_path, - "keypair", - wallet_manager, - )?] + vec![default_signer.signer_from_path(matches, wallet_manager)?] }; Ok(CliCommandInfo { command: CliCommand::Balance { @@ -849,8 +742,7 @@ pub fn parse_command( } ("cancel", Some(matches)) => { let process_id = value_of(matches, "process_id").unwrap(); - let default_signer = - signer_from_path(matches, default_signer_path, "keypair", wallet_manager)?; + let default_signer = default_signer.signer_from_path(matches, wallet_manager)?; Ok(CliCommandInfo { command: CliCommand::Cancel(process_id), @@ -912,12 +804,8 @@ pub fn parse_command( if nonce_account.is_some() { bulk_signers.push(nonce_authority); } - let signer_info = generate_unique_signers( - bulk_signers, - matches, - default_signer_path, - wallet_manager, - )?; + let signer_info = + default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?; Ok(CliCommandInfo { command: CliCommand::Pay(PayCommand { @@ -960,8 +848,7 @@ pub fn parse_command( let to = value_of(matches, "to").unwrap(); let process_id = value_of(matches, "process_id").unwrap(); - let default_signer = - signer_from_path(matches, default_signer_path, "keypair", wallet_manager)?; + let default_signer = default_signer.signer_from_path(matches, wallet_manager)?; Ok(CliCommandInfo { command: CliCommand::Witness(to, process_id), @@ -982,8 +869,7 @@ pub fn parse_command( } else { Utc::now() }; - let default_signer = - signer_from_path(matches, default_signer_path, "keypair", wallet_manager)?; + let default_signer = default_signer.signer_from_path(matches, wallet_manager)?; Ok(CliCommandInfo { command: CliCommand::TimeElapsed(to, process_id, dt), @@ -1008,12 +894,8 @@ pub fn parse_command( bulk_signers.push(nonce_authority); } - let signer_info = generate_unique_signers( - bulk_signers, - matches, - default_signer_path, - wallet_manager, - )?; + let signer_info = + default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?; Ok(CliCommandInfo { command: CliCommand::Transfer { @@ -1044,66 +926,16 @@ pub fn parse_command( pub type ProcessResult = Result>; -pub fn get_blockhash_and_fee_calculator( - rpc_client: &RpcClient, - sign_only: bool, - blockhash: Option, -) -> Result<(Hash, FeeCalculator), Box> { - Ok(if let Some(blockhash) = blockhash { - if sign_only { - (blockhash, FeeCalculator::default()) - } else { - (blockhash, rpc_client.get_recent_blockhash()?.1) - } - } else { - rpc_client.get_recent_blockhash()? - }) -} - -pub fn return_signers(tx: &Transaction, config: &CliConfig) -> ProcessResult { - let verify_results = tx.verify_with_results(); - let mut signers = Vec::new(); - let mut absent = Vec::new(); - let mut bad_sig = Vec::new(); - tx.signatures - .iter() - .zip(tx.message.account_keys.iter()) - .zip(verify_results.into_iter()) - .for_each(|((sig, key), res)| { - if res { - signers.push(format!("{}={}", key, sig)) - } else if *sig == Signature::default() { - absent.push(key.to_string()); - } else { - bad_sig.push(key.to_string()); - } - }); - - let cli_command = CliSignOnlyData { - blockhash: tx.message.recent_blockhash.to_string(), - signers, - absent, - bad_sig, - }; - - Ok(config.output_format.formatted_string(&cli_command)) -} - pub fn parse_create_address_with_seed( matches: &ArgMatches<'_>, - default_signer_path: &str, + default_signer: &DefaultSigner, wallet_manager: &mut Option>, ) -> Result { let from_pubkey = pubkey_of_signer(matches, "from", wallet_manager)?; let signers = if from_pubkey.is_some() { vec![] } else { - vec![signer_from_path( - matches, - default_signer_path, - "keypair", - wallet_manager, - )?] + vec![default_signer.signer_from_path(matches, wallet_manager)?] }; let program_id = match matches.value_of("program_id").unwrap() { @@ -1553,10 +1385,10 @@ fn process_pay( if sign_only { tx.try_partial_sign(&config.signers, blockhash)?; - return_signers(&tx, &config) + return_signers(&tx, &config.output_format) } else { if let Some(nonce_account) = &nonce_account { - let nonce_account = nonce::get_account_with_commitment( + let nonce_account = nonce_utils::get_account_with_commitment( rpc_client, nonce_account, config.commitment, @@ -1606,7 +1438,7 @@ fn process_pay( let mut tx = Transaction::new_unsigned(message); if sign_only { tx.try_partial_sign(&[config.signers[0], &contract_state], blockhash)?; - return_signers(&tx, &config) + return_signers(&tx, &config.output_format) } else { tx.try_sign(&[config.signers[0], &contract_state], blockhash)?; let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config( @@ -1657,7 +1489,7 @@ fn process_pay( let mut tx = Transaction::new_unsigned(message); if sign_only { tx.try_partial_sign(&[config.signers[0], &contract_state], blockhash)?; - return_signers(&tx, &config) + return_signers(&tx, &config.output_format) } else { tx.try_sign(&[config.signers[0], &contract_state], blockhash)?; let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config( @@ -1785,11 +1617,14 @@ fn process_transfer( if sign_only { tx.try_partial_sign(&config.signers, recent_blockhash)?; - return_signers(&tx, &config) + return_signers(&tx, &config.output_format) } else { if let Some(nonce_account) = &nonce_account { - let nonce_account = - nonce::get_account_with_commitment(rpc_client, nonce_account, config.commitment)?; + let nonce_account = nonce_utils::get_account_with_commitment( + rpc_client, + nonce_account, + config.commitment, + )?; check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?; } @@ -2506,28 +2341,6 @@ where } } -pub(crate) fn build_balance_message( - lamports: u64, - use_lamports_unit: bool, - show_unit: bool, -) -> String { - if use_lamports_unit { - let ess = if lamports == 1 { "" } else { "s" }; - let unit = if show_unit { - format!(" lamport{}", ess) - } else { - "".to_string() - }; - format!("{:?}{}", lamports, unit) - } else { - let sol = lamports_to_sol(lamports); - let sol_str = format!("{:.9}", sol); - let pretty_sol = sol_str.trim_end_matches('0').trim_end_matches('.'); - let unit = if show_unit { " SOL" } else { "" }; - format!("{}{}", pretty_sol, unit) - } -} - pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, 'v> { App::new(name) .about(about) @@ -2752,8 +2565,7 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, ' .takes_value(false), ) .offline_args() - .arg(nonce_arg()) - .arg(nonce_authority_arg()), + .nonce_args() ) .subcommand( SubCommand::with_name("resolve-signer") @@ -2845,8 +2657,7 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, ' .help("Return signature immediately after submitting the transaction, instead of waiting for confirmations"), ) .offline_args() - .arg(nonce_arg()) - .arg(nonce_authority_arg()) + .nonce_args() .arg(fee_payer_arg()), ) .subcommand( @@ -2883,12 +2694,10 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, ' mod tests { use super::*; use serde_json::Value; - use solana_client::mock_sender::SIGNATURE; + use solana_client::{blockhash_query, mock_sender::SIGNATURE}; use solana_sdk::{ pubkey::Pubkey, - signature::{ - keypair_from_seed, read_keypair_file, write_keypair_file, NullSigner, Presigner, - }, + signature::{keypair_from_seed, read_keypair_file, write_keypair_file, Presigner}, transaction::TransactionError, }; use std::path::PathBuf; @@ -2915,13 +2724,19 @@ mod tests { let default_keypair_file = make_tmp_path("keypair_file"); write_keypair_file(&default_keypair, &default_keypair_file).unwrap(); - let signer_info = - generate_unique_signers(vec![], &matches, &default_keypair_file, &mut None).unwrap(); + let default_signer = DefaultSigner { + arg_name: "keypair".to_string(), + path: default_keypair_file, + }; + + let signer_info = default_signer + .generate_unique_signers(vec![], &matches, &mut None) + .unwrap(); assert_eq!(signer_info.signers.len(), 0); - let signer_info = - generate_unique_signers(vec![None, None], &matches, &default_keypair_file, &mut None) - .unwrap(); + let signer_info = default_signer + .generate_unique_signers(vec![None, None], &matches, &mut None) + .unwrap(); assert_eq!(signer_info.signers.len(), 1); assert_eq!(signer_info.index_of(None), Some(0)); assert_eq!(signer_info.index_of(Some(Pubkey::new_rand())), None); @@ -2931,8 +2746,9 @@ mod tests { let keypair0_clone = keypair_from_seed(&[1u8; 32]).unwrap(); let keypair0_clone_pubkey = keypair0.pubkey(); let signers = vec![None, Some(keypair0.into()), Some(keypair0_clone.into())]; - let signer_info = - generate_unique_signers(signers, &matches, &default_keypair_file, &mut None).unwrap(); + let signer_info = default_signer + .generate_unique_signers(signers, &matches, &mut None) + .unwrap(); assert_eq!(signer_info.signers.len(), 2); assert_eq!(signer_info.index_of(None), Some(0)); assert_eq!(signer_info.index_of(Some(keypair0_pubkey)), Some(1)); @@ -2942,8 +2758,9 @@ mod tests { let keypair0_pubkey = keypair0.pubkey(); let keypair0_clone = keypair_from_seed(&[1u8; 32]).unwrap(); let signers = vec![Some(keypair0.into()), Some(keypair0_clone.into())]; - let signer_info = - generate_unique_signers(signers, &matches, &default_keypair_file, &mut None).unwrap(); + let signer_info = default_signer + .generate_unique_signers(signers, &matches, &mut None) + .unwrap(); assert_eq!(signer_info.signers.len(), 1); assert_eq!(signer_info.index_of(Some(keypair0_pubkey)), Some(0)); @@ -2963,8 +2780,9 @@ mod tests { Some(presigner1.into()), Some(keypair1.into()), ]; - let signer_info = - generate_unique_signers(signers, &matches, &default_keypair_file, &mut None).unwrap(); + let signer_info = default_signer + .generate_unique_signers(signers, &matches, &mut None) + .unwrap(); assert_eq!(signer_info.signers.len(), 2); assert_eq!(signer_info.index_of(Some(keypair0_pubkey)), Some(0)); assert_eq!(signer_info.index_of(Some(keypair1_pubkey)), Some(1)); @@ -2985,13 +2803,21 @@ mod tests { let witness1_string = format!("{}", witness1); let dt = Utc.ymd(2018, 9, 19).and_hms(17, 30, 59); + let default_keypair = Keypair::new(); + let keypair_file = make_tmp_path("keypair_file"); + write_keypair_file(&default_keypair, &keypair_file).unwrap(); + let keypair = read_keypair_file(&keypair_file).unwrap(); + let default_signer = DefaultSigner { + path: keypair_file.clone(), + arg_name: "".to_string(), + }; // Test Airdrop Subcommand let test_airdrop = test_commands .clone() .get_matches_from(vec!["test", "airdrop", "50", &pubkey_string]); assert_eq!( - parse_command(&test_airdrop, "", &mut None).unwrap(), + parse_command(&test_airdrop, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::Airdrop { faucet_host: None, @@ -3004,17 +2830,13 @@ mod tests { ); // Test Balance Subcommand, incl pubkey and keypair-file inputs - let default_keypair = Keypair::new(); - let keypair_file = make_tmp_path("keypair_file"); - write_keypair_file(&default_keypair, &keypair_file).unwrap(); - let keypair = read_keypair_file(&keypair_file).unwrap(); let test_balance = test_commands.clone().get_matches_from(vec![ "test", "balance", &keypair.pubkey().to_string(), ]); assert_eq!( - parse_command(&test_balance, "", &mut None).unwrap(), + parse_command(&test_balance, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::Balance { pubkey: Some(keypair.pubkey()), @@ -3030,7 +2852,7 @@ mod tests { "--lamports", ]); assert_eq!( - parse_command(&test_balance, "", &mut None).unwrap(), + parse_command(&test_balance, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::Balance { pubkey: Some(keypair.pubkey()), @@ -3044,7 +2866,7 @@ mod tests { .clone() .get_matches_from(vec!["test", "balance", "--lamports"]); assert_eq!( - parse_command(&test_balance, &keypair_file, &mut None).unwrap(), + parse_command(&test_balance, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::Balance { pubkey: None, @@ -3060,7 +2882,7 @@ mod tests { .clone() .get_matches_from(vec!["test", "cancel", &pubkey_string]); assert_eq!( - parse_command(&test_cancel, &keypair_file, &mut None).unwrap(), + parse_command(&test_cancel, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::Cancel(pubkey), signers: vec![read_keypair_file(&keypair_file).unwrap().into()], @@ -3075,7 +2897,7 @@ mod tests { .clone() .get_matches_from(vec!["test", "confirm", &signature_string]); assert_eq!( - parse_command(&test_confirm, "", &mut None).unwrap(), + parse_command(&test_confirm, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::Confirm(signature), signers: vec![], @@ -3084,7 +2906,7 @@ mod tests { let test_bad_signature = test_commands .clone() .get_matches_from(vec!["test", "confirm", "deadbeef"]); - assert!(parse_command(&test_bad_signature, "", &mut None).is_err()); + assert!(parse_command(&test_bad_signature, &default_signer, &mut None).is_err()); // Test CreateAddressWithSeed let from_pubkey = Some(Pubkey::new_rand()); @@ -3103,7 +2925,7 @@ mod tests { &from_str, ]); assert_eq!( - parse_command(&test_create_address_with_seed, "", &mut None).unwrap(), + parse_command(&test_create_address_with_seed, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::CreateAddressWithSeed { from_pubkey, @@ -3121,7 +2943,7 @@ mod tests { "STAKE", ]); assert_eq!( - parse_command(&test_create_address_with_seed, &keypair_file, &mut None).unwrap(), + parse_command(&test_create_address_with_seed, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::CreateAddressWithSeed { from_pubkey: None, @@ -3138,7 +2960,7 @@ mod tests { .clone() .get_matches_from(vec!["test", "deploy", "/Users/test/program.o"]); assert_eq!( - parse_command(&test_deploy, &keypair_file, &mut None).unwrap(), + parse_command(&test_deploy, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::Deploy { program_location: "/Users/test/program.o".to_string(), @@ -3159,7 +2981,7 @@ mod tests { &custom_address_file, ]); assert_eq!( - parse_command(&test_deploy, &keypair_file, &mut None).unwrap(), + parse_command(&test_deploy, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::Deploy { program_location: "/Users/test/program.o".to_string(), @@ -3179,7 +3001,7 @@ mod tests { .clone() .get_matches_from(vec!["test", "resolve-signer", &keypair_file]); assert_eq!( - parse_command(&test_resolve_signer, "", &mut None).unwrap(), + parse_command(&test_resolve_signer, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::ResolveSigner(Some(keypair_file.clone())), signers: vec![], @@ -3191,7 +3013,7 @@ mod tests { .clone() .get_matches_from(vec!["test", "resolve-signer", &pubkey_string]); assert_eq!( - parse_command(&test_resolve_signer, "", &mut None).unwrap(), + parse_command(&test_resolve_signer, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::ResolveSigner(Some(pubkey.to_string())), signers: vec![], @@ -3204,7 +3026,7 @@ mod tests { .clone() .get_matches_from(vec!["test", "pay", &pubkey_string, "50"]); assert_eq!( - parse_command(&test_pay, &keypair_file, &mut None).unwrap(), + parse_command(&test_pay, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::Pay(PayCommand { amount: SpendAmount::Some(50_000_000_000), @@ -3227,7 +3049,7 @@ mod tests { &witness1_string, ]); assert_eq!( - parse_command(&test_pay_multiple_witnesses, &keypair_file, &mut None).unwrap(), + parse_command(&test_pay_multiple_witnesses, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::Pay(PayCommand { amount: SpendAmount::Some(50_000_000_000), @@ -3247,7 +3069,7 @@ mod tests { &witness0_string, ]); assert_eq!( - parse_command(&test_pay_single_witness, &keypair_file, &mut None).unwrap(), + parse_command(&test_pay_single_witness, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::Pay(PayCommand { amount: SpendAmount::Some(50_000_000_000), @@ -3271,7 +3093,7 @@ mod tests { &witness0_string, ]); assert_eq!( - parse_command(&test_pay_timestamp, &keypair_file, &mut None).unwrap(), + parse_command(&test_pay_timestamp, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::Pay(PayCommand { amount: SpendAmount::Some(50_000_000_000), @@ -3297,7 +3119,7 @@ mod tests { "--sign-only", ]); assert_eq!( - parse_command(&test_pay, &keypair_file, &mut None).unwrap(), + parse_command(&test_pay, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::Pay(PayCommand { amount: SpendAmount::Some(50_000_000_000), @@ -3320,7 +3142,7 @@ mod tests { &blockhash_string, ]); assert_eq!( - parse_command(&test_pay, &keypair_file, &mut None).unwrap(), + parse_command(&test_pay, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::Pay(PayCommand { amount: SpendAmount::Some(50_000_000_000), @@ -3349,7 +3171,7 @@ mod tests { &pubkey_string, ]); assert_eq!( - parse_command(&test_pay, &keypair_file, &mut None).unwrap(), + parse_command(&test_pay, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::Pay(PayCommand { amount: SpendAmount::Some(50_000_000_000), @@ -3382,7 +3204,7 @@ mod tests { &keypair_file, ]); assert_eq!( - parse_command(&test_pay, &keypair_file, &mut None).unwrap(), + parse_command(&test_pay, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::Pay(PayCommand { amount: SpendAmount::Some(50_000_000_000), @@ -3420,7 +3242,7 @@ mod tests { &signer_arg, ]); assert_eq!( - parse_command(&test_pay, &keypair_file, &mut None).unwrap(), + parse_command(&test_pay, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::Pay(PayCommand { amount: SpendAmount::Some(50_000_000_000), @@ -3458,7 +3280,7 @@ mod tests { "--signer", &signer_arg, ]); - assert!(parse_command(&test_pay, &keypair_file, &mut None).is_err()); + assert!(parse_command(&test_pay, &default_signer, &mut None).is_err()); // Test Send-Signature Subcommand let test_send_signature = test_commands.clone().get_matches_from(vec![ @@ -3468,7 +3290,7 @@ mod tests { &pubkey_string, ]); assert_eq!( - parse_command(&test_send_signature, &keypair_file, &mut None).unwrap(), + parse_command(&test_send_signature, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::Witness(pubkey, pubkey), signers: vec![read_keypair_file(&keypair_file).unwrap().into()], @@ -3489,7 +3311,7 @@ mod tests { &witness1_string, ]); assert_eq!( - parse_command(&test_pay_multiple_witnesses, &keypair_file, &mut None).unwrap(), + parse_command(&test_pay_multiple_witnesses, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::Pay(PayCommand { amount: SpendAmount::Some(50_000_000_000), @@ -3513,7 +3335,7 @@ mod tests { "2018-09-19T17:30:59", ]); assert_eq!( - parse_command(&test_send_timestamp, &keypair_file, &mut None).unwrap(), + parse_command(&test_send_timestamp, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::TimeElapsed(pubkey, pubkey, dt), signers: vec![read_keypair_file(&keypair_file).unwrap().into()], @@ -3527,7 +3349,7 @@ mod tests { "--date", "20180919T17:30:59", ]); - assert!(parse_command(&test_bad_timestamp, &keypair_file, &mut None).is_err()); + assert!(parse_command(&test_bad_timestamp, &default_signer, &mut None).is_err()); } #[test] @@ -3914,6 +3736,10 @@ mod tests { let default_keypair = Keypair::new(); let default_keypair_file = make_tmp_path("keypair_file"); write_keypair_file(&default_keypair, &default_keypair_file).unwrap(); + let default_signer = DefaultSigner { + path: default_keypair_file.clone(), + arg_name: "".to_string(), + }; //Test Transfer Subcommand, SOL let from_keypair = keypair_from_seed(&[0u8; 32]).unwrap(); @@ -3926,7 +3752,7 @@ mod tests { .clone() .get_matches_from(vec!["test", "transfer", &to_string, "42"]); assert_eq!( - parse_command(&test_transfer, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_transfer, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::Transfer { amount: SpendAmount::Some(42_000_000_000), @@ -3948,7 +3774,7 @@ mod tests { .clone() .get_matches_from(vec!["test", "transfer", &to_string, "ALL"]); assert_eq!( - parse_command(&test_transfer, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_transfer, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::Transfer { amount: SpendAmount::All, @@ -3974,7 +3800,7 @@ mod tests { "42", ]); assert_eq!( - parse_command(&test_transfer, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_transfer, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::Transfer { amount: SpendAmount::Some(42_000_000_000), @@ -4004,7 +3830,7 @@ mod tests { "--sign-only", ]); assert_eq!( - parse_command(&test_transfer, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_transfer, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::Transfer { amount: SpendAmount::Some(42_000_000_000), @@ -4039,7 +3865,7 @@ mod tests { &blockhash_string, ]); assert_eq!( - parse_command(&test_transfer, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_transfer, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::Transfer { amount: SpendAmount::Some(42_000_000_000), @@ -4078,7 +3904,7 @@ mod tests { &nonce_authority_file, ]); assert_eq!( - parse_command(&test_transfer, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_transfer, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::Transfer { amount: SpendAmount::Some(42_000_000_000), @@ -4101,54 +3927,4 @@ mod tests { } ); } - - #[test] - fn test_return_signers() { - struct BadSigner { - pubkey: Pubkey, - } - - impl BadSigner { - pub fn new(pubkey: Pubkey) -> Self { - Self { pubkey } - } - } - - impl Signer for BadSigner { - fn try_pubkey(&self) -> Result { - Ok(self.pubkey) - } - - fn try_sign_message(&self, _message: &[u8]) -> Result { - Ok(Signature::new(&[1u8; 64])) - } - } - - let mut config = CliConfig::default(); - config.output_format = OutputFormat::JsonCompact; - let present: Box = Box::new(keypair_from_seed(&[2u8; 32]).unwrap()); - let absent: Box = Box::new(NullSigner::new(&Pubkey::new(&[3u8; 32]))); - let bad: Box = Box::new(BadSigner::new(Pubkey::new(&[4u8; 32]))); - let to = Pubkey::new(&[5u8; 32]); - let nonce = Pubkey::new(&[6u8; 32]); - let from = present.pubkey(); - let fee_payer = absent.pubkey(); - let nonce_auth = bad.pubkey(); - let mut tx = Transaction::new_unsigned(Message::new_with_nonce( - vec![system_instruction::transfer(&from, &to, 42)], - Some(&fee_payer), - &nonce, - &nonce_auth, - )); - - let signers = vec![present.as_ref(), absent.as_ref(), bad.as_ref()]; - let blockhash = Hash::new(&[7u8; 32]); - tx.try_partial_sign(&signers, blockhash).unwrap(); - let res = return_signers(&tx, &config).unwrap(); - let sign_only = parse_sign_only_reply_string(&res); - assert_eq!(sign_only.blockhash, blockhash); - assert_eq!(sign_only.present_signers[0].0, present.pubkey()); - assert_eq!(sign_only.absent_signers[0], absent.pubkey()); - assert_eq!(sign_only.bad_signers[0], bad.pubkey()); - } } diff --git a/cli/src/cluster_query.rs b/cli/src/cluster_query.rs index ae351a6c99..4b036406a6 100644 --- a/cli/src/cluster_query.rs +++ b/cli/src/cluster_query.rs @@ -1,15 +1,17 @@ use crate::{ cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult}, - cli_output::*, - display::{ - format_labeled_address, new_spinner_progress_bar, println_name_value, println_transaction, - }, spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount}, }; use clap::{value_t, value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand}; use console::{style, Emoji}; use solana_clap_utils::{ - commitment::commitment_arg, input_parsers::*, input_validators::*, keypair::signer_from_path, + commitment::commitment_arg, input_parsers::*, input_validators::*, keypair::DefaultSigner, +}; +use solana_cli_output::{ + display::{ + format_labeled_address, new_spinner_progress_bar, println_name_value, println_transaction, + }, + *, }; use solana_client::{ pubsub_client::PubsubClient, @@ -332,7 +334,7 @@ pub fn parse_catchup( pub fn parse_cluster_ping( matches: &ArgMatches<'_>, - default_signer_path: &str, + default_signer: &DefaultSigner, wallet_manager: &mut Option>, ) -> Result { let lamports = value_t_or_exit!(matches, "lamports", u64); @@ -350,12 +352,7 @@ pub fn parse_cluster_ping( count, timeout, }, - signers: vec![signer_from_path( - matches, - default_signer_path, - "keypair", - wallet_manager, - )?], + signers: vec![default_signer.signer_from_path(matches, wallet_manager)?], }) } @@ -1480,12 +1477,16 @@ mod tests { let default_keypair = Keypair::new(); let (default_keypair_file, mut tmp_file) = make_tmp_file(); write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap(); + let default_signer = DefaultSigner { + path: default_keypair_file, + arg_name: String::new(), + }; let test_cluster_version = test_commands .clone() .get_matches_from(vec!["test", "cluster-date"]); assert_eq!( - parse_command(&test_cluster_version, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_cluster_version, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::ClusterDate, signers: vec![], @@ -1496,7 +1497,7 @@ mod tests { .clone() .get_matches_from(vec!["test", "cluster-version"]); assert_eq!( - parse_command(&test_cluster_version, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_cluster_version, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::ClusterVersion, signers: vec![], @@ -1505,7 +1506,7 @@ mod tests { let test_fees = test_commands.clone().get_matches_from(vec!["test", "fees"]); assert_eq!( - parse_command(&test_fees, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_fees, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::Fees, signers: vec![], @@ -1518,7 +1519,7 @@ mod tests { .clone() .get_matches_from(vec!["test", "block-time", &slot.to_string()]); assert_eq!( - parse_command(&test_get_block_time, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_get_block_time, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::GetBlockTime { slot: Some(slot) }, signers: vec![], @@ -1529,7 +1530,7 @@ mod tests { .clone() .get_matches_from(vec!["test", "epoch"]); assert_eq!( - parse_command(&test_get_epoch, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_get_epoch, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::GetEpoch, signers: vec![], @@ -1540,7 +1541,7 @@ mod tests { .clone() .get_matches_from(vec!["test", "epoch-info"]); assert_eq!( - parse_command(&test_get_epoch_info, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_get_epoch_info, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::GetEpochInfo, signers: vec![], @@ -1551,7 +1552,7 @@ mod tests { .clone() .get_matches_from(vec!["test", "genesis-hash"]); assert_eq!( - parse_command(&test_get_genesis_hash, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_get_genesis_hash, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::GetGenesisHash, signers: vec![], @@ -1560,7 +1561,7 @@ mod tests { let test_get_slot = test_commands.clone().get_matches_from(vec!["test", "slot"]); assert_eq!( - parse_command(&test_get_slot, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_get_slot, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::GetSlot, signers: vec![], @@ -1571,7 +1572,7 @@ mod tests { .clone() .get_matches_from(vec!["test", "total-supply"]); assert_eq!( - parse_command(&test_total_supply, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_total_supply, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::TotalSupply, signers: vec![], @@ -1582,7 +1583,7 @@ mod tests { .clone() .get_matches_from(vec!["test", "transaction-count"]); assert_eq!( - parse_command(&test_transaction_count, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_transaction_count, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::GetTransactionCount, signers: vec![], @@ -1602,7 +1603,7 @@ mod tests { "max", ]); assert_eq!( - parse_command(&test_ping, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_ping, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::Ping { lamports: 1, diff --git a/cli/src/lib.rs b/cli/src/lib.rs index f9daaf053a..0dfbbe841a 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -18,16 +18,12 @@ macro_rules! pubkey { }; } -#[macro_use] extern crate serde_derive; pub mod checks; pub mod cli; -pub mod cli_output; pub mod cluster_query; -pub mod display; pub mod nonce; -pub mod offline; pub mod spend_utils; pub mod stake; pub mod test_utils; diff --git a/cli/src/main.rs b/cli/src/main.rs index 6014ee7e27..2420cc3ca9 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -5,22 +5,37 @@ use clap::{ use console::style; use solana_clap_utils::{ - commitment::COMMITMENT_ARG, input_parsers::commitment_of, input_validators::is_url, - keypair::SKIP_SEED_PHRASE_VALIDATION_ARG, DisplayError, + commitment::COMMITMENT_ARG, + input_parsers::commitment_of, + input_validators::is_url, + keypair::{CliSigners, DefaultSigner, SKIP_SEED_PHRASE_VALIDATION_ARG}, + DisplayError, }; -use solana_cli::{ - cli::{ - app, parse_command, process_command, CliCommandInfo, CliConfig, CliSigners, - DEFAULT_RPC_TIMEOUT_SECONDS, - }, - cli_output::OutputFormat, - display::{println_name_value, println_name_value_or}, +use solana_cli::cli::{ + app, parse_command, process_command, CliCommandInfo, CliConfig, SettingType, + DEFAULT_RPC_TIMEOUT_SECONDS, }; use solana_cli_config::{Config, CONFIG_FILE}; +use solana_cli_output::{display::println_name_value, OutputFormat}; use solana_client::rpc_config::RpcSendTransactionConfig; use solana_remote_wallet::remote_wallet::RemoteWalletManager; use std::{collections::HashMap, error, path::PathBuf, sync::Arc, time::Duration}; +pub fn println_name_value_or(name: &str, value: &str, setting_type: SettingType) { + let description = match setting_type { + SettingType::Explicit => "", + SettingType::Computed => "(computed)", + SettingType::SystemDefault => "(default)", + }; + + println!( + "{} {} {}", + style(name).bold(), + style(value), + style(description).italic(), + ); +} + fn parse_settings(matches: &ArgMatches<'_>) -> Result> { let parse_args = match matches.subcommand() { ("config", Some(matches)) => { @@ -139,13 +154,19 @@ pub fn parse_args<'a>( matches.value_of("json_rpc_url").unwrap_or(""), &config.json_rpc_url, ); + let default_signer_arg_name = "keypair".to_string(); let (_, default_signer_path) = CliConfig::compute_keypair_path_setting( - matches.value_of("keypair").unwrap_or(""), + matches.value_of(&default_signer_arg_name).unwrap_or(""), &config.keypair_path, ); + let default_signer = DefaultSigner { + arg_name: default_signer_arg_name, + path: default_signer_path.clone(), + }; + let CliCommandInfo { command, signers } = - parse_command(&matches, &default_signer_path, &mut wallet_manager)?; + parse_command(&matches, &default_signer, &mut wallet_manager)?; let output_format = matches .value_of("output_format") diff --git a/cli/src/nonce.rs b/cli/src/nonce.rs index 6703d79457..7e04d99df9 100644 --- a/cli/src/nonce.rs +++ b/cli/src/nonce.rs @@ -1,29 +1,26 @@ use crate::{ checks::{check_account_for_fee_with_commitment, check_unique_pubkeys}, cli::{ - generate_unique_signers, log_instruction_custom_error, CliCommand, CliCommandInfo, - CliConfig, CliError, ProcessResult, SignerIndex, + log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError, + ProcessResult, }, - cli_output::CliNonceAccount, spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount}, }; use clap::{App, Arg, ArgMatches, SubCommand}; use solana_clap_utils::{ - input_parsers::*, input_validators::*, offline::BLOCKHASH_ARG, ArgConstant, + input_parsers::*, + input_validators::*, + keypair::{DefaultSigner, SignerIndex}, + nonce::*, }; -use solana_client::rpc_client::RpcClient; +use solana_cli_output::CliNonceAccount; +use solana_client::{nonce_utils::*, rpc_client::RpcClient}; use solana_remote_wallet::remote_wallet::RemoteWalletManager; use solana_sdk::{ account::Account, - account_utils::StateMut, - commitment_config::CommitmentConfig, hash::Hash, message::Message, - nonce::{ - self, - state::{Data, Versions}, - State, - }, + nonce::{self, State}, pubkey::Pubkey, system_instruction::{ advance_nonce_account, authorize_nonce_account, create_nonce_account, @@ -33,64 +30,11 @@ use solana_sdk::{ transaction::Transaction, }; use std::sync::Arc; -use thiserror::Error; - -#[derive(Debug, Error, PartialEq)] -pub enum CliNonceError { - #[error("invalid account owner")] - InvalidAccountOwner, - #[error("invalid account data")] - InvalidAccountData, - #[error("unexpected account data size")] - UnexpectedDataSize, - #[error("query hash does not match stored hash")] - InvalidHash, - #[error("query authority does not match account authority")] - InvalidAuthority, - #[error("invalid state for requested operation")] - InvalidStateForOperation, - #[error("client error: {0}")] - Client(String), -} - -pub const NONCE_ARG: ArgConstant<'static> = ArgConstant { - name: "nonce", - long: "nonce", - help: "Provide the nonce account to use when creating a nonced \n\ - transaction. Nonced transactions are useful when a transaction \n\ - requires a lengthy signing process. Learn more about nonced \n\ - transactions at https://docs.solana.com/offline-signing/durable-nonce", -}; - -pub const NONCE_AUTHORITY_ARG: ArgConstant<'static> = ArgConstant { - name: "nonce_authority", - long: "nonce-authority", - help: "Provide the nonce authority keypair to use when signing a nonced transaction", -}; pub trait NonceSubCommands { fn nonce_subcommands(self) -> Self; } -pub fn nonce_arg<'a, 'b>() -> Arg<'a, 'b> { - Arg::with_name(NONCE_ARG.name) - .long(NONCE_ARG.long) - .takes_value(true) - .value_name("PUBKEY") - .requires(BLOCKHASH_ARG.name) - .validator(is_valid_pubkey) - .help(NONCE_ARG.help) -} - -pub fn nonce_authority_arg<'a, 'b>() -> Arg<'a, 'b> { - Arg::with_name(NONCE_AUTHORITY_ARG.name) - .long(NONCE_AUTHORITY_ARG.long) - .takes_value(true) - .value_name("KEYPAIR") - .validator(is_valid_signer) - .help(NONCE_AUTHORITY_ARG.help) -} - impl NonceSubCommands for App<'_, '_> { fn nonce_subcommands(self) -> Self { self.subcommand( @@ -220,64 +164,9 @@ impl NonceSubCommands for App<'_, '_> { } } -pub fn get_account( - rpc_client: &RpcClient, - nonce_pubkey: &Pubkey, -) -> Result { - get_account_with_commitment(rpc_client, nonce_pubkey, CommitmentConfig::default()) -} - -pub fn get_account_with_commitment( - rpc_client: &RpcClient, - nonce_pubkey: &Pubkey, - commitment: CommitmentConfig, -) -> Result { - rpc_client - .get_account_with_commitment(nonce_pubkey, commitment) - .map_err(|e| CliNonceError::Client(format!("{}", e))) - .and_then(|result| { - result.value.ok_or_else(|| { - CliNonceError::Client(format!("AccountNotFound: pubkey={}", nonce_pubkey)) - }) - }) - .and_then(|a| match account_identity_ok(&a) { - Ok(()) => Ok(a), - Err(e) => Err(e), - }) -} - -pub fn account_identity_ok(account: &Account) -> Result<(), CliNonceError> { - if account.owner != system_program::id() { - Err(CliNonceError::InvalidAccountOwner) - } else if account.data.is_empty() { - Err(CliNonceError::UnexpectedDataSize) - } else { - Ok(()) - } -} - -pub fn state_from_account(account: &Account) -> Result { - account_identity_ok(account)?; - StateMut::::state(account) - .map_err(|_| CliNonceError::InvalidAccountData) - .map(|v| v.convert_to_current()) -} - -pub fn data_from_account(account: &Account) -> Result { - account_identity_ok(account)?; - state_from_account(account).and_then(|ref s| data_from_state(s).map(|d| d.clone())) -} - -pub fn data_from_state(state: &State) -> Result<&Data, CliNonceError> { - match state { - State::Uninitialized => Err(CliNonceError::InvalidStateForOperation), - State::Initialized(data) => Ok(data), - } -} - pub fn parse_authorize_nonce_account( matches: &ArgMatches<'_>, - default_signer_path: &str, + default_signer: &DefaultSigner, wallet_manager: &mut Option>, ) -> Result { let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap(); @@ -286,10 +175,9 @@ pub fn parse_authorize_nonce_account( signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?; let payer_provided = None; - let signer_info = generate_unique_signers( + let signer_info = default_signer.generate_unique_signers( vec![payer_provided, nonce_authority], matches, - default_signer_path, wallet_manager, )?; @@ -305,7 +193,7 @@ pub fn parse_authorize_nonce_account( pub fn parse_nonce_create_account( matches: &ArgMatches<'_>, - default_signer_path: &str, + default_signer: &DefaultSigner, wallet_manager: &mut Option>, ) -> Result { let (nonce_account, nonce_account_pubkey) = @@ -315,10 +203,9 @@ pub fn parse_nonce_create_account( let nonce_authority = pubkey_of_signer(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?; let payer_provided = None; - let signer_info = generate_unique_signers( + let signer_info = default_signer.generate_unique_signers( vec![payer_provided, nonce_account], matches, - default_signer_path, wallet_manager, )?; @@ -348,7 +235,7 @@ pub fn parse_get_nonce( pub fn parse_new_nonce( matches: &ArgMatches<'_>, - default_signer_path: &str, + default_signer: &DefaultSigner, wallet_manager: &mut Option>, ) -> Result { let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap(); @@ -356,10 +243,9 @@ pub fn parse_new_nonce( signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?; let payer_provided = None; - let signer_info = generate_unique_signers( + let signer_info = default_signer.generate_unique_signers( vec![payer_provided, nonce_authority], matches, - default_signer_path, wallet_manager, )?; @@ -391,7 +277,7 @@ pub fn parse_show_nonce_account( pub fn parse_withdraw_from_nonce_account( matches: &ArgMatches<'_>, - default_signer_path: &str, + default_signer: &DefaultSigner, wallet_manager: &mut Option>, ) -> Result { let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap(); @@ -402,10 +288,9 @@ pub fn parse_withdraw_from_nonce_account( signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?; let payer_provided = None; - let signer_info = generate_unique_signers( + let signer_info = default_signer.generate_unique_signers( vec![payer_provided, nonce_authority], matches, - default_signer_path, wallet_manager, )?; @@ -429,14 +314,14 @@ pub fn check_nonce_account( match state_from_account(nonce_account)? { State::Initialized(ref data) => { if &data.blockhash != nonce_hash { - Err(CliNonceError::InvalidHash.into()) + Err(Error::InvalidHash.into()) } else if nonce_authority != &data.authority { - Err(CliNonceError::InvalidAuthority.into()) + Err(Error::InvalidAuthority.into()) } else { Ok(()) } } - State::Uninitialized => Err(CliNonceError::InvalidStateForOperation.into()), + State::Uninitialized => Err(Error::InvalidStateForOperation.into()), } } @@ -691,9 +576,10 @@ mod tests { use crate::cli::{app, parse_command}; use solana_sdk::{ account::Account, + account_utils::StateMut, fee_calculator::FeeCalculator, hash::hash, - nonce::{self, State}, + nonce::{self, state::Versions, State}, signature::{read_keypair_file, write_keypair, Keypair, Signer}, system_program, }; @@ -710,6 +596,10 @@ mod tests { let default_keypair = Keypair::new(); let (default_keypair_file, mut tmp_file) = make_tmp_file(); write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap(); + let default_signer = DefaultSigner { + path: default_keypair_file.clone(), + arg_name: String::new(), + }; let (keypair_file, mut tmp_file) = make_tmp_file(); let nonce_account_keypair = Keypair::new(); write_keypair(&nonce_account_keypair, tmp_file.as_file_mut()).unwrap(); @@ -728,12 +618,7 @@ mod tests { &Pubkey::default().to_string(), ]); assert_eq!( - parse_command( - &test_authorize_nonce_account, - &default_keypair_file, - &mut None - ) - .unwrap(), + parse_command(&test_authorize_nonce_account, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::AuthorizeNonceAccount { nonce_account: nonce_account_pubkey, @@ -754,12 +639,7 @@ mod tests { &authority_keypair_file, ]); assert_eq!( - parse_command( - &test_authorize_nonce_account, - &default_keypair_file, - &mut None - ) - .unwrap(), + parse_command(&test_authorize_nonce_account, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::AuthorizeNonceAccount { nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(), @@ -781,7 +661,7 @@ mod tests { "50", ]); assert_eq!( - parse_command(&test_create_nonce_account, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_create_nonce_account, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::CreateNonceAccount { nonce_account: 1, @@ -806,7 +686,7 @@ mod tests { &authority_keypair_file, ]); assert_eq!( - parse_command(&test_create_nonce_account, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_create_nonce_account, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::CreateNonceAccount { nonce_account: 1, @@ -828,7 +708,7 @@ mod tests { &nonce_account_string, ]); assert_eq!( - parse_command(&test_get_nonce, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_get_nonce, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::GetNonce(nonce_account_keypair.pubkey()), signers: vec![], @@ -842,7 +722,7 @@ mod tests { .get_matches_from(vec!["test", "new-nonce", &keypair_file]); let nonce_account = read_keypair_file(&keypair_file).unwrap(); assert_eq!( - parse_command(&test_new_nonce, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_new_nonce, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::NewNonce { nonce_account: nonce_account.pubkey(), @@ -862,7 +742,7 @@ mod tests { ]); let nonce_account = read_keypair_file(&keypair_file).unwrap(); assert_eq!( - parse_command(&test_new_nonce, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_new_nonce, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::NewNonce { nonce_account: nonce_account.pubkey(), @@ -882,7 +762,7 @@ mod tests { &nonce_account_string, ]); assert_eq!( - parse_command(&test_show_nonce_account, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_show_nonce_account, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::ShowNonceAccount { nonce_account_pubkey: nonce_account_keypair.pubkey(), @@ -903,7 +783,7 @@ mod tests { assert_eq!( parse_command( &test_withdraw_from_nonce_account, - &default_keypair_file, + &default_signer, &mut None ) .unwrap(), @@ -931,7 +811,7 @@ mod tests { assert_eq!( parse_command( &test_withdraw_from_nonce_account, - &default_keypair_file, + &default_signer, &mut None ) .unwrap(), @@ -966,14 +846,14 @@ mod tests { if let CliError::InvalidNonce(err) = check_nonce_account(&invalid_owner.unwrap(), &nonce_pubkey, &blockhash).unwrap_err() { - assert_eq!(err, CliNonceError::InvalidAccountOwner,); + assert_eq!(err, Error::InvalidAccountOwner,); } let invalid_data = Account::new_data(1, &"invalid", &system_program::ID); if let CliError::InvalidNonce(err) = check_nonce_account(&invalid_data.unwrap(), &nonce_pubkey, &blockhash).unwrap_err() { - assert_eq!(err, CliNonceError::InvalidAccountData,); + assert_eq!(err, Error::InvalidAccountData,); } let data = Versions::new_current(State::Initialized(nonce::state::Data { @@ -985,7 +865,7 @@ mod tests { if let CliError::InvalidNonce(err) = check_nonce_account(&invalid_hash.unwrap(), &nonce_pubkey, &blockhash).unwrap_err() { - assert_eq!(err, CliNonceError::InvalidHash,); + assert_eq!(err, Error::InvalidHash,); } let data = Versions::new_current(State::Initialized(nonce::state::Data { @@ -997,7 +877,7 @@ mod tests { if let CliError::InvalidNonce(err) = check_nonce_account(&invalid_authority.unwrap(), &nonce_pubkey, &blockhash).unwrap_err() { - assert_eq!(err, CliNonceError::InvalidAuthority,); + assert_eq!(err, Error::InvalidAuthority,); } let data = Versions::new_current(State::Uninitialized); @@ -1005,7 +885,7 @@ mod tests { if let CliError::InvalidNonce(err) = check_nonce_account(&invalid_state.unwrap(), &nonce_pubkey, &blockhash).unwrap_err() { - assert_eq!(err, CliNonceError::InvalidStateForOperation,); + assert_eq!(err, Error::InvalidStateForOperation,); } } @@ -1017,14 +897,14 @@ mod tests { let system_account = Account::new(1, 0, &system_program::id()); assert_eq!( account_identity_ok(&system_account), - Err(CliNonceError::UnexpectedDataSize), + Err(Error::UnexpectedDataSize), ); let other_program = Pubkey::new(&[1u8; 32]); let other_account_no_data = Account::new(1, 0, &other_program); assert_eq!( account_identity_ok(&other_account_no_data), - Err(CliNonceError::InvalidAccountOwner), + Err(Error::InvalidAccountOwner), ); } @@ -1049,7 +929,7 @@ mod tests { let wrong_data_size_account = Account::new(1, 1, &system_program::id()); assert_eq!( state_from_account(&wrong_data_size_account), - Err(CliNonceError::InvalidAccountData), + Err(Error::InvalidAccountData), ); } @@ -1059,11 +939,11 @@ mod tests { let state = state_from_account(&nonce_account).unwrap(); assert_eq!( data_from_state(&state), - Err(CliNonceError::InvalidStateForOperation) + Err(Error::InvalidStateForOperation) ); assert_eq!( data_from_account(&nonce_account), - Err(CliNonceError::InvalidStateForOperation) + Err(Error::InvalidStateForOperation) ); let data = nonce::state::Data { diff --git a/cli/src/offline/mod.rs b/cli/src/offline/mod.rs deleted file mode 100644 index 3cef004cdc..0000000000 --- a/cli/src/offline/mod.rs +++ /dev/null @@ -1,129 +0,0 @@ -pub mod blockhash_query; - -use crate::nonce; -use clap::{App, Arg, ArgMatches}; -use serde_json::Value; -use solana_clap_utils::{ - input_parsers::{pubkey_of, value_of}, - input_validators::{is_hash, is_pubkey_sig}, - keypair::presigner_from_pubkey_sigs, - offline::{BLOCKHASH_ARG, SIGNER_ARG, SIGN_ONLY_ARG}, -}; -use solana_client::rpc_client::RpcClient; -use solana_sdk::{ - fee_calculator::FeeCalculator, - hash::Hash, - pubkey::Pubkey, - signature::{Presigner, Signature}, -}; -use std::str::FromStr; - -fn blockhash_arg<'a, 'b>() -> Arg<'a, 'b> { - Arg::with_name(BLOCKHASH_ARG.name) - .long(BLOCKHASH_ARG.long) - .takes_value(true) - .value_name("BLOCKHASH") - .validator(is_hash) - .help(BLOCKHASH_ARG.help) -} - -fn sign_only_arg<'a, 'b>() -> Arg<'a, 'b> { - Arg::with_name(SIGN_ONLY_ARG.name) - .long(SIGN_ONLY_ARG.long) - .takes_value(false) - .requires(BLOCKHASH_ARG.name) - .help(SIGN_ONLY_ARG.help) -} - -fn signer_arg<'a, 'b>() -> Arg<'a, 'b> { - Arg::with_name(SIGNER_ARG.name) - .long(SIGNER_ARG.long) - .takes_value(true) - .value_name("PUBKEY=SIGNATURE") - .validator(is_pubkey_sig) - .requires(BLOCKHASH_ARG.name) - .multiple(true) - .help(SIGNER_ARG.help) -} - -pub trait OfflineArgs { - fn offline_args(self) -> Self; -} - -impl OfflineArgs for App<'_, '_> { - fn offline_args(self) -> Self { - self.arg(blockhash_arg()) - .arg(sign_only_arg()) - .arg(signer_arg()) - } -} - -pub struct SignOnly { - pub blockhash: Hash, - pub present_signers: Vec<(Pubkey, Signature)>, - pub absent_signers: Vec, - pub bad_signers: Vec, -} - -impl SignOnly { - pub fn has_all_signers(&self) -> bool { - self.absent_signers.is_empty() && self.bad_signers.is_empty() - } - - pub fn presigner_of(&self, pubkey: &Pubkey) -> Option { - presigner_from_pubkey_sigs(pubkey, &self.present_signers) - } -} - -pub fn parse_sign_only_reply_string(reply: &str) -> SignOnly { - let object: Value = serde_json::from_str(&reply).unwrap(); - let blockhash_str = object.get("blockhash").unwrap().as_str().unwrap(); - let blockhash = blockhash_str.parse::().unwrap(); - let mut present_signers: Vec<(Pubkey, Signature)> = Vec::new(); - let signer_strings = object.get("signers"); - if let Some(sig_strings) = signer_strings { - present_signers = sig_strings - .as_array() - .unwrap() - .iter() - .map(|signer_string| { - let mut signer = signer_string.as_str().unwrap().split('='); - let key = Pubkey::from_str(signer.next().unwrap()).unwrap(); - let sig = Signature::from_str(signer.next().unwrap()).unwrap(); - (key, sig) - }) - .collect(); - } - let mut absent_signers: Vec = Vec::new(); - let signer_strings = object.get("absent"); - if let Some(sig_strings) = signer_strings { - absent_signers = sig_strings - .as_array() - .unwrap() - .iter() - .map(|val| { - let s = val.as_str().unwrap(); - Pubkey::from_str(s).unwrap() - }) - .collect(); - } - let mut bad_signers: Vec = Vec::new(); - let signer_strings = object.get("badSig"); - if let Some(sig_strings) = signer_strings { - bad_signers = sig_strings - .as_array() - .unwrap() - .iter() - .map(|val| { - let s = val.as_str().unwrap(); - Pubkey::from_str(s).unwrap() - }) - .collect(); - } - SignOnly { - blockhash, - present_signers, - absent_signers, - bad_signers, - } -} diff --git a/cli/src/stake.rs b/cli/src/stake.rs index dd66f2faba..e39474133b 100644 --- a/cli/src/stake.rs +++ b/cli/src/stake.rs @@ -1,18 +1,29 @@ use crate::{ checks::{check_account_for_fee_with_commitment, check_unique_pubkeys}, cli::{ - fee_payer_arg, generate_unique_signers, log_instruction_custom_error, nonce_authority_arg, - return_signers, CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult, - SignerIndex, FEE_PAYER_ARG, + log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError, + ProcessResult, }, - cli_output::{CliStakeHistory, CliStakeHistoryEntry, CliStakeState, CliStakeType}, - nonce::{self, check_nonce_account, nonce_arg, NONCE_ARG, NONCE_AUTHORITY_ARG}, - offline::{blockhash_query::BlockhashQuery, *}, + nonce::check_nonce_account, spend_utils::{resolve_spend_tx_and_check_account_balances, SpendAmount}, }; use clap::{App, Arg, ArgGroup, ArgMatches, SubCommand}; -use solana_clap_utils::{input_parsers::*, input_validators::*, offline::*, ArgConstant}; -use solana_client::{rpc_client::RpcClient, rpc_request::DELINQUENT_VALIDATOR_SLOT_DISTANCE}; +use solana_clap_utils::{ + fee_payer::{fee_payer_arg, FEE_PAYER_ARG}, + input_parsers::*, + input_validators::*, + keypair::{DefaultSigner, SignerIndex}, + nonce::*, + offline::*, + ArgConstant, +}; +use solana_cli_output::{ + return_signers, CliStakeHistory, CliStakeHistoryEntry, CliStakeState, CliStakeType, +}; +use solana_client::{ + blockhash_query::BlockhashQuery, nonce_utils, rpc_client::RpcClient, + rpc_request::DELINQUENT_VALIDATOR_SLOT_DISTANCE, +}; use solana_remote_wallet::remote_wallet::RemoteWalletManager; use solana_sdk::{ account_utils::StateMut, @@ -144,8 +155,7 @@ impl StakeSubCommands for App<'_, '_> { .help("Source account of funds [default: cli config keypair]"), ) .offline_args() - .arg(nonce_arg()) - .arg(nonce_authority_arg()) + .nonce_args() .arg(fee_payer_arg()) ) .subcommand( @@ -174,8 +184,7 @@ impl StakeSubCommands for App<'_, '_> { ) .arg(stake_authority_arg()) .offline_args() - .arg(nonce_arg()) - .arg(nonce_authority_arg()) + .nonce_args() .arg(fee_payer_arg()) ) .subcommand( @@ -205,8 +214,7 @@ impl StakeSubCommands for App<'_, '_> { .arg(stake_authority_arg()) .arg(withdraw_authority_arg()) .offline_args() - .arg(nonce_arg()) - .arg(nonce_authority_arg()) + .nonce_args() .arg(fee_payer_arg()) ) .subcommand( @@ -221,8 +229,7 @@ impl StakeSubCommands for App<'_, '_> { ) .arg(stake_authority_arg()) .offline_args() - .arg(nonce_arg()) - .arg(nonce_authority_arg()) + .nonce_args() .arg(fee_payer_arg()) ) .subcommand( @@ -262,8 +269,7 @@ impl StakeSubCommands for App<'_, '_> { ) .arg(stake_authority_arg()) .offline_args() - .arg(nonce_arg()) - .arg(nonce_authority_arg()) + .nonce_args() .arg(fee_payer_arg()) ) .subcommand( @@ -285,8 +291,7 @@ impl StakeSubCommands for App<'_, '_> { ) .arg(stake_authority_arg()) .offline_args() - .arg(nonce_arg()) - .arg(nonce_authority_arg()) + .nonce_args() .arg(fee_payer_arg()) ) .subcommand( @@ -317,8 +322,7 @@ impl StakeSubCommands for App<'_, '_> { ) .arg(withdraw_authority_arg()) .offline_args() - .arg(nonce_arg()) - .arg(nonce_authority_arg()) + .nonce_args() .arg(fee_payer_arg()) .arg( Arg::with_name("custodian") @@ -373,8 +377,7 @@ impl StakeSubCommands for App<'_, '_> { .help("Keypair of the existing custodian [default: cli config pubkey]") ) .offline_args() - .arg(nonce_arg()) - .arg(nonce_authority_arg()) + .nonce_args() .arg(fee_payer_arg()) ) .subcommand( @@ -411,7 +414,7 @@ impl StakeSubCommands for App<'_, '_> { pub fn parse_stake_create_account( matches: &ArgMatches<'_>, - default_signer_path: &str, + default_signer: &DefaultSigner, wallet_manager: &mut Option>, ) -> Result { let seed = matches.value_of("seed").map(|s| s.to_string()); @@ -436,7 +439,7 @@ pub fn parse_stake_create_account( bulk_signers.push(nonce_authority); } let signer_info = - generate_unique_signers(bulk_signers, matches, default_signer_path, wallet_manager)?; + default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?; Ok(CliCommandInfo { command: CliCommand::CreateStakeAccount { @@ -463,7 +466,7 @@ pub fn parse_stake_create_account( pub fn parse_stake_delegate_stake( matches: &ArgMatches<'_>, - default_signer_path: &str, + default_signer: &DefaultSigner, wallet_manager: &mut Option>, ) -> Result { let stake_account_pubkey = @@ -485,7 +488,7 @@ pub fn parse_stake_delegate_stake( bulk_signers.push(nonce_authority); } let signer_info = - generate_unique_signers(bulk_signers, matches, default_signer_path, wallet_manager)?; + default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?; Ok(CliCommandInfo { command: CliCommand::DelegateStake { @@ -505,7 +508,7 @@ pub fn parse_stake_delegate_stake( pub fn parse_stake_authorize( matches: &ArgMatches<'_>, - default_signer_path: &str, + default_signer: &DefaultSigner, wallet_manager: &mut Option>, ) -> Result { let stake_account_pubkey = @@ -557,7 +560,7 @@ pub fn parse_stake_authorize( bulk_signers.push(nonce_authority); } let signer_info = - generate_unique_signers(bulk_signers, matches, default_signer_path, wallet_manager)?; + default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?; let new_authorizations = new_authorizations .into_iter() @@ -588,7 +591,7 @@ pub fn parse_stake_authorize( pub fn parse_split_stake( matches: &ArgMatches<'_>, - default_signer_path: &str, + default_signer: &DefaultSigner, wallet_manager: &mut Option>, ) -> Result { let stake_account_pubkey = @@ -612,7 +615,7 @@ pub fn parse_split_stake( bulk_signers.push(nonce_authority); } let signer_info = - generate_unique_signers(bulk_signers, matches, default_signer_path, wallet_manager)?; + default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?; Ok(CliCommandInfo { command: CliCommand::SplitStake { @@ -633,7 +636,7 @@ pub fn parse_split_stake( pub fn parse_merge_stake( matches: &ArgMatches<'_>, - default_signer_path: &str, + default_signer: &DefaultSigner, wallet_manager: &mut Option>, ) -> Result { let stake_account_pubkey = @@ -655,7 +658,7 @@ pub fn parse_merge_stake( bulk_signers.push(nonce_authority); } let signer_info = - generate_unique_signers(bulk_signers, matches, default_signer_path, wallet_manager)?; + default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?; Ok(CliCommandInfo { command: CliCommand::MergeStake { @@ -674,7 +677,7 @@ pub fn parse_merge_stake( pub fn parse_stake_deactivate_stake( matches: &ArgMatches<'_>, - default_signer_path: &str, + default_signer: &DefaultSigner, wallet_manager: &mut Option>, ) -> Result { let stake_account_pubkey = @@ -693,7 +696,7 @@ pub fn parse_stake_deactivate_stake( bulk_signers.push(nonce_authority); } let signer_info = - generate_unique_signers(bulk_signers, matches, default_signer_path, wallet_manager)?; + default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?; Ok(CliCommandInfo { command: CliCommand::DeactivateStake { @@ -711,7 +714,7 @@ pub fn parse_stake_deactivate_stake( pub fn parse_stake_withdraw_stake( matches: &ArgMatches<'_>, - default_signer_path: &str, + default_signer: &DefaultSigner, wallet_manager: &mut Option>, ) -> Result { let stake_account_pubkey = @@ -737,7 +740,7 @@ pub fn parse_stake_withdraw_stake( bulk_signers.push(custodian); } let signer_info = - generate_unique_signers(bulk_signers, matches, default_signer_path, wallet_manager)?; + default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?; Ok(CliCommandInfo { command: CliCommand::WithdrawStake { @@ -758,7 +761,7 @@ pub fn parse_stake_withdraw_stake( pub fn parse_stake_set_lockup( matches: &ArgMatches<'_>, - default_signer_path: &str, + default_signer: &DefaultSigner, wallet_manager: &mut Option>, ) -> Result { let stake_account_pubkey = @@ -781,7 +784,7 @@ pub fn parse_stake_set_lockup( bulk_signers.push(nonce_authority); } let signer_info = - generate_unique_signers(bulk_signers, matches, default_signer_path, wallet_manager)?; + default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?; Ok(CliCommandInfo { command: CliCommand::StakeSetLockup { @@ -934,8 +937,11 @@ pub fn process_create_stake_account( } if let Some(nonce_account) = &nonce_account { - let nonce_account = - nonce::get_account_with_commitment(rpc_client, nonce_account, config.commitment)?; + let nonce_account = nonce_utils::get_account_with_commitment( + rpc_client, + nonce_account, + config.commitment, + )?; check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?; } } @@ -943,7 +949,7 @@ pub fn process_create_stake_account( let mut tx = Transaction::new_unsigned(message); if sign_only { tx.try_partial_sign(&config.signers, recent_blockhash)?; - return_signers(&tx, &config) + return_signers(&tx, &config.output_format) } else { tx.try_sign(&config.signers, recent_blockhash)?; let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config( @@ -1002,12 +1008,15 @@ pub fn process_stake_authorize( if sign_only { tx.try_partial_sign(&config.signers, recent_blockhash)?; - return_signers(&tx, &config) + return_signers(&tx, &config.output_format) } else { tx.try_sign(&config.signers, recent_blockhash)?; if let Some(nonce_account) = &nonce_account { - let nonce_account = - nonce::get_account_with_commitment(rpc_client, nonce_account, config.commitment)?; + let nonce_account = nonce_utils::get_account_with_commitment( + rpc_client, + nonce_account, + config.commitment, + )?; check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?; } check_account_for_fee_with_commitment( @@ -1062,12 +1071,15 @@ pub fn process_deactivate_stake_account( if sign_only { tx.try_partial_sign(&config.signers, recent_blockhash)?; - return_signers(&tx, &config) + return_signers(&tx, &config.output_format) } else { tx.try_sign(&config.signers, recent_blockhash)?; if let Some(nonce_account) = &nonce_account { - let nonce_account = - nonce::get_account_with_commitment(rpc_client, nonce_account, config.commitment)?; + let nonce_account = nonce_utils::get_account_with_commitment( + rpc_client, + nonce_account, + config.commitment, + )?; check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?; } check_account_for_fee_with_commitment( @@ -1131,12 +1143,15 @@ pub fn process_withdraw_stake( if sign_only { tx.try_partial_sign(&config.signers, recent_blockhash)?; - return_signers(&tx, &config) + return_signers(&tx, &config.output_format) } else { tx.try_sign(&config.signers, recent_blockhash)?; if let Some(nonce_account) = &nonce_account { - let nonce_account = - nonce::get_account_with_commitment(rpc_client, nonce_account, config.commitment)?; + let nonce_account = nonce_utils::get_account_with_commitment( + rpc_client, + nonce_account, + config.commitment, + )?; check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?; } check_account_for_fee_with_commitment( @@ -1271,12 +1286,15 @@ pub fn process_split_stake( if sign_only { tx.try_partial_sign(&config.signers, recent_blockhash)?; - return_signers(&tx, &config) + return_signers(&tx, &config.output_format) } else { tx.try_sign(&config.signers, recent_blockhash)?; if let Some(nonce_account) = &nonce_account { - let nonce_account = - nonce::get_account_with_commitment(rpc_client, nonce_account, config.commitment)?; + let nonce_account = nonce_utils::get_account_with_commitment( + rpc_client, + nonce_account, + config.commitment, + )?; check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?; } check_account_for_fee_with_commitment( @@ -1370,12 +1388,15 @@ pub fn process_merge_stake( if sign_only { tx.try_partial_sign(&config.signers, recent_blockhash)?; - return_signers(&tx, &config) + return_signers(&tx, &config.output_format) } else { tx.try_sign(&config.signers, recent_blockhash)?; if let Some(nonce_account) = &nonce_account { - let nonce_account = - nonce::get_account_with_commitment(rpc_client, nonce_account, config.commitment)?; + let nonce_account = nonce_utils::get_account_with_commitment( + rpc_client, + nonce_account, + config.commitment, + )?; check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?; } check_account_for_fee_with_commitment( @@ -1433,12 +1454,15 @@ pub fn process_stake_set_lockup( if sign_only { tx.try_partial_sign(&config.signers, recent_blockhash)?; - return_signers(&tx, &config) + return_signers(&tx, &config.output_format) } else { tx.try_sign(&config.signers, recent_blockhash)?; if let Some(nonce_account) = &nonce_account { - let nonce_account = - nonce::get_account_with_commitment(rpc_client, nonce_account, config.commitment)?; + let nonce_account = nonce_utils::get_account_with_commitment( + rpc_client, + nonce_account, + config.commitment, + )?; check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?; } check_account_for_fee_with_commitment( @@ -1718,12 +1742,15 @@ pub fn process_delegate_stake( if sign_only { tx.try_partial_sign(&config.signers, recent_blockhash)?; - return_signers(&tx, &config) + return_signers(&tx, &config.output_format) } else { tx.try_sign(&config.signers, recent_blockhash)?; if let Some(nonce_account) = &nonce_account { - let nonce_account = - nonce::get_account_with_commitment(rpc_client, nonce_account, config.commitment)?; + let nonce_account = nonce_utils::get_account_with_commitment( + rpc_client, + nonce_account, + config.commitment, + )?; check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?; } check_account_for_fee_with_commitment( @@ -1746,6 +1773,7 @@ pub fn process_delegate_stake( mod tests { use super::*; use crate::cli::{app, parse_command}; + use solana_client::blockhash_query; use solana_sdk::{ hash::Hash, signature::{ @@ -1766,6 +1794,10 @@ mod tests { let default_keypair = Keypair::new(); let (default_keypair_file, mut tmp_file) = make_tmp_file(); write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap(); + let default_signer = DefaultSigner { + path: default_keypair_file.clone(), + arg_name: String::new(), + }; let (keypair_file, mut tmp_file) = make_tmp_file(); let stake_account_keypair = Keypair::new(); write_keypair(&stake_account_keypair, tmp_file.as_file_mut()).unwrap(); @@ -1793,7 +1825,7 @@ mod tests { &new_withdraw_string, ]); assert_eq!( - parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_stake_authorize, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::StakeAuthorize { stake_account_pubkey, @@ -1827,7 +1859,7 @@ mod tests { &withdraw_authority_keypair_file, ]); assert_eq!( - parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_stake_authorize, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::StakeAuthorize { stake_account_pubkey, @@ -1865,7 +1897,7 @@ mod tests { &withdraw_authority_keypair_file, ]); assert_eq!( - parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_stake_authorize, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::StakeAuthorize { stake_account_pubkey, @@ -1895,7 +1927,7 @@ mod tests { &new_stake_string, ]); assert_eq!( - parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_stake_authorize, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::StakeAuthorize { stake_account_pubkey, @@ -1919,7 +1951,7 @@ mod tests { &stake_authority_keypair_file, ]); assert_eq!( - parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_stake_authorize, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::StakeAuthorize { stake_account_pubkey, @@ -1949,7 +1981,7 @@ mod tests { &withdraw_authority_keypair_file, ]); assert_eq!( - parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_stake_authorize, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::StakeAuthorize { stake_account_pubkey, @@ -1976,7 +2008,7 @@ mod tests { &new_withdraw_string, ]); assert_eq!( - parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_stake_authorize, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::StakeAuthorize { stake_account_pubkey, @@ -2004,7 +2036,7 @@ mod tests { &withdraw_authority_keypair_file, ]); assert_eq!( - parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_stake_authorize, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::StakeAuthorize { stake_account_pubkey, @@ -2042,7 +2074,7 @@ mod tests { "--sign-only", ]); assert_eq!( - parse_command(&test_authorize, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_authorize, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::StakeAuthorize { stake_account_pubkey, @@ -2075,7 +2107,7 @@ mod tests { &pubkey.to_string(), ]); assert_eq!( - parse_command(&test_authorize, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_authorize, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::StakeAuthorize { stake_account_pubkey, @@ -2121,7 +2153,7 @@ mod tests { &pubkey2.to_string(), ]); assert_eq!( - parse_command(&test_authorize, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_authorize, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::StakeAuthorize { stake_account_pubkey, @@ -2153,7 +2185,7 @@ mod tests { &blockhash_string, ]); assert_eq!( - parse_command(&test_authorize, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_authorize, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::StakeAuthorize { stake_account_pubkey, @@ -2190,7 +2222,7 @@ mod tests { &nonce_keypair_file, ]); assert_eq!( - parse_command(&test_authorize, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_authorize, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::StakeAuthorize { stake_account_pubkey, @@ -2226,7 +2258,7 @@ mod tests { &fee_payer_keypair_file, ]); assert_eq!( - parse_command(&test_authorize, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_authorize, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::StakeAuthorize { stake_account_pubkey, @@ -2260,7 +2292,7 @@ mod tests { &signer, ]); assert_eq!( - parse_command(&test_authorize, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_authorize, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::StakeAuthorize { stake_account_pubkey, @@ -2301,7 +2333,7 @@ mod tests { "43", ]); assert_eq!( - parse_command(&test_create_stake_account, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_create_stake_account, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::CreateStakeAccount { stake_account: 1, @@ -2342,12 +2374,7 @@ mod tests { ]); assert_eq!( - parse_command( - &test_create_stake_account2, - &default_keypair_file, - &mut None - ) - .unwrap(), + parse_command(&test_create_stake_account2, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::CreateStakeAccount { stake_account: 1, @@ -2400,12 +2427,7 @@ mod tests { ]); assert_eq!( - parse_command( - &test_create_stake_account2, - &default_keypair_file, - &mut None - ) - .unwrap(), + parse_command(&test_create_stake_account2, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::CreateStakeAccount { stake_account: 1, @@ -2441,7 +2463,7 @@ mod tests { &vote_account_string, ]); assert_eq!( - parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_delegate_stake, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::DelegateStake { stake_account_pubkey, @@ -2470,7 +2492,7 @@ mod tests { &stake_authority_keypair_file, ]); assert_eq!( - parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_delegate_stake, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::DelegateStake { stake_account_pubkey, @@ -2501,7 +2523,7 @@ mod tests { &vote_account_string, ]); assert_eq!( - parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_delegate_stake, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::DelegateStake { stake_account_pubkey, @@ -2530,7 +2552,7 @@ mod tests { &blockhash_string, ]); assert_eq!( - parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_delegate_stake, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::DelegateStake { stake_account_pubkey, @@ -2560,7 +2582,7 @@ mod tests { "--sign-only", ]); assert_eq!( - parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_delegate_stake, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::DelegateStake { stake_account_pubkey, @@ -2594,7 +2616,7 @@ mod tests { &key1.to_string(), ]); assert_eq!( - parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_delegate_stake, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::DelegateStake { stake_account_pubkey, @@ -2640,7 +2662,7 @@ mod tests { &key2.to_string(), ]); assert_eq!( - parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_delegate_stake, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::DelegateStake { stake_account_pubkey, @@ -2677,7 +2699,7 @@ mod tests { &fee_payer_keypair_file, ]); assert_eq!( - parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_delegate_stake, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::DelegateStake { stake_account_pubkey, @@ -2707,7 +2729,7 @@ mod tests { ]); assert_eq!( - parse_command(&test_withdraw_stake, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_withdraw_stake, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::WithdrawStake { stake_account_pubkey, @@ -2737,7 +2759,7 @@ mod tests { ]); assert_eq!( - parse_command(&test_withdraw_stake, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_withdraw_stake, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::WithdrawStake { stake_account_pubkey, @@ -2772,7 +2794,7 @@ mod tests { ]); assert_eq!( - parse_command(&test_withdraw_stake, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_withdraw_stake, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::WithdrawStake { stake_account_pubkey, @@ -2815,7 +2837,7 @@ mod tests { ]); assert_eq!( - parse_command(&test_withdraw_stake, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_withdraw_stake, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::WithdrawStake { stake_account_pubkey, @@ -2848,7 +2870,7 @@ mod tests { &stake_account_string, ]); assert_eq!( - parse_command(&test_deactivate_stake, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_deactivate_stake, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::DeactivateStake { stake_account_pubkey, @@ -2872,7 +2894,7 @@ mod tests { &stake_authority_keypair_file, ]); assert_eq!( - parse_command(&test_deactivate_stake, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_deactivate_stake, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::DeactivateStake { stake_account_pubkey, @@ -2903,7 +2925,7 @@ mod tests { &blockhash_string, ]); assert_eq!( - parse_command(&test_deactivate_stake, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_deactivate_stake, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::DeactivateStake { stake_account_pubkey, @@ -2930,7 +2952,7 @@ mod tests { "--sign-only", ]); assert_eq!( - parse_command(&test_deactivate_stake, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_deactivate_stake, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::DeactivateStake { stake_account_pubkey, @@ -2961,7 +2983,7 @@ mod tests { &key1.to_string(), ]); assert_eq!( - parse_command(&test_deactivate_stake, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_deactivate_stake, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::DeactivateStake { stake_account_pubkey, @@ -3004,7 +3026,7 @@ mod tests { &key2.to_string(), ]); assert_eq!( - parse_command(&test_deactivate_stake, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_deactivate_stake, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::DeactivateStake { stake_account_pubkey, @@ -3035,7 +3057,7 @@ mod tests { &fee_payer_keypair_file, ]); assert_eq!( - parse_command(&test_deactivate_stake, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_deactivate_stake, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::DeactivateStake { stake_account_pubkey, @@ -3069,7 +3091,7 @@ mod tests { "50", ]); assert_eq!( - parse_command(&test_split_stake_account, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_split_stake_account, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::SplitStake { stake_account_pubkey: stake_account_keypair.pubkey(), @@ -3130,7 +3152,7 @@ mod tests { &stake_signer, ]); assert_eq!( - parse_command(&test_split_stake_account, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_split_stake_account, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::SplitStake { stake_account_pubkey: stake_account_keypair.pubkey(), @@ -3170,7 +3192,7 @@ mod tests { &source_stake_account_pubkey.to_string(), ]); assert_eq!( - parse_command(&test_merge_stake_account, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_merge_stake_account, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::MergeStake { stake_account_pubkey: stake_account_keypair.pubkey(), diff --git a/cli/src/validator_info.rs b/cli/src/validator_info.rs index 6bc653f8c0..eb9a64ab5a 100644 --- a/cli/src/validator_info.rs +++ b/cli/src/validator_info.rs @@ -1,6 +1,5 @@ use crate::{ cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult}, - cli_output::{CliValidatorInfo, CliValidatorInfoVec}, spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount}, }; use bincode::deserialize; @@ -13,8 +12,9 @@ use solana_account_decoder::validator_info::{ use solana_clap_utils::{ input_parsers::pubkey_of, input_validators::{is_pubkey, is_url}, - keypair::signer_from_path, + keypair::DefaultSigner, }; +use solana_cli_output::{CliValidatorInfo, CliValidatorInfoVec}; use solana_client::rpc_client::RpcClient; use solana_config_program::{config_instruction, get_config_data, ConfigKeys, ConfigState}; use solana_remote_wallet::remote_wallet::RemoteWalletManager; @@ -212,7 +212,7 @@ impl ValidatorInfoSubCommands for App<'_, '_> { pub fn parse_validator_info_command( matches: &ArgMatches<'_>, - default_signer_path: &str, + default_signer: &DefaultSigner, wallet_manager: &mut Option>, ) -> Result { let info_pubkey = pubkey_of(matches, "info_pubkey"); @@ -224,12 +224,7 @@ pub fn parse_validator_info_command( force_keybase: matches.is_present("force"), info_pubkey, }, - signers: vec![signer_from_path( - matches, - default_signer_path, - "keypair", - wallet_manager, - )?], + signers: vec![default_signer.signer_from_path(matches, wallet_manager)?], }) } diff --git a/cli/src/vote.rs b/cli/src/vote.rs index 25c6503e38..4a59d5bee7 100644 --- a/cli/src/vote.rs +++ b/cli/src/vote.rs @@ -1,14 +1,19 @@ use crate::{ checks::{check_account_for_fee_with_commitment, check_unique_pubkeys}, cli::{ - generate_unique_signers, log_instruction_custom_error, CliCommand, CliCommandInfo, - CliConfig, CliError, ProcessResult, SignerIndex, + log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError, + ProcessResult, }, - cli_output::{CliEpochVotingHistory, CliLockout, CliVoteAccount}, spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount}, }; use clap::{value_t_or_exit, App, Arg, ArgMatches, SubCommand}; -use solana_clap_utils::{commitment::commitment_arg, input_parsers::*, input_validators::*}; +use solana_clap_utils::{ + commitment::commitment_arg, + input_parsers::*, + input_validators::*, + keypair::{DefaultSigner, SignerIndex}, +}; +use solana_cli_output::{CliEpochVotingHistory, CliLockout, CliVoteAccount}; use solana_client::rpc_client::RpcClient; use solana_remote_wallet::remote_wallet::RemoteWalletManager; use solana_sdk::{ @@ -246,7 +251,7 @@ impl VoteSubCommands for App<'_, '_> { pub fn parse_create_vote_account( matches: &ArgMatches<'_>, - default_signer_path: &str, + default_signer: &DefaultSigner, wallet_manager: &mut Option>, ) -> Result { let (vote_account, vote_account_pubkey) = signer_of(matches, "vote_account", wallet_manager)?; @@ -258,10 +263,9 @@ pub fn parse_create_vote_account( let authorized_withdrawer = pubkey_of_signer(matches, "authorized_withdrawer", wallet_manager)?; let payer_provided = None; - let signer_info = generate_unique_signers( + let signer_info = default_signer.generate_unique_signers( vec![payer_provided, vote_account, identity_account], matches, - default_signer_path, wallet_manager, )?; @@ -280,7 +284,7 @@ pub fn parse_create_vote_account( pub fn parse_vote_authorize( matches: &ArgMatches<'_>, - default_signer_path: &str, + default_signer: &DefaultSigner, wallet_manager: &mut Option>, vote_authorize: VoteAuthorize, ) -> Result { @@ -291,10 +295,9 @@ pub fn parse_vote_authorize( let (authorized, _) = signer_of(matches, "authorized", wallet_manager)?; let payer_provided = None; - let signer_info = generate_unique_signers( + let signer_info = default_signer.generate_unique_signers( vec![payer_provided, authorized], matches, - default_signer_path, wallet_manager, )?; @@ -310,7 +313,7 @@ pub fn parse_vote_authorize( pub fn parse_vote_update_validator( matches: &ArgMatches<'_>, - default_signer_path: &str, + default_signer: &DefaultSigner, wallet_manager: &mut Option>, ) -> Result { let vote_account_pubkey = @@ -321,10 +324,9 @@ pub fn parse_vote_update_validator( signer_of(matches, "authorized_withdrawer", wallet_manager)?; let payer_provided = None; - let signer_info = generate_unique_signers( + let signer_info = default_signer.generate_unique_signers( vec![payer_provided, authorized_withdrawer, new_identity_account], matches, - default_signer_path, wallet_manager, )?; @@ -340,7 +342,7 @@ pub fn parse_vote_update_validator( pub fn parse_vote_update_commission( matches: &ArgMatches<'_>, - default_signer_path: &str, + default_signer: &DefaultSigner, wallet_manager: &mut Option>, ) -> Result { let vote_account_pubkey = @@ -350,10 +352,9 @@ pub fn parse_vote_update_commission( let commission = value_t_or_exit!(matches, "commission", u8); let payer_provided = None; - let signer_info = generate_unique_signers( + let signer_info = default_signer.generate_unique_signers( vec![payer_provided, authorized_withdrawer], matches, - default_signer_path, wallet_manager, )?; @@ -385,7 +386,7 @@ pub fn parse_vote_get_account_command( pub fn parse_withdraw_from_vote_account( matches: &ArgMatches<'_>, - default_signer_path: &str, + default_signer: &DefaultSigner, wallet_manager: &mut Option>, ) -> Result { let vote_account_pubkey = @@ -398,10 +399,9 @@ pub fn parse_withdraw_from_vote_account( signer_of(matches, "authorized_withdrawer", wallet_manager)?; let payer_provided = None; - let signer_info = generate_unique_signers( + let signer_info = default_signer.generate_unique_signers( vec![payer_provided, withdraw_authority], matches, - default_signer_path, wallet_manager, )?; @@ -794,6 +794,10 @@ mod tests { let default_keypair = Keypair::new(); let (default_keypair_file, mut tmp_file) = make_tmp_file(); write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap(); + let default_signer = DefaultSigner { + path: default_keypair_file.clone(), + arg_name: String::new(), + }; let test_authorize_voter = test_commands.clone().get_matches_from(vec![ "test", @@ -803,7 +807,7 @@ mod tests { &pubkey2_string, ]); assert_eq!( - parse_command(&test_authorize_voter, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_authorize_voter, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::VoteAuthorize { vote_account_pubkey: pubkey, @@ -826,7 +830,7 @@ mod tests { &pubkey2_string, ]); assert_eq!( - parse_command(&test_authorize_voter, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_authorize_voter, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::VoteAuthorize { vote_account_pubkey: pubkey, @@ -856,7 +860,7 @@ mod tests { "10", ]); assert_eq!( - parse_command(&test_create_vote_account, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_create_vote_account, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::CreateVoteAccount { vote_account: 1, @@ -885,7 +889,7 @@ mod tests { &identity_keypair_file, ]); assert_eq!( - parse_command(&test_create_vote_account2, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_create_vote_account2, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::CreateVoteAccount { vote_account: 1, @@ -918,7 +922,7 @@ mod tests { &authed.to_string(), ]); assert_eq!( - parse_command(&test_create_vote_account3, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_create_vote_account3, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::CreateVoteAccount { vote_account: 1, @@ -949,7 +953,7 @@ mod tests { &authed.to_string(), ]); assert_eq!( - parse_command(&test_create_vote_account4, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_create_vote_account4, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::CreateVoteAccount { vote_account: 1, @@ -975,7 +979,7 @@ mod tests { &keypair_file, ]); assert_eq!( - parse_command(&test_update_validator, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_update_validator, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::VoteUpdateValidator { vote_account_pubkey: pubkey, @@ -998,7 +1002,7 @@ mod tests { &keypair_file, ]); assert_eq!( - parse_command(&test_update_commission, &default_keypair_file, &mut None).unwrap(), + parse_command(&test_update_commission, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::VoteUpdateCommission { vote_account_pubkey: pubkey, @@ -1021,12 +1025,7 @@ mod tests { "42", ]); assert_eq!( - parse_command( - &test_withdraw_from_vote_account, - &default_keypair_file, - &mut None - ) - .unwrap(), + parse_command(&test_withdraw_from_vote_account, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::WithdrawFromVoteAccount { vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(), @@ -1047,12 +1046,7 @@ mod tests { "ALL", ]); assert_eq!( - parse_command( - &test_withdraw_from_vote_account, - &default_keypair_file, - &mut None - ) - .unwrap(), + parse_command(&test_withdraw_from_vote_account, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::WithdrawFromVoteAccount { vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(), @@ -1078,12 +1072,7 @@ mod tests { &withdraw_authority_file, ]); assert_eq!( - parse_command( - &test_withdraw_from_vote_account, - &default_keypair_file, - &mut None - ) - .unwrap(), + parse_command(&test_withdraw_from_vote_account, &default_signer, &mut None).unwrap(), CliCommandInfo { command: CliCommand::WithdrawFromVoteAccount { vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(), diff --git a/cli/tests/nonce.rs b/cli/tests/nonce.rs index baec3ed00f..06df183de1 100644 --- a/cli/tests/nonce.rs +++ b/cli/tests/nonce.rs @@ -1,15 +1,14 @@ use solana_cli::{ cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig}, - cli_output::OutputFormat, - nonce, - offline::{ - blockhash_query::{self, BlockhashQuery}, - parse_sign_only_reply_string, - }, spend_utils::SpendAmount, test_utils::{check_ready, check_recent_balance}, }; -use solana_client::rpc_client::RpcClient; +use solana_cli_output::{parse_sign_only_reply_string, OutputFormat}; +use solana_client::{ + blockhash_query::{self, BlockhashQuery}, + nonce_utils, + rpc_client::RpcClient, +}; use solana_core::contact_info::ContactInfo; use solana_core::test_validator::{TestValidator, TestValidatorOptions}; use solana_faucet::faucet::run_local_faucet; @@ -302,11 +301,14 @@ fn test_create_account_with_seed() { check_recent_balance(0, &rpc_client, &to_address); // Fetch nonce hash - let nonce_hash = - nonce::get_account_with_commitment(&rpc_client, &nonce_address, CommitmentConfig::recent()) - .and_then(|ref a| nonce::data_from_account(a)) - .unwrap() - .blockhash; + let nonce_hash = nonce_utils::get_account_with_commitment( + &rpc_client, + &nonce_address, + CommitmentConfig::recent(), + ) + .and_then(|ref a| nonce_utils::data_from_account(a)) + .unwrap() + .blockhash; // Test by creating transfer TX with nonce, fully offline let mut authority_config = CliConfig::recent_for_tests(); diff --git a/cli/tests/pay.rs b/cli/tests/pay.rs index 3900dc3b71..a30cfc7d96 100644 --- a/cli/tests/pay.rs +++ b/cli/tests/pay.rs @@ -2,16 +2,15 @@ use chrono::prelude::*; use serde_json::Value; use solana_cli::{ cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig, PayCommand}, - cli_output::OutputFormat, - nonce, - offline::{ - blockhash_query::{self, BlockhashQuery}, - parse_sign_only_reply_string, - }, spend_utils::SpendAmount, test_utils::check_recent_balance, }; -use solana_client::rpc_client::RpcClient; +use solana_cli_output::{parse_sign_only_reply_string, OutputFormat}; +use solana_client::{ + blockhash_query::{self, BlockhashQuery}, + nonce_utils, + rpc_client::RpcClient, +}; use solana_core::test_validator::TestValidator; use solana_faucet::faucet::run_local_faucet; use solana_sdk::{ @@ -411,12 +410,12 @@ fn test_nonced_pay_tx() { check_recent_balance(minimum_nonce_balance, &rpc_client, &nonce_account.pubkey()); // Fetch nonce hash - let nonce_hash = nonce::get_account_with_commitment( + let nonce_hash = nonce_utils::get_account_with_commitment( &rpc_client, &nonce_account.pubkey(), CommitmentConfig::recent(), ) - .and_then(|ref a| nonce::data_from_account(a)) + .and_then(|ref a| nonce_utils::data_from_account(a)) .unwrap() .blockhash; @@ -438,12 +437,12 @@ fn test_nonced_pay_tx() { check_recent_balance(10, &rpc_client, &bob_pubkey); // Verify that nonce has been used - let nonce_hash2 = nonce::get_account_with_commitment( + let nonce_hash2 = nonce_utils::get_account_with_commitment( &rpc_client, &nonce_account.pubkey(), CommitmentConfig::recent(), ) - .and_then(|ref a| nonce::data_from_account(a)) + .and_then(|ref a| nonce_utils::data_from_account(a)) .unwrap() .blockhash; assert_ne!(nonce_hash, nonce_hash2); diff --git a/cli/tests/stake.rs b/cli/tests/stake.rs index 6b6843aaeb..91c058098e 100644 --- a/cli/tests/stake.rs +++ b/cli/tests/stake.rs @@ -1,15 +1,14 @@ use solana_cli::{ cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig}, - cli_output::OutputFormat, - nonce, - offline::{ - blockhash_query::{self, BlockhashQuery}, - parse_sign_only_reply_string, - }, spend_utils::SpendAmount, test_utils::{check_ready, check_recent_balance}, }; -use solana_client::rpc_client::RpcClient; +use solana_cli_output::{parse_sign_only_reply_string, OutputFormat}; +use solana_client::{ + blockhash_query::{self, BlockhashQuery}, + nonce_utils, + rpc_client::RpcClient, +}; use solana_core::test_validator::{TestValidator, TestValidatorOptions}; use solana_faucet::faucet::run_local_faucet; use solana_sdk::{ @@ -503,12 +502,12 @@ fn test_nonced_stake_delegation_and_deactivation() { process_command(&config).unwrap(); // Fetch nonce hash - let nonce_hash = nonce::get_account_with_commitment( + let nonce_hash = nonce_utils::get_account_with_commitment( &rpc_client, &nonce_account.pubkey(), CommitmentConfig::recent(), ) - .and_then(|ref a| nonce::data_from_account(a)) + .and_then(|ref a| nonce_utils::data_from_account(a)) .unwrap() .blockhash; @@ -531,12 +530,12 @@ fn test_nonced_stake_delegation_and_deactivation() { process_command(&config).unwrap(); // Fetch nonce hash - let nonce_hash = nonce::get_account_with_commitment( + let nonce_hash = nonce_utils::get_account_with_commitment( &rpc_client, &nonce_account.pubkey(), CommitmentConfig::recent(), ) - .and_then(|ref a| nonce::data_from_account(a)) + .and_then(|ref a| nonce_utils::data_from_account(a)) .unwrap() .blockhash; @@ -770,12 +769,12 @@ fn test_stake_authorize() { process_command(&config).unwrap(); // Fetch nonce hash - let nonce_hash = nonce::get_account_with_commitment( + let nonce_hash = nonce_utils::get_account_with_commitment( &rpc_client, &nonce_account.pubkey(), CommitmentConfig::recent(), ) - .and_then(|ref a| nonce::data_from_account(a)) + .and_then(|ref a| nonce_utils::data_from_account(a)) .unwrap() .blockhash; @@ -824,12 +823,12 @@ fn test_stake_authorize() { }; assert_eq!(current_authority, online_authority_pubkey); - let new_nonce_hash = nonce::get_account_with_commitment( + let new_nonce_hash = nonce_utils::get_account_with_commitment( &rpc_client, &nonce_account.pubkey(), CommitmentConfig::recent(), ) - .and_then(|ref a| nonce::data_from_account(a)) + .and_then(|ref a| nonce_utils::data_from_account(a)) .unwrap() .blockhash; assert_ne!(nonce_hash, new_nonce_hash); @@ -1069,12 +1068,12 @@ fn test_stake_split() { check_recent_balance(minimum_nonce_balance, &rpc_client, &nonce_account.pubkey()); // Fetch nonce hash - let nonce_hash = nonce::get_account_with_commitment( + let nonce_hash = nonce_utils::get_account_with_commitment( &rpc_client, &nonce_account.pubkey(), CommitmentConfig::recent(), ) - .and_then(|ref a| nonce::data_from_account(a)) + .and_then(|ref a| nonce_utils::data_from_account(a)) .unwrap() .blockhash; @@ -1338,12 +1337,12 @@ fn test_stake_set_lockup() { check_recent_balance(minimum_nonce_balance, &rpc_client, &nonce_account_pubkey); // Fetch nonce hash - let nonce_hash = nonce::get_account_with_commitment( + let nonce_hash = nonce_utils::get_account_with_commitment( &rpc_client, &nonce_account.pubkey(), CommitmentConfig::recent(), ) - .and_then(|ref a| nonce::data_from_account(a)) + .and_then(|ref a| nonce_utils::data_from_account(a)) .unwrap() .blockhash; @@ -1465,12 +1464,12 @@ fn test_offline_nonced_create_stake_account_and_withdraw() { process_command(&config).unwrap(); // Fetch nonce hash - let nonce_hash = nonce::get_account_with_commitment( + let nonce_hash = nonce_utils::get_account_with_commitment( &rpc_client, &nonce_account.pubkey(), CommitmentConfig::recent(), ) - .and_then(|ref a| nonce::data_from_account(a)) + .and_then(|ref a| nonce_utils::data_from_account(a)) .unwrap() .blockhash; @@ -1520,12 +1519,12 @@ fn test_offline_nonced_create_stake_account_and_withdraw() { check_recent_balance(50_000, &rpc_client, &stake_pubkey); // Fetch nonce hash - let nonce_hash = nonce::get_account_with_commitment( + let nonce_hash = nonce_utils::get_account_with_commitment( &rpc_client, &nonce_account.pubkey(), CommitmentConfig::recent(), ) - .and_then(|ref a| nonce::data_from_account(a)) + .and_then(|ref a| nonce_utils::data_from_account(a)) .unwrap() .blockhash; @@ -1568,12 +1567,12 @@ fn test_offline_nonced_create_stake_account_and_withdraw() { check_recent_balance(42, &rpc_client, &recipient_pubkey); // Fetch nonce hash - let nonce_hash = nonce::get_account_with_commitment( + let nonce_hash = nonce_utils::get_account_with_commitment( &rpc_client, &nonce_account.pubkey(), CommitmentConfig::recent(), ) - .and_then(|ref a| nonce::data_from_account(a)) + .and_then(|ref a| nonce_utils::data_from_account(a)) .unwrap() .blockhash; diff --git a/cli/tests/transfer.rs b/cli/tests/transfer.rs index 3c849b026a..7cda0ab44e 100644 --- a/cli/tests/transfer.rs +++ b/cli/tests/transfer.rs @@ -1,15 +1,14 @@ use solana_cli::{ cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig}, - cli_output::OutputFormat, - nonce, - offline::{ - blockhash_query::{self, BlockhashQuery}, - parse_sign_only_reply_string, - }, spend_utils::SpendAmount, test_utils::{check_ready, check_recent_balance}, }; -use solana_client::rpc_client::RpcClient; +use solana_cli_output::{parse_sign_only_reply_string, OutputFormat}; +use solana_client::{ + blockhash_query::{self, BlockhashQuery}, + nonce_utils, + rpc_client::RpcClient, +}; use solana_core::test_validator::{TestValidator, TestValidatorOptions}; use solana_faucet::faucet::run_local_faucet; use solana_sdk::{ @@ -153,12 +152,12 @@ fn test_transfer() { check_recent_balance(49_987 - minimum_nonce_balance, &rpc_client, &sender_pubkey); // Fetch nonce hash - let nonce_hash = nonce::get_account_with_commitment( + let nonce_hash = nonce_utils::get_account_with_commitment( &rpc_client, &nonce_account.pubkey(), CommitmentConfig::recent(), ) - .and_then(|ref a| nonce::data_from_account(a)) + .and_then(|ref a| nonce_utils::data_from_account(a)) .unwrap() .blockhash; @@ -181,12 +180,12 @@ fn test_transfer() { process_command(&config).unwrap(); check_recent_balance(49_976 - minimum_nonce_balance, &rpc_client, &sender_pubkey); check_recent_balance(30, &rpc_client, &recipient_pubkey); - let new_nonce_hash = nonce::get_account_with_commitment( + let new_nonce_hash = nonce_utils::get_account_with_commitment( &rpc_client, &nonce_account.pubkey(), CommitmentConfig::recent(), ) - .and_then(|ref a| nonce::data_from_account(a)) + .and_then(|ref a| nonce_utils::data_from_account(a)) .unwrap() .blockhash; assert_ne!(nonce_hash, new_nonce_hash); @@ -202,12 +201,12 @@ fn test_transfer() { check_recent_balance(49_975 - minimum_nonce_balance, &rpc_client, &sender_pubkey); // Fetch nonce hash - let nonce_hash = nonce::get_account_with_commitment( + let nonce_hash = nonce_utils::get_account_with_commitment( &rpc_client, &nonce_account.pubkey(), CommitmentConfig::recent(), ) - .and_then(|ref a| nonce::data_from_account(a)) + .and_then(|ref a| nonce_utils::data_from_account(a)) .unwrap() .blockhash; diff --git a/cli/tests/vote.rs b/cli/tests/vote.rs index bb32dba825..8510ee1630 100644 --- a/cli/tests/vote.rs +++ b/cli/tests/vote.rs @@ -1,10 +1,12 @@ use solana_cli::{ cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig}, - offline::{blockhash_query::BlockhashQuery, *}, spend_utils::SpendAmount, test_utils::check_recent_balance, }; -use solana_client::rpc_client::RpcClient; +use solana_client::{ + blockhash_query::{self, BlockhashQuery}, + rpc_client::RpcClient, +}; use solana_core::test_validator::TestValidator; use solana_faucet::faucet::run_local_faucet; use solana_sdk::{ diff --git a/client/Cargo.toml b/client/Cargo.toml index 904a9df188..1a97c28a81 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -11,6 +11,7 @@ edition = "2018" [dependencies] bincode = "1.3.1" bs58 = "0.3.1" +clap = "2.33.0" indicatif = "0.15.0" jsonrpc-core = "14.2.0" log = "0.4.8" @@ -20,6 +21,7 @@ serde = "1.0.112" serde_derive = "1.0.103" serde_json = "1.0.56" solana-account-decoder = { path = "../account-decoder", version = "1.3.13" } +solana-clap-utils = { path = "../clap-utils", version = "1.3.13" } solana-net-utils = { path = "../net-utils", version = "1.3.13" } solana-sdk = { path = "../sdk", version = "1.3.13" } solana-transaction-status = { path = "../transaction-status", version = "1.3.13" } diff --git a/cli/src/offline/blockhash_query.rs b/client/src/blockhash_query.rs similarity index 93% rename from cli/src/offline/blockhash_query.rs rename to client/src/blockhash_query.rs index 486a0817e5..dae1b79a60 100644 --- a/cli/src/offline/blockhash_query.rs +++ b/client/src/blockhash_query.rs @@ -1,5 +1,13 @@ -use super::*; -use solana_sdk::commitment_config::CommitmentConfig; +use crate::{nonce_utils, rpc_client::RpcClient}; +use clap::ArgMatches; +use solana_clap_utils::{ + input_parsers::{pubkey_of, value_of}, + nonce::*, + offline::*, +}; +use solana_sdk::{ + commitment_config::CommitmentConfig, fee_calculator::FeeCalculator, hash::Hash, pubkey::Pubkey, +}; #[derive(Debug, PartialEq)] pub enum Source { @@ -21,8 +29,8 @@ impl Source { Ok((res.0, res.1)) } Self::NonceAccount(ref pubkey) => { - let data = nonce::get_account_with_commitment(rpc_client, pubkey, commitment) - .and_then(|ref a| nonce::data_from_account(a))?; + let data = nonce_utils::get_account_with_commitment(rpc_client, pubkey, commitment) + .and_then(|ref a| nonce_utils::data_from_account(a))?; Ok((data.blockhash, data.fee_calculator)) } } @@ -42,8 +50,8 @@ impl Source { Ok(res) } Self::NonceAccount(ref pubkey) => { - let res = nonce::get_account_with_commitment(rpc_client, pubkey, commitment)?; - let res = nonce::data_from_account(&res)?; + let res = nonce_utils::get_account_with_commitment(rpc_client, pubkey, commitment)?; + let res = nonce_utils::data_from_account(&res)?; Ok(Some(res) .filter(|d| d.blockhash == *blockhash) .map(|d| d.fee_calculator)) @@ -75,7 +83,7 @@ impl BlockhashQuery { pub fn new_from_matches(matches: &ArgMatches<'_>) -> Self { let blockhash = value_of(matches, BLOCKHASH_ARG.name); let sign_only = matches.is_present(SIGN_ONLY_ARG.name); - let nonce_account = pubkey_of(matches, nonce::NONCE_ARG.name); + let nonce_account = pubkey_of(matches, NONCE_ARG.name); BlockhashQuery::new(blockhash, sign_only, nonce_account) } @@ -108,17 +116,15 @@ impl Default for BlockhashQuery { #[cfg(test)] mod tests { use super::*; - use crate::{nonce::nonce_arg, offline::blockhash_query::BlockhashQuery}; - use clap::App; - use serde_json::{self, json, Value}; - use solana_account_decoder::{UiAccount, UiAccountEncoding}; - use solana_client::{ + use crate::{ + blockhash_query, rpc_request::RpcRequest, rpc_response::{Response, RpcFeeCalculator, RpcResponseContext}, }; - use solana_sdk::{ - account::Account, fee_calculator::FeeCalculator, hash::hash, nonce, system_program, - }; + use clap::App; + use serde_json::{self, json, Value}; + use solana_account_decoder::{UiAccount, UiAccountEncoding}; + use solana_sdk::{account::Account, hash::hash, nonce, system_program}; use std::collections::HashMap; #[test] @@ -171,9 +177,7 @@ mod tests { #[test] fn test_blockhash_query_new_from_matches_ok() { - let test_commands = App::new("blockhash_query_test") - .arg(nonce_arg()) - .offline_args(); + let test_commands = App::new("blockhash_query_test").nonce_args().offline_args(); let blockhash = hash(&[1u8]); let blockhash_string = blockhash.to_string(); diff --git a/client/src/lib.rs b/client/src/lib.rs index 6a4bce877f..3d70dd0a1a 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -1,9 +1,11 @@ #[macro_use] extern crate serde_derive; +pub mod blockhash_query; pub mod client_error; pub mod http_sender; pub mod mock_sender; +pub mod nonce_utils; pub mod perf_utils; pub mod pubsub_client; pub mod rpc_client; diff --git a/client/src/nonce_utils.rs b/client/src/nonce_utils.rs new file mode 100644 index 0000000000..5a2d448380 --- /dev/null +++ b/client/src/nonce_utils.rs @@ -0,0 +1,82 @@ +use crate::rpc_client::RpcClient; +use solana_sdk::{ + account::Account, + account_utils::StateMut, + commitment_config::CommitmentConfig, + nonce::{ + state::{Data, Versions}, + State, + }, + pubkey::Pubkey, + system_program, +}; + +#[derive(Debug, thiserror::Error, PartialEq)] +pub enum Error { + #[error("invalid account owner")] + InvalidAccountOwner, + #[error("invalid account data")] + InvalidAccountData, + #[error("unexpected account data size")] + UnexpectedDataSize, + #[error("query hash does not match stored hash")] + InvalidHash, + #[error("query authority does not match account authority")] + InvalidAuthority, + #[error("invalid state for requested operation")] + InvalidStateForOperation, + #[error("client error: {0}")] + Client(String), +} + +pub fn get_account(rpc_client: &RpcClient, nonce_pubkey: &Pubkey) -> Result { + get_account_with_commitment(rpc_client, nonce_pubkey, CommitmentConfig::default()) +} + +pub fn get_account_with_commitment( + rpc_client: &RpcClient, + nonce_pubkey: &Pubkey, + commitment: CommitmentConfig, +) -> Result { + rpc_client + .get_account_with_commitment(nonce_pubkey, commitment) + .map_err(|e| Error::Client(format!("{}", e))) + .and_then(|result| { + result + .value + .ok_or_else(|| Error::Client(format!("AccountNotFound: pubkey={}", nonce_pubkey))) + }) + .and_then(|a| match account_identity_ok(&a) { + Ok(()) => Ok(a), + Err(e) => Err(e), + }) +} + +pub fn account_identity_ok(account: &Account) -> Result<(), Error> { + if account.owner != system_program::id() { + Err(Error::InvalidAccountOwner) + } else if account.data.is_empty() { + Err(Error::UnexpectedDataSize) + } else { + Ok(()) + } +} + +pub fn state_from_account(account: &Account) -> Result { + account_identity_ok(account)?; + StateMut::::state(account) + .map_err(|_| Error::InvalidAccountData) + .map(|v| v.convert_to_current()) +} + +pub fn data_from_account(account: &Account) -> Result { + account_identity_ok(account)?; + state_from_account(account).and_then(|ref s| data_from_state(s).map(|d| d.clone())) +} + +pub fn data_from_state(state: &State) -> Result<&Data, Error> { + match state { + State::Uninitialized => Err(Error::InvalidStateForOperation), + State::Initialized(data) => Ok(data), + } +} diff --git a/ledger-tool/Cargo.toml b/ledger-tool/Cargo.toml index 9641125814..9c687c8bf7 100644 --- a/ledger-tool/Cargo.toml +++ b/ledger-tool/Cargo.toml @@ -20,7 +20,7 @@ regex = "1" serde_json = "1.0.56" serde_yaml = "0.8.13" solana-clap-utils = { path = "../clap-utils", version = "1.3.13" } -solana-cli = { path = "../cli", version = "1.3.13" } +solana-cli-output = { path = "../cli-output", version = "1.3.13" } solana-ledger = { path = "../ledger", version = "1.3.13" } solana-logger = { path = "../logger", version = "1.3.13" } solana-measure = { path = "../measure", version = "1.3.13" } diff --git a/ledger-tool/src/bigtable.rs b/ledger-tool/src/bigtable.rs index eff4e2b65b..1f346139e7 100644 --- a/ledger-tool/src/bigtable.rs +++ b/ledger-tool/src/bigtable.rs @@ -4,7 +4,7 @@ use solana_clap_utils::{ input_parsers::pubkey_of, input_validators::{is_slot, is_valid_pubkey}, }; -use solana_cli::display::println_transaction; +use solana_cli_output::display::println_transaction; use solana_ledger::{blockstore::Blockstore, blockstore_db::AccessType}; use solana_sdk::{clock::Slot, pubkey::Pubkey, signature::Signature}; use solana_transaction_status::UiTransactionEncoding; diff --git a/ledger-tool/src/main.rs b/ledger-tool/src/main.rs index edcd681792..14c7b35f0c 100644 --- a/ledger-tool/src/main.rs +++ b/ledger-tool/src/main.rs @@ -118,7 +118,7 @@ fn output_entry( }) .map(|transaction_status| transaction_status.into()); - solana_cli::display::println_transaction( + solana_cli_output::display::println_transaction( &transaction, &transaction_status, " ", diff --git a/stake-accounts/src/arg_parser.rs b/stake-accounts/src/arg_parser.rs index 812c72900e..fc2f029b46 100644 --- a/stake-accounts/src/arg_parser.rs +++ b/stake-accounts/src/arg_parser.rs @@ -13,13 +13,7 @@ use std::ffi::OsString; use std::process::exit; fn fee_payer_arg<'a, 'b>() -> Arg<'a, 'b> { - Arg::with_name("fee_payer") - .long("fee-payer") - .required(true) - .takes_value(true) - .value_name("KEYPAIR") - .validator(is_valid_signer) - .help("Fee payer") + solana_clap_utils::fee_payer::fee_payer_arg().required(true) } fn funding_keypair_arg<'a, 'b>() -> Arg<'a, 'b> { diff --git a/stake-o-matic/Cargo.toml b/stake-o-matic/Cargo.toml index bdf3be7b93..5d3b96ea06 100644 --- a/stake-o-matic/Cargo.toml +++ b/stake-o-matic/Cargo.toml @@ -15,6 +15,7 @@ serde_yaml = "0.8.13" solana-clap-utils = { path = "../clap-utils", version = "1.3.13" } solana-client = { path = "../client", version = "1.3.13" } solana-cli-config = { path = "../cli-config", version = "1.3.13" } +solana-cli-output = { path = "../cli-output", version = "1.3.13" } solana-cli = { path = "../cli", version = "1.3.13" } solana-logger = { path = "../logger", version = "1.3.13" } solana-metrics = { path = "../metrics", version = "1.3.13" } diff --git a/stake-o-matic/src/main.rs b/stake-o-matic/src/main.rs index c99e0877b1..96cb7c10c4 100644 --- a/stake-o-matic/src/main.rs +++ b/stake-o-matic/src/main.rs @@ -4,7 +4,7 @@ use solana_clap_utils::{ input_parsers::{keypair_of, pubkey_of}, input_validators::{is_amount, is_keypair, is_pubkey_or_keypair, is_url, is_valid_percentage}, }; -use solana_cli::display::format_labeled_address; +use solana_cli_output::display::format_labeled_address; use solana_client::{ client_error, rpc_client::RpcClient, rpc_request::MAX_GET_SIGNATURE_STATUSES_QUERY_ITEMS, rpc_response::RpcVoteAccountInfo, diff --git a/watchtower/Cargo.toml b/watchtower/Cargo.toml index cf202e38c6..08095ed617 100644 --- a/watchtower/Cargo.toml +++ b/watchtower/Cargo.toml @@ -14,7 +14,7 @@ log = "0.4.8" humantime = "2.0.1" solana-clap-utils = { path = "../clap-utils", version = "1.3.13" } solana-cli-config = { path = "../cli-config", version = "1.3.13" } -solana-cli = { path = "../cli", version = "1.3.13" } +solana-cli-output = { path = "../cli-output", version = "1.3.13" } solana-client = { path = "../client", version = "1.3.13" } solana-logger = { path = "../logger", version = "1.3.13" } solana-metrics = { path = "../metrics", version = "1.3.13" } diff --git a/watchtower/src/main.rs b/watchtower/src/main.rs index 67e22b00c5..6b72f28f69 100644 --- a/watchtower/src/main.rs +++ b/watchtower/src/main.rs @@ -6,7 +6,7 @@ use solana_clap_utils::{ input_parsers::pubkeys_of, input_validators::{is_pubkey_or_keypair, is_url}, }; -use solana_cli::display::{format_labeled_address, write_transaction}; +use solana_cli_output::display::{format_labeled_address, write_transaction}; use solana_client::{ client_error::Result as ClientResult, rpc_client::RpcClient, rpc_response::RpcVoteAccountStatus, };