diff --git a/Cargo.lock b/Cargo.lock index 5d71019a0e..046a74dbbf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3488,6 +3488,7 @@ dependencies = [ "solana-budget-program", "solana-clap-utils", "solana-cli-config", + "solana-cli-output", "solana-client", "solana-config-program", "solana-core", @@ -3518,6 +3519,26 @@ dependencies = [ "url 2.1.1", ] +[[package]] +name = "solana-cli-output" +version = "1.2.30" +dependencies = [ + "Inflector", + "chrono", + "console 0.10.3", + "humantime 2.0.1", + "indicatif", + "serde", + "serde_derive", + "serde_json", + "solana-clap-utils", + "solana-client", + "solana-sdk 1.2.30", + "solana-stake-program", + "solana-transaction-status", + "solana-vote-program", +] + [[package]] name = "solana-client" version = "1.2.30" @@ -3525,6 +3546,7 @@ dependencies = [ "assert_matches", "bincode", "bs58", + "clap", "indicatif", "jsonrpc-core", "jsonrpc-http-server", @@ -3535,6 +3557,7 @@ dependencies = [ "serde_derive", "serde_json", "solana-account-decoder", + "solana-clap-utils", "solana-logger 1.2.30", "solana-net-utils", "solana-sdk 1.2.30", @@ -3927,7 +3950,7 @@ dependencies = [ "serde_yaml", "signal-hook", "solana-clap-utils", - "solana-cli", + "solana-cli-output", "solana-ledger", "solana-logger 1.2.30", "solana-measure", @@ -4375,6 +4398,7 @@ dependencies = [ "serde_yaml", "solana-clap-utils", "solana-cli-config", + "solana-cli-output", "solana-client", "solana-logger 1.2.30", "solana-metrics", @@ -4604,8 +4628,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.2.30", "solana-metrics", diff --git a/Cargo.toml b/Cargo.toml index d37b9f78ba..bd0cb329e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "accounts-bench", "banking-bench", "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 ca520a4f7b..4a50f2d44b 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..56c2ec8153 --- /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.2.30" +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.10.1" +humantime = "2.0.0" +Inflector = "0.11.4" +indicatif = "0.14.0" +serde = "1.0.110" +serde_derive = "1.0.103" +serde_json = "1.0.53" +solana-clap-utils = { path = "../clap-utils", version = "1.2.30" } +solana-client = { path = "../client", version = "1.2.30" } +solana-sdk = { path = "../sdk", version = "1.2.30" } +solana-stake-program = { path = "../programs/stake", version = "1.2.30" } +solana-transaction-status = { path = "../transaction-status", version = "1.2.30" } +solana-vote-program = { path = "../programs/vote", version = "1.2.30" } + +[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 9372246dbf..a42c7b0b34 100644 --- a/cli/src/cli_output.rs +++ b/cli-output/src/cli_output.rs @@ -1,24 +1,29 @@ -use crate::{cli::build_balance_message, display::writeln_name_value}; +use crate::display::{build_balance_message, 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::{ authorized_voters::AuthorizedVoters, vote_state::{BlockTimestamp, Lockout}, }; -use std::{collections::BTreeMap, fmt, time::Duration}; +use std::{collections::BTreeMap, fmt, str::FromStr, time::Duration}; static WARNING: Emoji = Emoji("⚠️", "!"); @@ -1086,3 +1091,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 90% rename from cli/src/display.rs rename to cli-output/src/display.rs index a623383051..4c48d73dbe 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::{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 == "" { @@ -27,21 +44,6 @@ pub fn writeln_name_value(f: &mut fmt::Formatter, name: &str, value: &str) -> fm writeln!(f, "{} {}", style(name).bold(), styled_value) } -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(), - ); -} - 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 301158cfc1..ebe3f4ce96 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -31,6 +31,7 @@ solana-account-decoder = { path = "../account-decoder", version = "1.2.30" } solana-budget-program = { path = "../programs/budget", version = "1.2.30" } solana-clap-utils = { path = "../clap-utils", version = "1.2.30" } solana-cli-config = { path = "../cli-config", version = "1.2.30" } +solana-cli-output = { path = "../cli-output", version = "1.2.30" } solana-client = { path = "../client", version = "1.2.30" } solana-config-program = { path = "../programs/config", version = "1.2.30" } solana-faucet = { path = "../faucet", version = "1.2.30" } diff --git a/cli/src/cli.rs b/cli/src/cli.rs index dd8a5730f6..0178915240 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,15 +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::{ + self, commitment::{commitment_arg_with_default, COMMITMENT_ARG}, + fee_payer::{fee_payer_arg, FEE_PAYER_ARG}, input_parsers::*, input_validators::*, - keypair::signer_from_path, - offline::SIGN_ONLY_ARG, - ArgConstant, + 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}, @@ -41,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, @@ -73,96 +72,8 @@ 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 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, @@ -475,7 +386,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}")] @@ -490,10 +401,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), } } @@ -613,7 +524,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() { @@ -628,7 +539,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, @@ -650,7 +561,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![], @@ -667,99 +578,87 @@ 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)) => Ok(CliCommandInfo { command: CliCommand::Deploy(matches.value_of("program_location").unwrap().to_string()), - signers: vec![signer_from_path( - matches, - default_signer_path, - "keypair", - wallet_manager, - )?], + signers: vec![default_signer.signer_from_path(matches, wallet_manager)?], }), // 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 @@ -787,12 +686,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 { @@ -811,12 +705,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 { @@ -829,8 +718,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), @@ -892,12 +780,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 { @@ -940,8 +824,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), @@ -962,8 +845,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), @@ -988,12 +870,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 { @@ -1024,66 +902,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() { @@ -1508,7 +1336,7 @@ 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 = rpc_client.get_account(nonce_account)?; @@ -1552,7 +1380,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(&tx); @@ -1598,7 +1426,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(&tx); @@ -1707,7 +1535,7 @@ 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 = rpc_client.get_account(nonce_account)?; @@ -2436,28 +2264,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) @@ -2667,8 +2473,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") @@ -2760,8 +2565,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( @@ -2798,12 +2602,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; @@ -2830,13 +2632,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); @@ -2846,8 +2654,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)); @@ -2857,8 +2666,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)); @@ -2878,8 +2688,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)); @@ -2900,13 +2711,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, @@ -2919,17 +2738,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()), @@ -2946,7 +2761,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()), @@ -2961,7 +2776,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, @@ -2978,7 +2793,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()], @@ -2993,7 +2808,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![], @@ -3002,7 +2817,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()); @@ -3021,7 +2836,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, @@ -3039,7 +2854,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, @@ -3056,7 +2871,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("/Users/test/program.o".to_string()), signers: vec![read_keypair_file(&keypair_file).unwrap().into()], @@ -3069,7 +2884,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![], @@ -3081,7 +2896,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![], @@ -3094,7 +2909,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), @@ -3117,7 +2932,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), @@ -3137,7 +2952,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), @@ -3161,7 +2976,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), @@ -3187,7 +3002,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), @@ -3210,7 +3025,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), @@ -3239,7 +3054,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), @@ -3272,7 +3087,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), @@ -3310,7 +3125,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), @@ -3348,7 +3163,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![ @@ -3358,7 +3173,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()], @@ -3379,7 +3194,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), @@ -3403,7 +3218,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()], @@ -3417,7 +3232,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] @@ -3807,6 +3622,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(); @@ -3819,7 +3638,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), @@ -3841,7 +3660,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, @@ -3867,7 +3686,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), @@ -3897,7 +3716,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), @@ -3932,7 +3751,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), @@ -3971,7 +3790,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), @@ -3994,54 +3813,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 6a3f223842..950b01caab 100644 --- a/cli/src/cluster_query.rs +++ b/cli/src/cluster_query.rs @@ -1,7 +1,5 @@ use crate::{ cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult}, - cli_output::*, - display::{new_spinner_progress_bar, println_name_value}, spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount}, }; use clap::{value_t, value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand}; @@ -10,7 +8,11 @@ use solana_clap_utils::{ commitment::{commitment_arg, COMMITMENT_ARG}, input_parsers::*, input_validators::*, - keypair::signer_from_path, + keypair::DefaultSigner, +}; +use solana_cli_output::{ + display::{new_spinner_progress_bar, println_name_value}, + *, }; use solana_client::{ pubsub_client::{PubsubClient, SlotInfoMessage}, @@ -306,7 +308,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); @@ -326,12 +328,7 @@ pub fn parse_cluster_ping( timeout, commitment_config, }, - signers: vec![signer_from_path( - matches, - default_signer_path, - "keypair", - wallet_manager, - )?], + signers: vec![default_signer.signer_from_path(matches, wallet_manager)?], }) } @@ -1342,12 +1339,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![], @@ -1358,7 +1359,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![], @@ -1367,7 +1368,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![], @@ -1380,7 +1381,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![], @@ -1391,7 +1392,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 { commitment_config: CommitmentConfig::recent(), @@ -1404,7 +1405,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![], @@ -1413,7 +1414,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 { commitment_config: CommitmentConfig::recent(), @@ -1426,7 +1427,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 { commitment_config: CommitmentConfig::recent(), @@ -1439,7 +1440,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 { commitment_config: CommitmentConfig::recent(), @@ -1452,7 +1453,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 { commitment_config: CommitmentConfig::recent(), @@ -1474,7 +1475,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 f68d6c2370..eec50029b4 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -2,17 +2,33 @@ use clap::{crate_description, crate_name, AppSettings, Arg, ArgGroup, ArgMatches use console::style; use solana_clap_utils::{ - input_validators::is_url, keypair::SKIP_SEED_PHRASE_VALIDATION_ARG, DisplayError, + 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}, - cli_output::OutputFormat, - display::{println_name_value, println_name_value_or}, +use solana_cli::cli::{ + app, parse_command, process_command, CliCommandInfo, CliConfig, SettingType, }; use solana_cli_config::{Config, CONFIG_FILE}; +use solana_cli_output::{display::println_name_value, OutputFormat}; use solana_remote_wallet::remote_wallet::RemoteWalletManager; use std::{error, sync::Arc}; +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)) => match matches.subcommand() { @@ -119,13 +135,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 ff31876a4e..bf24e5c122 100644 --- a/cli/src/nonce.rs +++ b/cli/src/nonce.rs @@ -1,28 +1,26 @@ use crate::{ checks::{check_account_for_fee, 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, 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, @@ -32,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( @@ -219,51 +164,9 @@ impl NonceSubCommands for App<'_, '_> { } } -pub fn get_account( - rpc_client: &RpcClient, - nonce_pubkey: &Pubkey, -) -> Result { - rpc_client - .get_account(nonce_pubkey) - .map_err(|e| CliNonceError::Client(format!("{}", e))) - .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(); @@ -272,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, )?; @@ -291,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) = @@ -301,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, )?; @@ -334,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(); @@ -342,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, )?; @@ -377,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(); @@ -388,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, )?; @@ -415,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()), } } @@ -638,9 +537,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, }; @@ -657,6 +557,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(); @@ -675,12 +579,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, @@ -701,12 +600,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(), @@ -728,7 +622,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, @@ -753,7 +647,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, @@ -775,7 +669,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![], @@ -789,7 +683,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(), @@ -809,7 +703,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(), @@ -829,7 +723,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(), @@ -850,7 +744,7 @@ mod tests { assert_eq!( parse_command( &test_withdraw_from_nonce_account, - &default_keypair_file, + &default_signer, &mut None ) .unwrap(), @@ -878,7 +772,7 @@ mod tests { assert_eq!( parse_command( &test_withdraw_from_nonce_account, - &default_keypair_file, + &default_signer, &mut None ) .unwrap(), @@ -913,14 +807,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 { @@ -932,7 +826,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 { @@ -944,7 +838,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); @@ -952,7 +846,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,); } } @@ -964,14 +858,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), ); } @@ -996,7 +890,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), ); } @@ -1006,11 +900,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 2d3793cf16..707033e431 100644 --- a/cli/src/stake.rs +++ b/cli/src/stake.rs @@ -1,18 +1,29 @@ use crate::{ checks::{check_account_for_fee, 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::{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, rpc_client::RpcClient, + rpc_request::DELINQUENT_VALIDATOR_SLOT_DISTANCE, +}; use solana_remote_wallet::remote_wallet::RemoteWalletManager; use solana_sdk::{ account_utils::StateMut, @@ -142,8 +153,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( @@ -172,8 +182,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( @@ -203,8 +212,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( @@ -219,8 +227,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( @@ -260,8 +267,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( @@ -283,8 +289,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( @@ -315,8 +320,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") @@ -371,8 +375,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( @@ -409,7 +412,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()); @@ -434,7 +437,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 { @@ -461,7 +464,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 = @@ -483,7 +486,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 { @@ -503,7 +506,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 = @@ -555,7 +558,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() @@ -586,7 +589,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 = @@ -610,7 +613,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 { @@ -631,7 +634,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 = @@ -653,7 +656,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 { @@ -672,7 +675,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 = @@ -691,7 +694,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 { @@ -709,7 +712,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 = @@ -735,7 +738,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 { @@ -756,7 +759,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 = @@ -779,7 +782,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 { @@ -939,7 +942,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(&tx); @@ -994,7 +997,7 @@ 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 { @@ -1048,7 +1051,7 @@ 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 { @@ -1111,7 +1114,7 @@ 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 { @@ -1245,7 +1248,7 @@ 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 { @@ -1338,7 +1341,7 @@ 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 { @@ -1395,7 +1398,7 @@ 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 { @@ -1648,7 +1651,7 @@ 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 { @@ -1670,6 +1673,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::{ @@ -1690,6 +1694,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(); @@ -1717,7 +1725,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, @@ -1751,7 +1759,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, @@ -1789,7 +1797,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, @@ -1819,7 +1827,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, @@ -1843,7 +1851,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, @@ -1873,7 +1881,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, @@ -1900,7 +1908,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, @@ -1928,7 +1936,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, @@ -1966,7 +1974,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, @@ -1999,7 +2007,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, @@ -2045,7 +2053,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, @@ -2077,7 +2085,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, @@ -2114,7 +2122,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, @@ -2150,7 +2158,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, @@ -2184,7 +2192,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, @@ -2225,7 +2233,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, @@ -2266,12 +2274,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, @@ -2324,12 +2327,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, @@ -2365,7 +2363,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, @@ -2394,7 +2392,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, @@ -2425,7 +2423,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, @@ -2454,7 +2452,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, @@ -2484,7 +2482,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, @@ -2518,7 +2516,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, @@ -2564,7 +2562,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, @@ -2601,7 +2599,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, @@ -2631,7 +2629,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, @@ -2661,7 +2659,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, @@ -2696,7 +2694,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, @@ -2739,7 +2737,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 +2770,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, @@ -2796,7 +2794,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, @@ -2827,7 +2825,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, @@ -2854,7 +2852,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, @@ -2885,7 +2883,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, @@ -2928,7 +2926,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, @@ -2959,7 +2957,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, @@ -2993,7 +2991,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(), @@ -3054,7 +3052,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(), @@ -3094,7 +3092,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 e1e4dd58e6..10e0089687 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 11407d8d66..0133edd1b1 100644 --- a/cli/src/vote.rs +++ b/cli/src/vote.rs @@ -1,10 +1,9 @@ use crate::{ checks::{check_account_for_fee, 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}; @@ -12,7 +11,9 @@ use solana_clap_utils::{ commitment::{commitment_arg, 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::{ @@ -250,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)?; @@ -262,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, )?; @@ -284,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 { @@ -295,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, )?; @@ -314,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 = @@ -325,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, )?; @@ -344,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 = @@ -354,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, )?; @@ -391,7 +388,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 = @@ -404,10 +401,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, )?; @@ -760,6 +756,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", @@ -769,7 +769,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, @@ -792,7 +792,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, @@ -822,7 +822,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, @@ -851,7 +851,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, @@ -884,7 +884,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, @@ -915,7 +915,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, @@ -941,7 +941,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, @@ -964,7 +964,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, @@ -987,12 +987,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(), @@ -1013,12 +1008,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(), @@ -1044,12 +1034,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 5934452f62..59faa8c392 100644 --- a/cli/tests/nonce.rs +++ b/cli/tests/nonce.rs @@ -1,15 +1,14 @@ use solana_cli::test_utils::check_balance; 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, }; -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::validator::{TestValidator, TestValidatorOptions}; use solana_faucet::faucet::run_local_faucet; @@ -299,8 +298,8 @@ fn test_create_account_with_seed() { check_balance(0, &rpc_client, &to_address); // Fetch nonce hash - let nonce_hash = nonce::get_account(&rpc_client, &nonce_address) - .and_then(|ref a| nonce::data_from_account(a)) + let nonce_hash = nonce_utils::get_account(&rpc_client, &nonce_address) + .and_then(|ref a| nonce_utils::data_from_account(a)) .unwrap() .blockhash; diff --git a/cli/tests/pay.rs b/cli/tests/pay.rs index a82fa9ef70..5753589910 100644 --- a/cli/tests/pay.rs +++ b/cli/tests/pay.rs @@ -3,15 +3,14 @@ use serde_json::Value; use solana_cli::test_utils::check_balance; 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, }; -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::validator::TestValidator; use solana_faucet::faucet::run_local_faucet; use solana_sdk::{ @@ -410,8 +409,8 @@ fn test_nonced_pay_tx() { check_balance(minimum_nonce_balance, &rpc_client, &nonce_account.pubkey()); // Fetch nonce hash - let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey()) - .and_then(|ref a| nonce::data_from_account(a)) + let nonce_hash = nonce_utils::get_account(&rpc_client, &nonce_account.pubkey()) + .and_then(|ref a| nonce_utils::data_from_account(a)) .unwrap() .blockhash; @@ -433,8 +432,8 @@ fn test_nonced_pay_tx() { check_balance(10, &rpc_client, &bob_pubkey); // Verify that nonce has been used - let nonce_hash2 = nonce::get_account(&rpc_client, &nonce_account.pubkey()) - .and_then(|ref a| nonce::data_from_account(a)) + let nonce_hash2 = nonce_utils::get_account(&rpc_client, &nonce_account.pubkey()) + .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 9429cf0ab3..c384504c75 100644 --- a/cli/tests/stake.rs +++ b/cli/tests/stake.rs @@ -1,15 +1,14 @@ use solana_cli::test_utils::check_balance; 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, }; -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::validator::{TestValidator, TestValidatorOptions}; use solana_faucet::faucet::run_local_faucet; use solana_sdk::{ @@ -502,8 +501,8 @@ fn test_nonced_stake_delegation_and_deactivation() { process_command(&config).unwrap(); // Fetch nonce hash - let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey()) - .and_then(|ref a| nonce::data_from_account(a)) + let nonce_hash = nonce_utils::get_account(&rpc_client, &nonce_account.pubkey()) + .and_then(|ref a| nonce_utils::data_from_account(a)) .unwrap() .blockhash; @@ -526,8 +525,8 @@ fn test_nonced_stake_delegation_and_deactivation() { process_command(&config).unwrap(); // Fetch nonce hash - let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey()) - .and_then(|ref a| nonce::data_from_account(a)) + let nonce_hash = nonce_utils::get_account(&rpc_client, &nonce_account.pubkey()) + .and_then(|ref a| nonce_utils::data_from_account(a)) .unwrap() .blockhash; @@ -742,8 +741,8 @@ fn test_stake_authorize() { process_command(&config).unwrap(); // Fetch nonce hash - let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey()) - .and_then(|ref a| nonce::data_from_account(a)) + let nonce_hash = nonce_utils::get_account(&rpc_client, &nonce_account.pubkey()) + .and_then(|ref a| nonce_utils::data_from_account(a)) .unwrap() .blockhash; @@ -788,8 +787,8 @@ fn test_stake_authorize() { }; assert_eq!(current_authority, online_authority_pubkey); - let new_nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey()) - .and_then(|ref a| nonce::data_from_account(a)) + let new_nonce_hash = nonce_utils::get_account(&rpc_client, &nonce_account.pubkey()) + .and_then(|ref a| nonce_utils::data_from_account(a)) .unwrap() .blockhash; assert_ne!(nonce_hash, new_nonce_hash); @@ -1024,8 +1023,8 @@ fn test_stake_split() { check_balance(minimum_nonce_balance, &rpc_client, &nonce_account.pubkey()); // Fetch nonce hash - let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey()) - .and_then(|ref a| nonce::data_from_account(a)) + let nonce_hash = nonce_utils::get_account(&rpc_client, &nonce_account.pubkey()) + .and_then(|ref a| nonce_utils::data_from_account(a)) .unwrap() .blockhash; @@ -1281,8 +1280,8 @@ fn test_stake_set_lockup() { check_balance(minimum_nonce_balance, &rpc_client, &nonce_account_pubkey); // Fetch nonce hash - let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey()) - .and_then(|ref a| nonce::data_from_account(a)) + let nonce_hash = nonce_utils::get_account(&rpc_client, &nonce_account.pubkey()) + .and_then(|ref a| nonce_utils::data_from_account(a)) .unwrap() .blockhash; @@ -1400,8 +1399,8 @@ fn test_offline_nonced_create_stake_account_and_withdraw() { process_command(&config).unwrap(); // Fetch nonce hash - let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey()) - .and_then(|ref a| nonce::data_from_account(a)) + let nonce_hash = nonce_utils::get_account(&rpc_client, &nonce_account.pubkey()) + .and_then(|ref a| nonce_utils::data_from_account(a)) .unwrap() .blockhash; @@ -1451,8 +1450,8 @@ fn test_offline_nonced_create_stake_account_and_withdraw() { check_balance(50_000, &rpc_client, &stake_pubkey); // Fetch nonce hash - let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey()) - .and_then(|ref a| nonce::data_from_account(a)) + let nonce_hash = nonce_utils::get_account(&rpc_client, &nonce_account.pubkey()) + .and_then(|ref a| nonce_utils::data_from_account(a)) .unwrap() .blockhash; @@ -1495,8 +1494,8 @@ fn test_offline_nonced_create_stake_account_and_withdraw() { check_balance(42, &rpc_client, &recipient_pubkey); // Fetch nonce hash - let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey()) - .and_then(|ref a| nonce::data_from_account(a)) + let nonce_hash = nonce_utils::get_account(&rpc_client, &nonce_account.pubkey()) + .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 8c76416104..4500c67aa4 100644 --- a/cli/tests/transfer.rs +++ b/cli/tests/transfer.rs @@ -1,15 +1,14 @@ use solana_cli::test_utils::check_balance; 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, }; -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::validator::{TestValidator, TestValidatorOptions}; use solana_faucet::faucet::run_local_faucet; use solana_sdk::{ @@ -147,8 +146,8 @@ fn test_transfer() { check_balance(49_987 - minimum_nonce_balance, &rpc_client, &sender_pubkey); // Fetch nonce hash - let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey()) - .and_then(|ref a| nonce::data_from_account(a)) + let nonce_hash = nonce_utils::get_account(&rpc_client, &nonce_account.pubkey()) + .and_then(|ref a| nonce_utils::data_from_account(a)) .unwrap() .blockhash; @@ -171,8 +170,8 @@ fn test_transfer() { process_command(&config).unwrap(); check_balance(49_976 - minimum_nonce_balance, &rpc_client, &sender_pubkey); check_balance(30, &rpc_client, &recipient_pubkey); - let new_nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey()) - .and_then(|ref a| nonce::data_from_account(a)) + let new_nonce_hash = nonce_utils::get_account(&rpc_client, &nonce_account.pubkey()) + .and_then(|ref a| nonce_utils::data_from_account(a)) .unwrap() .blockhash; assert_ne!(nonce_hash, new_nonce_hash); @@ -188,8 +187,8 @@ fn test_transfer() { check_balance(49_975 - minimum_nonce_balance, &rpc_client, &sender_pubkey); // Fetch nonce hash - let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey()) - .and_then(|ref a| nonce::data_from_account(a)) + let nonce_hash = nonce_utils::get_account(&rpc_client, &nonce_account.pubkey()) + .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 366f54c6f0..e7a941bc51 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_balance, }; -use solana_client::rpc_client::RpcClient; +use solana_client::{ + blockhash_query::{self, BlockhashQuery}, + rpc_client::RpcClient, +}; use solana_core::validator::TestValidator; use solana_faucet::faucet::run_local_faucet; use solana_sdk::{ diff --git a/client/Cargo.toml b/client/Cargo.toml index 4dd9736cec..e3b20f73b8 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.14.0" jsonrpc-core = "14.1.0" log = "0.4.8" @@ -20,6 +21,7 @@ serde = "1.0.110" serde_derive = "1.0.103" serde_json = "1.0.53" solana-account-decoder = { path = "../account-decoder", version = "1.2.30" } +solana-clap-utils = { path = "../clap-utils", version = "1.2.30" } solana-net-utils = { path = "../net-utils", version = "1.2.30" } solana-sdk = { path = "../sdk", version = "1.2.30" } solana-transaction-status = { path = "../transaction-status", version = "1.2.30" } diff --git a/cli/src/offline/blockhash_query.rs b/client/src/blockhash_query.rs similarity index 94% rename from cli/src/offline/blockhash_query.rs rename to client/src/blockhash_query.rs index f38b17d2f6..d5667e420a 100644 --- a/cli/src/offline/blockhash_query.rs +++ b/client/src/blockhash_query.rs @@ -1,4 +1,11 @@ -use super::*; +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::{fee_calculator::FeeCalculator, hash::Hash, pubkey::Pubkey}; #[derive(Debug, PartialEq)] pub enum Source { @@ -17,8 +24,8 @@ impl Source { Ok(res) } Self::NonceAccount(ref pubkey) => { - let data = nonce::get_account(rpc_client, pubkey) - .and_then(|ref a| nonce::data_from_account(a))?; + let data = nonce_utils::get_account(rpc_client, pubkey) + .and_then(|ref a| nonce_utils::data_from_account(a))?; Ok((data.blockhash, data.fee_calculator)) } } @@ -35,8 +42,8 @@ impl Source { Ok(res) } Self::NonceAccount(ref pubkey) => { - let res = nonce::get_account(rpc_client, pubkey) - .and_then(|ref a| nonce::data_from_account(a)) + let res = nonce_utils::get_account(rpc_client, pubkey) + .and_then(|ref a| nonce_utils::data_from_account(a)) .and_then(|d| { if d.blockhash == *blockhash { Ok(Some(d.fee_calculator)) @@ -73,7 +80,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) } @@ -103,17 +110,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] @@ -166,9 +171,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..d8a2ccfb4f --- /dev/null +++ b/client/src/nonce_utils.rs @@ -0,0 +1,68 @@ +use crate::rpc_client::RpcClient; +use solana_sdk::{ + account::Account, + account_utils::StateMut, + 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 { + rpc_client + .get_account(nonce_pubkey) + .map_err(|e| Error::Client(format!("{}", e))) + .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 e56916a0d5..b187bfefbb 100644 --- a/ledger-tool/Cargo.toml +++ b/ledger-tool/Cargo.toml @@ -20,7 +20,7 @@ regex = "1" serde_json = "1.0.53" serde_yaml = "0.8.12" solana-clap-utils = { path = "../clap-utils", version = "1.2.30" } -solana-cli = { path = "../cli", version = "1.2.30" } +solana-cli-output = { path = "../cli-output", version = "1.2.30" } solana-ledger = { path = "../ledger", version = "1.2.30" } solana-logger = { path = "../logger", version = "1.2.30" } solana-measure = { path = "../measure", version = "1.2.30" } 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 e6b48f2b23..7921a3f857 100644 --- a/ledger-tool/src/main.rs +++ b/ledger-tool/src/main.rs @@ -124,7 +124,7 @@ fn output_slot( }) .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 90a0d54134..10c31c506c 100644 --- a/stake-o-matic/Cargo.toml +++ b/stake-o-matic/Cargo.toml @@ -15,6 +15,7 @@ serde_yaml = "0.8.12" solana-clap-utils = { path = "../clap-utils", version = "1.2.30" } solana-client = { path = "../client", version = "1.2.30" } solana-cli-config = { path = "../cli-config", version = "1.2.30" } +solana-cli-output = { path = "../cli-output", version = "1.2.30" } solana-logger = { path = "../logger", version = "1.2.30" } solana-metrics = { path = "../metrics", version = "1.2.30" } solana-notifier = { path = "../notifier", version = "1.2.30" } diff --git a/watchtower/Cargo.toml b/watchtower/Cargo.toml index 8b620a01e6..a3a6ab273a 100644 --- a/watchtower/Cargo.toml +++ b/watchtower/Cargo.toml @@ -14,7 +14,7 @@ log = "0.4.8" humantime = "2.0.0" solana-clap-utils = { path = "../clap-utils", version = "1.2.30" } solana-cli-config = { path = "../cli-config", version = "1.2.30" } -solana-cli = { path = "../cli", version = "1.2.30" } +solana-cli-output = { path = "../cli-output", version = "1.2.30" } solana-client = { path = "../client", version = "1.2.30" } solana-logger = { path = "../logger", version = "1.2.30" } solana-metrics = { path = "../metrics", version = "1.2.30" } diff --git a/watchtower/src/main.rs b/watchtower/src/main.rs index 967099e0e1..4071e62a75 100644 --- a/watchtower/src/main.rs +++ b/watchtower/src/main.rs @@ -184,7 +184,7 @@ fn process_confirmed_block(notifier: &Notifier, slot: Slot, confirmed_block: Con if notify { let mut w = Vec::new(); - if solana_cli::display::write_transaction( + if solana_cli_output::display::write_transaction( &mut w, &transaction, &rpc_transaction.meta,