CLI: Factor out offline helpers (bp #12382)

This commit is contained in:
Trent Nelson
2020-09-21 13:26:06 -06:00
committed by mergify[bot]
parent dfabe35b27
commit f4a2045876
37 changed files with 925 additions and 931 deletions

28
Cargo.lock generated
View File

@ -3488,6 +3488,7 @@ dependencies = [
"solana-budget-program", "solana-budget-program",
"solana-clap-utils", "solana-clap-utils",
"solana-cli-config", "solana-cli-config",
"solana-cli-output",
"solana-client", "solana-client",
"solana-config-program", "solana-config-program",
"solana-core", "solana-core",
@ -3518,6 +3519,26 @@ dependencies = [
"url 2.1.1", "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]] [[package]]
name = "solana-client" name = "solana-client"
version = "1.2.30" version = "1.2.30"
@ -3525,6 +3546,7 @@ dependencies = [
"assert_matches", "assert_matches",
"bincode", "bincode",
"bs58", "bs58",
"clap",
"indicatif", "indicatif",
"jsonrpc-core", "jsonrpc-core",
"jsonrpc-http-server", "jsonrpc-http-server",
@ -3535,6 +3557,7 @@ dependencies = [
"serde_derive", "serde_derive",
"serde_json", "serde_json",
"solana-account-decoder", "solana-account-decoder",
"solana-clap-utils",
"solana-logger 1.2.30", "solana-logger 1.2.30",
"solana-net-utils", "solana-net-utils",
"solana-sdk 1.2.30", "solana-sdk 1.2.30",
@ -3927,7 +3950,7 @@ dependencies = [
"serde_yaml", "serde_yaml",
"signal-hook", "signal-hook",
"solana-clap-utils", "solana-clap-utils",
"solana-cli", "solana-cli-output",
"solana-ledger", "solana-ledger",
"solana-logger 1.2.30", "solana-logger 1.2.30",
"solana-measure", "solana-measure",
@ -4375,6 +4398,7 @@ dependencies = [
"serde_yaml", "serde_yaml",
"solana-clap-utils", "solana-clap-utils",
"solana-cli-config", "solana-cli-config",
"solana-cli-output",
"solana-client", "solana-client",
"solana-logger 1.2.30", "solana-logger 1.2.30",
"solana-metrics", "solana-metrics",
@ -4604,8 +4628,8 @@ dependencies = [
"humantime 2.0.1", "humantime 2.0.1",
"log 0.4.8", "log 0.4.8",
"solana-clap-utils", "solana-clap-utils",
"solana-cli",
"solana-cli-config", "solana-cli-config",
"solana-cli-output",
"solana-client", "solana-client",
"solana-logger 1.2.30", "solana-logger 1.2.30",
"solana-metrics", "solana-metrics",

View File

@ -6,6 +6,7 @@ members = [
"accounts-bench", "accounts-bench",
"banking-bench", "banking-bench",
"cli-config", "cli-config",
"cli-output",
"client", "client",
"core", "core",
"dos", "dos",

View File

@ -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)
}

View File

@ -11,6 +11,7 @@ use solana_remote_wallet::{
remote_wallet::{maybe_wallet_manager, RemoteWalletError, RemoteWalletManager}, remote_wallet::{maybe_wallet_manager, RemoteWalletError, RemoteWalletManager},
}; };
use solana_sdk::{ use solana_sdk::{
hash::Hash,
pubkey::Pubkey, pubkey::Pubkey,
signature::{ signature::{
keypair_from_seed, keypair_from_seed_phrase_and_passphrase, read_keypair, keypair_from_seed, keypair_from_seed_phrase_and_passphrase, read_keypair,
@ -25,6 +26,81 @@ use std::{
sync::Arc, sync::Arc,
}; };
pub struct SignOnly {
pub blockhash: Hash,
pub present_signers: Vec<(Pubkey, Signature)>,
pub absent_signers: Vec<Pubkey>,
pub bad_signers: Vec<Pubkey>,
}
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> {
presigner_from_pubkey_sigs(pubkey, &self.present_signers)
}
}
pub type CliSigners = Vec<Box<dyn Signer>>;
pub type SignerIndex = usize;
pub struct CliSignerInfo {
pub signers: CliSigners,
}
impl CliSignerInfo {
pub fn index_of(&self, pubkey: Option<Pubkey>) -> Option<usize> {
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<Option<Box<dyn Signer>>>,
matches: &ArgMatches<'_>,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliSignerInfo, Box<dyn error::Error>> {
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<Arc<RemoteWalletManager>>,
) -> Result<Box<dyn Signer>, Box<dyn std::error::Error>> {
signer_from_path(matches, &self.path, &self.arg_name, wallet_manager)
}
}
pub enum KeypairUrl { pub enum KeypairUrl {
Ask, Ask,
Filepath(String), Filepath(String),

View File

@ -24,7 +24,9 @@ impl std::fmt::Debug for DisplayError {
} }
pub mod commitment; pub mod commitment;
pub mod fee_payer;
pub mod input_parsers; pub mod input_parsers;
pub mod input_validators; pub mod input_validators;
pub mod keypair; pub mod keypair;
pub mod nonce;
pub mod offline; pub mod offline;

47
clap-utils/src/nonce.rs Normal file
View File

@ -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))
}
}

View File

@ -1,4 +1,5 @@
use crate::ArgConstant; use crate::{input_validators::*, ArgConstant};
use clap::{App, Arg};
pub const BLOCKHASH_ARG: ArgConstant<'static> = ArgConstant { pub const BLOCKHASH_ARG: ArgConstant<'static> = ArgConstant {
name: "blockhash", name: "blockhash",
@ -17,3 +18,43 @@ pub const SIGNER_ARG: ArgConstant<'static> = ArgConstant {
long: "signer", long: "signer",
help: "Provide a public-key/signature pair for the transaction", 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())
}
}

28
cli-output/Cargo.toml Normal file
View File

@ -0,0 +1,28 @@
[package]
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
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"]

View File

@ -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 chrono::{DateTime, NaiveDateTime, SecondsFormat, Utc};
use console::{style, Emoji}; use console::{style, Emoji};
use inflector::cases::titlecase::to_title_case; use inflector::cases::titlecase::to_title_case;
use serde::Serialize; use serde::{Deserialize, Serialize};
use serde_json::{Map, Value}; use serde_json::{Map, Value};
use solana_clap_utils::keypair::SignOnly;
use solana_client::rpc_response::{ use solana_client::rpc_response::{
RpcAccountBalance, RpcKeyedAccount, RpcSupply, RpcVoteAccountInfo, RpcAccountBalance, RpcKeyedAccount, RpcSupply, RpcVoteAccountInfo,
}; };
use solana_sdk::{ use solana_sdk::{
clock::{self, Epoch, Slot, UnixTimestamp}, clock::{self, Epoch, Slot, UnixTimestamp},
epoch_info::EpochInfo, epoch_info::EpochInfo,
hash::Hash,
native_token::lamports_to_sol, native_token::lamports_to_sol,
pubkey::Pubkey,
signature::Signature,
stake_history::StakeHistoryEntry, stake_history::StakeHistoryEntry,
transaction::Transaction,
}; };
use solana_stake_program::stake_state::{Authorized, Lockup}; use solana_stake_program::stake_state::{Authorized, Lockup};
use solana_vote_program::{ use solana_vote_program::{
authorized_voters::AuthorizedVoters, authorized_voters::AuthorizedVoters,
vote_state::{BlockTimestamp, Lockout}, vote_state::{BlockTimestamp, Lockout},
}; };
use std::{collections::BTreeMap, fmt, time::Duration}; use std::{collections::BTreeMap, fmt, str::FromStr, time::Duration};
static WARNING: Emoji = Emoji("⚠️", "!"); static WARNING: Emoji = Emoji("⚠️", "!");
@ -1086,3 +1091,149 @@ impl fmt::Display for CliFees {
Ok(()) Ok(())
} }
} }
pub fn return_signers(
tx: &Transaction,
output_format: &OutputFormat,
) -> Result<String, Box<dyn std::error::Error>> {
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::<Hash>().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<Pubkey> = 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<Pubkey> = 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<Pubkey, SignerError> {
Ok(self.pubkey)
}
fn try_sign_message(&self, _message: &[u8]) -> Result<Signature, SignerError> {
Ok(Signature::new(&[1u8; 64]))
}
}
let present: Box<dyn Signer> = Box::new(keypair_from_seed(&[2u8; 32]).unwrap());
let absent: Box<dyn Signer> = Box::new(NullSigner::new(&Pubkey::new(&[3u8; 32])));
let bad: Box<dyn Signer> = 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());
}
}

View File

@ -1,4 +1,3 @@
use crate::cli::SettingType;
use console::style; use console::style;
use indicatif::{ProgressBar, ProgressStyle}; use indicatif::{ProgressBar, ProgressStyle};
use solana_sdk::{ use solana_sdk::{
@ -8,6 +7,24 @@ use solana_sdk::{
use solana_transaction_status::UiTransactionStatusMeta; use solana_transaction_status::UiTransactionStatusMeta;
use std::{fmt, io}; 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" // Pretty print a "name value"
pub fn println_name_value(name: &str, value: &str) { pub fn println_name_value(name: &str, value: &str) {
let styled_value = if value == "" { 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) 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( pub fn println_signers(
blockhash: &Hash, blockhash: &Hash,
signers: &[String], signers: &[String],

3
cli-output/src/lib.rs Normal file
View File

@ -0,0 +1,3 @@
mod cli_output;
pub mod display;
pub use cli_output::*;

View File

@ -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-budget-program = { path = "../programs/budget", version = "1.2.30" }
solana-clap-utils = { path = "../clap-utils", 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-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-client = { path = "../client", version = "1.2.30" }
solana-config-program = { path = "../programs/config", version = "1.2.30" } solana-config-program = { path = "../programs/config", version = "1.2.30" }
solana-faucet = { path = "../faucet", version = "1.2.30" } solana-faucet = { path = "../faucet", version = "1.2.30" }

View File

@ -1,14 +1,5 @@
use crate::{ use crate::{
checks::*, checks::*, cluster_query::*, nonce::*, spend_utils::*, stake::*, validator_info::*, vote::*,
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::*,
}; };
use chrono::prelude::*; use chrono::prelude::*;
use clap::{value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand}; 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_account_decoder::{UiAccount, UiAccountEncoding};
use solana_budget_program::budget_instruction::{self, BudgetError}; use solana_budget_program::budget_instruction::{self, BudgetError};
use solana_clap_utils::{ use solana_clap_utils::{
self,
commitment::{commitment_arg_with_default, COMMITMENT_ARG}, commitment::{commitment_arg_with_default, COMMITMENT_ARG},
fee_payer::{fee_payer_arg, FEE_PAYER_ARG},
input_parsers::*, input_parsers::*,
input_validators::*, input_validators::*,
keypair::signer_from_path, keypair::*,
offline::SIGN_ONLY_ARG, nonce::*,
ArgConstant, 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::{ use solana_client::{
blockhash_query::BlockhashQuery,
client_error::{ClientError, ClientErrorKind, Result as ClientResult}, client_error::{ClientError, ClientErrorKind, Result as ClientResult},
nonce_utils,
rpc_client::RpcClient, rpc_client::RpcClient,
rpc_config::{RpcLargestAccountsFilter, RpcSendTransactionConfig}, rpc_config::{RpcLargestAccountsFilter, RpcSendTransactionConfig},
rpc_response::{Response, RpcKeyedAccount}, rpc_response::{Response, RpcKeyedAccount},
@ -41,12 +42,10 @@ use solana_sdk::{
clock::{Epoch, Slot, DEFAULT_TICKS_PER_SECOND}, clock::{Epoch, Slot, DEFAULT_TICKS_PER_SECOND},
commitment_config::CommitmentConfig, commitment_config::CommitmentConfig,
decode_error::DecodeError, decode_error::DecodeError,
fee_calculator::FeeCalculator,
hash::Hash, hash::Hash,
instruction::InstructionError, instruction::InstructionError,
loader_instruction, loader_instruction,
message::Message, message::Message,
native_token::lamports_to_sol,
pubkey::{Pubkey, MAX_SEED_LEN}, pubkey::{Pubkey, MAX_SEED_LEN},
signature::{Keypair, Signature, Signer, SignerError}, signature::{Keypair, Signature, Signer, SignerError},
signers::Signers, signers::Signers,
@ -73,96 +72,8 @@ use std::{
use thiserror::Error; use thiserror::Error;
use url::Url; use url::Url;
pub type CliSigners = Vec<Box<dyn Signer>>;
pub type SignerIndex = usize;
pub(crate) struct CliSignerInfo {
pub signers: CliSigners,
}
impl CliSignerInfo {
pub(crate) fn index_of(&self, pubkey: Option<Pubkey>) -> Option<usize> {
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<Option<Box<dyn Signer>>>,
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliSignerInfo, Box<dyn error::Error>> {
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 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<Keypair> 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)] #[derive(Default, Debug, PartialEq)]
pub struct PayCommand { pub struct PayCommand {
pub amount: SpendAmount, pub amount: SpendAmount,
@ -475,7 +386,7 @@ pub enum CliError {
#[error("insufficient funds for spend ({0} SOL) and fee ({1} SOL)")] #[error("insufficient funds for spend ({0} SOL) and fee ({1} SOL)")]
InsufficientFundsForSpendAndFee(f64, f64), InsufficientFundsForSpendAndFee(f64, f64),
#[error(transparent)] #[error(transparent)]
InvalidNonce(CliNonceError), InvalidNonce(nonce_utils::Error),
#[error("dynamic program error: {0}")] #[error("dynamic program error: {0}")]
DynamicProgramError(String), DynamicProgramError(String),
#[error("rpc request error: {0}")] #[error("rpc request error: {0}")]
@ -490,10 +401,10 @@ impl From<Box<dyn error::Error>> for CliError {
} }
} }
impl From<CliNonceError> for CliError { impl From<nonce_utils::Error> for CliError {
fn from(error: CliNonceError) -> Self { fn from(error: nonce_utils::Error) -> Self {
match error { match error {
CliNonceError::Client(client_error) => Self::RpcRequestError(client_error), nonce_utils::Error::Client(client_error) => Self::RpcRequestError(client_error),
_ => Self::InvalidNonce(error), _ => Self::InvalidNonce(error),
} }
} }
@ -613,7 +524,7 @@ impl Default for CliConfig<'_> {
pub fn parse_command( pub fn parse_command(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer_path: &str, default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, Box<dyn error::Error>> { ) -> Result<CliCommandInfo, Box<dyn error::Error>> {
let response = match matches.subcommand() { let response = match matches.subcommand() {
@ -628,7 +539,7 @@ pub fn parse_command(
signers: vec![], signers: vec![],
}), }),
("create-address-with-seed", Some(matches)) => { ("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 { ("fees", Some(_matches)) => Ok(CliCommandInfo {
command: CliCommand::Fees, command: CliCommand::Fees,
@ -650,7 +561,7 @@ pub fn parse_command(
command: CliCommand::LeaderSchedule, command: CliCommand::LeaderSchedule,
signers: vec![], 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 { ("live-slots", Some(_matches)) => Ok(CliCommandInfo {
command: CliCommand::LiveSlots, command: CliCommand::LiveSlots,
signers: vec![], signers: vec![],
@ -667,99 +578,87 @@ pub fn parse_command(
} }
// Nonce Commands // Nonce Commands
("authorize-nonce-account", Some(matches)) => { ("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)) => { ("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), ("nonce", Some(matches)) => parse_get_nonce(matches, wallet_manager),
("new-nonce", Some(matches)) => { ("new-nonce", Some(matches)) => parse_new_nonce(matches, default_signer, wallet_manager),
parse_new_nonce(matches, default_signer_path, wallet_manager)
}
("nonce-account", Some(matches)) => parse_show_nonce_account(matches, wallet_manager), ("nonce-account", Some(matches)) => parse_show_nonce_account(matches, wallet_manager),
("withdraw-from-nonce-account", Some(matches)) => { ("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 // Program Deployment
("deploy", Some(matches)) => Ok(CliCommandInfo { ("deploy", Some(matches)) => Ok(CliCommandInfo {
command: CliCommand::Deploy(matches.value_of("program_location").unwrap().to_string()), command: CliCommand::Deploy(matches.value_of("program_location").unwrap().to_string()),
signers: vec![signer_from_path( signers: vec![default_signer.signer_from_path(matches, wallet_manager)?],
matches,
default_signer_path,
"keypair",
wallet_manager,
)?],
}), }),
// Stake Commands // Stake Commands
("create-stake-account", Some(matches)) => { ("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)) => { ("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)) => { ("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)) => { ("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)) => { ("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)) => { ("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)) => { ("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)) => { ("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-account", Some(matches)) => parse_show_stake_account(matches, wallet_manager),
("stake-history", Some(matches)) => parse_show_stake_history(matches), ("stake-history", Some(matches)) => parse_show_stake_history(matches),
// Validator Info Commands // Validator Info Commands
("validator-info", Some(matches)) => match matches.subcommand() { ("validator-info", Some(matches)) => match matches.subcommand() {
("publish", Some(matches)) => { ("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), ("get", Some(matches)) => parse_get_validator_info_command(matches),
_ => unreachable!(), _ => unreachable!(),
}, },
// Vote Commands // Vote Commands
("create-vote-account", Some(matches)) => { ("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)) => { ("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)) => { ("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( ("vote-authorize-voter", Some(matches)) => parse_vote_authorize(
matches, matches,
default_signer_path, default_signer,
wallet_manager, wallet_manager,
VoteAuthorize::Voter, VoteAuthorize::Voter,
), ),
("vote-authorize-withdrawer", Some(matches)) => parse_vote_authorize( ("vote-authorize-withdrawer", Some(matches)) => parse_vote_authorize(
matches, matches,
default_signer_path, default_signer,
wallet_manager, wallet_manager,
VoteAuthorize::Withdrawer, VoteAuthorize::Withdrawer,
), ),
("vote-account", Some(matches)) => parse_vote_get_account_command(matches, wallet_manager), ("vote-account", Some(matches)) => parse_vote_get_account_command(matches, wallet_manager),
("withdraw-from-vote-account", Some(matches)) => { ("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 // Wallet Commands
("address", Some(matches)) => Ok(CliCommandInfo { ("address", Some(matches)) => Ok(CliCommandInfo {
command: CliCommand::Address, command: CliCommand::Address,
signers: vec![signer_from_path( signers: vec![default_signer.signer_from_path(matches, wallet_manager)?],
matches,
default_signer_path,
"keypair",
wallet_manager,
)?],
}), }),
("airdrop", Some(matches)) => { ("airdrop", Some(matches)) => {
let faucet_port = matches let faucet_port = matches
@ -787,12 +686,7 @@ pub fn parse_command(
let signers = if pubkey.is_some() { let signers = if pubkey.is_some() {
vec![] vec![]
} else { } else {
vec![signer_from_path( vec![default_signer.signer_from_path(matches, wallet_manager)?]
matches,
default_signer_path,
"keypair",
wallet_manager,
)?]
}; };
let lamports = lamports_of_sol(matches, "amount").unwrap(); let lamports = lamports_of_sol(matches, "amount").unwrap();
Ok(CliCommandInfo { Ok(CliCommandInfo {
@ -811,12 +705,7 @@ pub fn parse_command(
let signers = if pubkey.is_some() { let signers = if pubkey.is_some() {
vec![] vec![]
} else { } else {
vec![signer_from_path( vec![default_signer.signer_from_path(matches, wallet_manager)?]
matches,
default_signer_path,
"keypair",
wallet_manager,
)?]
}; };
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::Balance { command: CliCommand::Balance {
@ -829,8 +718,7 @@ pub fn parse_command(
} }
("cancel", Some(matches)) => { ("cancel", Some(matches)) => {
let process_id = value_of(matches, "process_id").unwrap(); let process_id = value_of(matches, "process_id").unwrap();
let default_signer = let default_signer = default_signer.signer_from_path(matches, wallet_manager)?;
signer_from_path(matches, default_signer_path, "keypair", wallet_manager)?;
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::Cancel(process_id), command: CliCommand::Cancel(process_id),
@ -892,12 +780,8 @@ pub fn parse_command(
if nonce_account.is_some() { if nonce_account.is_some() {
bulk_signers.push(nonce_authority); bulk_signers.push(nonce_authority);
} }
let signer_info = generate_unique_signers( let signer_info =
bulk_signers, default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
matches,
default_signer_path,
wallet_manager,
)?;
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::Pay(PayCommand { command: CliCommand::Pay(PayCommand {
@ -940,8 +824,7 @@ pub fn parse_command(
let to = value_of(matches, "to").unwrap(); let to = value_of(matches, "to").unwrap();
let process_id = value_of(matches, "process_id").unwrap(); let process_id = value_of(matches, "process_id").unwrap();
let default_signer = let default_signer = default_signer.signer_from_path(matches, wallet_manager)?;
signer_from_path(matches, default_signer_path, "keypair", wallet_manager)?;
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::Witness(to, process_id), command: CliCommand::Witness(to, process_id),
@ -962,8 +845,7 @@ pub fn parse_command(
} else { } else {
Utc::now() Utc::now()
}; };
let default_signer = let default_signer = default_signer.signer_from_path(matches, wallet_manager)?;
signer_from_path(matches, default_signer_path, "keypair", wallet_manager)?;
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::TimeElapsed(to, process_id, dt), command: CliCommand::TimeElapsed(to, process_id, dt),
@ -988,12 +870,8 @@ pub fn parse_command(
bulk_signers.push(nonce_authority); bulk_signers.push(nonce_authority);
} }
let signer_info = generate_unique_signers( let signer_info =
bulk_signers, default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
matches,
default_signer_path,
wallet_manager,
)?;
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::Transfer { command: CliCommand::Transfer {
@ -1024,66 +902,16 @@ pub fn parse_command(
pub type ProcessResult = Result<String, Box<dyn std::error::Error>>; pub type ProcessResult = Result<String, Box<dyn std::error::Error>>;
pub fn get_blockhash_and_fee_calculator(
rpc_client: &RpcClient,
sign_only: bool,
blockhash: Option<Hash>,
) -> Result<(Hash, FeeCalculator), Box<dyn std::error::Error>> {
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( pub fn parse_create_address_with_seed(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer_path: &str, default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let from_pubkey = pubkey_of_signer(matches, "from", wallet_manager)?; let from_pubkey = pubkey_of_signer(matches, "from", wallet_manager)?;
let signers = if from_pubkey.is_some() { let signers = if from_pubkey.is_some() {
vec![] vec![]
} else { } else {
vec![signer_from_path( vec![default_signer.signer_from_path(matches, wallet_manager)?]
matches,
default_signer_path,
"keypair",
wallet_manager,
)?]
}; };
let program_id = match matches.value_of("program_id").unwrap() { let program_id = match matches.value_of("program_id").unwrap() {
@ -1508,7 +1336,7 @@ fn process_pay(
if sign_only { if sign_only {
tx.try_partial_sign(&config.signers, blockhash)?; tx.try_partial_sign(&config.signers, blockhash)?;
return_signers(&tx, &config) return_signers(&tx, &config.output_format)
} else { } else {
if let Some(nonce_account) = &nonce_account { if let Some(nonce_account) = &nonce_account {
let nonce_account = rpc_client.get_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); let mut tx = Transaction::new_unsigned(message);
if sign_only { if sign_only {
tx.try_partial_sign(&[config.signers[0], &contract_state], blockhash)?; tx.try_partial_sign(&[config.signers[0], &contract_state], blockhash)?;
return_signers(&tx, &config) return_signers(&tx, &config.output_format)
} else { } else {
tx.try_sign(&[config.signers[0], &contract_state], blockhash)?; tx.try_sign(&[config.signers[0], &contract_state], blockhash)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx); 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); let mut tx = Transaction::new_unsigned(message);
if sign_only { if sign_only {
tx.try_partial_sign(&[config.signers[0], &contract_state], blockhash)?; tx.try_partial_sign(&[config.signers[0], &contract_state], blockhash)?;
return_signers(&tx, &config) return_signers(&tx, &config.output_format)
} else { } else {
tx.try_sign(&[config.signers[0], &contract_state], blockhash)?; tx.try_sign(&[config.signers[0], &contract_state], blockhash)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx); let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
@ -1707,7 +1535,7 @@ fn process_transfer(
if sign_only { if sign_only {
tx.try_partial_sign(&config.signers, recent_blockhash)?; tx.try_partial_sign(&config.signers, recent_blockhash)?;
return_signers(&tx, &config) return_signers(&tx, &config.output_format)
} else { } else {
if let Some(nonce_account) = &nonce_account { if let Some(nonce_account) = &nonce_account {
let nonce_account = rpc_client.get_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> { pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, 'v> {
App::new(name) App::new(name)
.about(about) .about(about)
@ -2667,8 +2473,7 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
.takes_value(false), .takes_value(false),
) )
.offline_args() .offline_args()
.arg(nonce_arg()) .nonce_args()
.arg(nonce_authority_arg()),
) )
.subcommand( .subcommand(
SubCommand::with_name("resolve-signer") 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"), .help("Return signature immediately after submitting the transaction, instead of waiting for confirmations"),
) )
.offline_args() .offline_args()
.arg(nonce_arg()) .nonce_args()
.arg(nonce_authority_arg())
.arg(fee_payer_arg()), .arg(fee_payer_arg()),
) )
.subcommand( .subcommand(
@ -2798,12 +2602,10 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
mod tests { mod tests {
use super::*; use super::*;
use serde_json::Value; use serde_json::Value;
use solana_client::mock_sender::SIGNATURE; use solana_client::{blockhash_query, mock_sender::SIGNATURE};
use solana_sdk::{ use solana_sdk::{
pubkey::Pubkey, pubkey::Pubkey,
signature::{ signature::{keypair_from_seed, read_keypair_file, write_keypair_file, Presigner},
keypair_from_seed, read_keypair_file, write_keypair_file, NullSigner, Presigner,
},
transaction::TransactionError, transaction::TransactionError,
}; };
use std::path::PathBuf; use std::path::PathBuf;
@ -2830,13 +2632,19 @@ mod tests {
let default_keypair_file = make_tmp_path("keypair_file"); let default_keypair_file = make_tmp_path("keypair_file");
write_keypair_file(&default_keypair, &default_keypair_file).unwrap(); write_keypair_file(&default_keypair, &default_keypair_file).unwrap();
let signer_info = let default_signer = DefaultSigner {
generate_unique_signers(vec![], &matches, &default_keypair_file, &mut None).unwrap(); 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); assert_eq!(signer_info.signers.len(), 0);
let signer_info = let signer_info = default_signer
generate_unique_signers(vec![None, None], &matches, &default_keypair_file, &mut None) .generate_unique_signers(vec![None, None], &matches, &mut None)
.unwrap(); .unwrap();
assert_eq!(signer_info.signers.len(), 1); assert_eq!(signer_info.signers.len(), 1);
assert_eq!(signer_info.index_of(None), Some(0)); assert_eq!(signer_info.index_of(None), Some(0));
assert_eq!(signer_info.index_of(Some(Pubkey::new_rand())), None); 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 = keypair_from_seed(&[1u8; 32]).unwrap();
let keypair0_clone_pubkey = keypair0.pubkey(); let keypair0_clone_pubkey = keypair0.pubkey();
let signers = vec![None, Some(keypair0.into()), Some(keypair0_clone.into())]; let signers = vec![None, Some(keypair0.into()), Some(keypair0_clone.into())];
let signer_info = let signer_info = default_signer
generate_unique_signers(signers, &matches, &default_keypair_file, &mut None).unwrap(); .generate_unique_signers(signers, &matches, &mut None)
.unwrap();
assert_eq!(signer_info.signers.len(), 2); assert_eq!(signer_info.signers.len(), 2);
assert_eq!(signer_info.index_of(None), Some(0)); assert_eq!(signer_info.index_of(None), Some(0));
assert_eq!(signer_info.index_of(Some(keypair0_pubkey)), Some(1)); assert_eq!(signer_info.index_of(Some(keypair0_pubkey)), Some(1));
@ -2857,8 +2666,9 @@ mod tests {
let keypair0_pubkey = keypair0.pubkey(); let keypair0_pubkey = keypair0.pubkey();
let keypair0_clone = keypair_from_seed(&[1u8; 32]).unwrap(); let keypair0_clone = keypair_from_seed(&[1u8; 32]).unwrap();
let signers = vec![Some(keypair0.into()), Some(keypair0_clone.into())]; let signers = vec![Some(keypair0.into()), Some(keypair0_clone.into())];
let signer_info = let signer_info = default_signer
generate_unique_signers(signers, &matches, &default_keypair_file, &mut None).unwrap(); .generate_unique_signers(signers, &matches, &mut None)
.unwrap();
assert_eq!(signer_info.signers.len(), 1); assert_eq!(signer_info.signers.len(), 1);
assert_eq!(signer_info.index_of(Some(keypair0_pubkey)), Some(0)); assert_eq!(signer_info.index_of(Some(keypair0_pubkey)), Some(0));
@ -2878,8 +2688,9 @@ mod tests {
Some(presigner1.into()), Some(presigner1.into()),
Some(keypair1.into()), Some(keypair1.into()),
]; ];
let signer_info = let signer_info = default_signer
generate_unique_signers(signers, &matches, &default_keypair_file, &mut None).unwrap(); .generate_unique_signers(signers, &matches, &mut None)
.unwrap();
assert_eq!(signer_info.signers.len(), 2); 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(keypair0_pubkey)), Some(0));
assert_eq!(signer_info.index_of(Some(keypair1_pubkey)), Some(1)); assert_eq!(signer_info.index_of(Some(keypair1_pubkey)), Some(1));
@ -2900,13 +2711,21 @@ mod tests {
let witness1_string = format!("{}", witness1); let witness1_string = format!("{}", witness1);
let dt = Utc.ymd(2018, 9, 19).and_hms(17, 30, 59); 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 // Test Airdrop Subcommand
let test_airdrop = let test_airdrop =
test_commands test_commands
.clone() .clone()
.get_matches_from(vec!["test", "airdrop", "50", &pubkey_string]); .get_matches_from(vec!["test", "airdrop", "50", &pubkey_string]);
assert_eq!( assert_eq!(
parse_command(&test_airdrop, "", &mut None).unwrap(), parse_command(&test_airdrop, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::Airdrop { command: CliCommand::Airdrop {
faucet_host: None, faucet_host: None,
@ -2919,17 +2738,13 @@ mod tests {
); );
// Test Balance Subcommand, incl pubkey and keypair-file inputs // 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![ let test_balance = test_commands.clone().get_matches_from(vec![
"test", "test",
"balance", "balance",
&keypair.pubkey().to_string(), &keypair.pubkey().to_string(),
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_balance, "", &mut None).unwrap(), parse_command(&test_balance, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::Balance { command: CliCommand::Balance {
pubkey: Some(keypair.pubkey()), pubkey: Some(keypair.pubkey()),
@ -2946,7 +2761,7 @@ mod tests {
"--lamports", "--lamports",
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_balance, "", &mut None).unwrap(), parse_command(&test_balance, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::Balance { command: CliCommand::Balance {
pubkey: Some(keypair.pubkey()), pubkey: Some(keypair.pubkey()),
@ -2961,7 +2776,7 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "balance", "--lamports"]); .get_matches_from(vec!["test", "balance", "--lamports"]);
assert_eq!( assert_eq!(
parse_command(&test_balance, &keypair_file, &mut None).unwrap(), parse_command(&test_balance, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::Balance { command: CliCommand::Balance {
pubkey: None, pubkey: None,
@ -2978,7 +2793,7 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "cancel", &pubkey_string]); .get_matches_from(vec!["test", "cancel", &pubkey_string]);
assert_eq!( assert_eq!(
parse_command(&test_cancel, &keypair_file, &mut None).unwrap(), parse_command(&test_cancel, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::Cancel(pubkey), command: CliCommand::Cancel(pubkey),
signers: vec![read_keypair_file(&keypair_file).unwrap().into()], signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
@ -2993,7 +2808,7 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "confirm", &signature_string]); .get_matches_from(vec!["test", "confirm", &signature_string]);
assert_eq!( assert_eq!(
parse_command(&test_confirm, "", &mut None).unwrap(), parse_command(&test_confirm, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::Confirm(signature), command: CliCommand::Confirm(signature),
signers: vec![], signers: vec![],
@ -3002,7 +2817,7 @@ mod tests {
let test_bad_signature = test_commands let test_bad_signature = test_commands
.clone() .clone()
.get_matches_from(vec!["test", "confirm", "deadbeef"]); .get_matches_from(vec!["test", "confirm", "deadbeef"]);
assert!(parse_command(&test_bad_signature, "", &mut None).is_err()); assert!(parse_command(&test_bad_signature, &default_signer, &mut None).is_err());
// Test CreateAddressWithSeed // Test CreateAddressWithSeed
let from_pubkey = Some(Pubkey::new_rand()); let from_pubkey = Some(Pubkey::new_rand());
@ -3021,7 +2836,7 @@ mod tests {
&from_str, &from_str,
]); ]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::CreateAddressWithSeed { command: CliCommand::CreateAddressWithSeed {
from_pubkey, from_pubkey,
@ -3039,7 +2854,7 @@ mod tests {
"STAKE", "STAKE",
]); ]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::CreateAddressWithSeed { command: CliCommand::CreateAddressWithSeed {
from_pubkey: None, from_pubkey: None,
@ -3056,7 +2871,7 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "deploy", "/Users/test/program.o"]); .get_matches_from(vec!["test", "deploy", "/Users/test/program.o"]);
assert_eq!( assert_eq!(
parse_command(&test_deploy, &keypair_file, &mut None).unwrap(), parse_command(&test_deploy, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::Deploy("/Users/test/program.o".to_string()), command: CliCommand::Deploy("/Users/test/program.o".to_string()),
signers: vec![read_keypair_file(&keypair_file).unwrap().into()], signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
@ -3069,7 +2884,7 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "resolve-signer", &keypair_file]); .get_matches_from(vec!["test", "resolve-signer", &keypair_file]);
assert_eq!( assert_eq!(
parse_command(&test_resolve_signer, "", &mut None).unwrap(), parse_command(&test_resolve_signer, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::ResolveSigner(Some(keypair_file.clone())), command: CliCommand::ResolveSigner(Some(keypair_file.clone())),
signers: vec![], signers: vec![],
@ -3081,7 +2896,7 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "resolve-signer", &pubkey_string]); .get_matches_from(vec!["test", "resolve-signer", &pubkey_string]);
assert_eq!( assert_eq!(
parse_command(&test_resolve_signer, "", &mut None).unwrap(), parse_command(&test_resolve_signer, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::ResolveSigner(Some(pubkey.to_string())), command: CliCommand::ResolveSigner(Some(pubkey.to_string())),
signers: vec![], signers: vec![],
@ -3094,7 +2909,7 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "pay", &pubkey_string, "50"]); .get_matches_from(vec!["test", "pay", &pubkey_string, "50"]);
assert_eq!( assert_eq!(
parse_command(&test_pay, &keypair_file, &mut None).unwrap(), parse_command(&test_pay, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::Pay(PayCommand { command: CliCommand::Pay(PayCommand {
amount: SpendAmount::Some(50_000_000_000), amount: SpendAmount::Some(50_000_000_000),
@ -3117,7 +2932,7 @@ mod tests {
&witness1_string, &witness1_string,
]); ]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::Pay(PayCommand { command: CliCommand::Pay(PayCommand {
amount: SpendAmount::Some(50_000_000_000), amount: SpendAmount::Some(50_000_000_000),
@ -3137,7 +2952,7 @@ mod tests {
&witness0_string, &witness0_string,
]); ]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::Pay(PayCommand { command: CliCommand::Pay(PayCommand {
amount: SpendAmount::Some(50_000_000_000), amount: SpendAmount::Some(50_000_000_000),
@ -3161,7 +2976,7 @@ mod tests {
&witness0_string, &witness0_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_pay_timestamp, &keypair_file, &mut None).unwrap(), parse_command(&test_pay_timestamp, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::Pay(PayCommand { command: CliCommand::Pay(PayCommand {
amount: SpendAmount::Some(50_000_000_000), amount: SpendAmount::Some(50_000_000_000),
@ -3187,7 +3002,7 @@ mod tests {
"--sign-only", "--sign-only",
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_pay, &keypair_file, &mut None).unwrap(), parse_command(&test_pay, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::Pay(PayCommand { command: CliCommand::Pay(PayCommand {
amount: SpendAmount::Some(50_000_000_000), amount: SpendAmount::Some(50_000_000_000),
@ -3210,7 +3025,7 @@ mod tests {
&blockhash_string, &blockhash_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_pay, &keypair_file, &mut None).unwrap(), parse_command(&test_pay, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::Pay(PayCommand { command: CliCommand::Pay(PayCommand {
amount: SpendAmount::Some(50_000_000_000), amount: SpendAmount::Some(50_000_000_000),
@ -3239,7 +3054,7 @@ mod tests {
&pubkey_string, &pubkey_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_pay, &keypair_file, &mut None).unwrap(), parse_command(&test_pay, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::Pay(PayCommand { command: CliCommand::Pay(PayCommand {
amount: SpendAmount::Some(50_000_000_000), amount: SpendAmount::Some(50_000_000_000),
@ -3272,7 +3087,7 @@ mod tests {
&keypair_file, &keypair_file,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_pay, &keypair_file, &mut None).unwrap(), parse_command(&test_pay, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::Pay(PayCommand { command: CliCommand::Pay(PayCommand {
amount: SpendAmount::Some(50_000_000_000), amount: SpendAmount::Some(50_000_000_000),
@ -3310,7 +3125,7 @@ mod tests {
&signer_arg, &signer_arg,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_pay, &keypair_file, &mut None).unwrap(), parse_command(&test_pay, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::Pay(PayCommand { command: CliCommand::Pay(PayCommand {
amount: SpendAmount::Some(50_000_000_000), amount: SpendAmount::Some(50_000_000_000),
@ -3348,7 +3163,7 @@ mod tests {
"--signer", "--signer",
&signer_arg, &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 // Test Send-Signature Subcommand
let test_send_signature = test_commands.clone().get_matches_from(vec![ let test_send_signature = test_commands.clone().get_matches_from(vec![
@ -3358,7 +3173,7 @@ mod tests {
&pubkey_string, &pubkey_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_send_signature, &keypair_file, &mut None).unwrap(), parse_command(&test_send_signature, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::Witness(pubkey, pubkey), command: CliCommand::Witness(pubkey, pubkey),
signers: vec![read_keypair_file(&keypair_file).unwrap().into()], signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
@ -3379,7 +3194,7 @@ mod tests {
&witness1_string, &witness1_string,
]); ]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::Pay(PayCommand { command: CliCommand::Pay(PayCommand {
amount: SpendAmount::Some(50_000_000_000), amount: SpendAmount::Some(50_000_000_000),
@ -3403,7 +3218,7 @@ mod tests {
"2018-09-19T17:30:59", "2018-09-19T17:30:59",
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_send_timestamp, &keypair_file, &mut None).unwrap(), parse_command(&test_send_timestamp, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::TimeElapsed(pubkey, pubkey, dt), command: CliCommand::TimeElapsed(pubkey, pubkey, dt),
signers: vec![read_keypair_file(&keypair_file).unwrap().into()], signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
@ -3417,7 +3232,7 @@ mod tests {
"--date", "--date",
"20180919T17:30:59", "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] #[test]
@ -3807,6 +3622,10 @@ mod tests {
let default_keypair = Keypair::new(); let default_keypair = Keypair::new();
let default_keypair_file = make_tmp_path("keypair_file"); let default_keypair_file = make_tmp_path("keypair_file");
write_keypair_file(&default_keypair, &default_keypair_file).unwrap(); 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 //Test Transfer Subcommand, SOL
let from_keypair = keypair_from_seed(&[0u8; 32]).unwrap(); let from_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
@ -3819,7 +3638,7 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "transfer", &to_string, "42"]); .get_matches_from(vec!["test", "transfer", &to_string, "42"]);
assert_eq!( assert_eq!(
parse_command(&test_transfer, &default_keypair_file, &mut None).unwrap(), parse_command(&test_transfer, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::Transfer { command: CliCommand::Transfer {
amount: SpendAmount::Some(42_000_000_000), amount: SpendAmount::Some(42_000_000_000),
@ -3841,7 +3660,7 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "transfer", &to_string, "ALL"]); .get_matches_from(vec!["test", "transfer", &to_string, "ALL"]);
assert_eq!( assert_eq!(
parse_command(&test_transfer, &default_keypair_file, &mut None).unwrap(), parse_command(&test_transfer, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::Transfer { command: CliCommand::Transfer {
amount: SpendAmount::All, amount: SpendAmount::All,
@ -3867,7 +3686,7 @@ mod tests {
"42", "42",
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_transfer, &default_keypair_file, &mut None).unwrap(), parse_command(&test_transfer, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::Transfer { command: CliCommand::Transfer {
amount: SpendAmount::Some(42_000_000_000), amount: SpendAmount::Some(42_000_000_000),
@ -3897,7 +3716,7 @@ mod tests {
"--sign-only", "--sign-only",
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_transfer, &default_keypair_file, &mut None).unwrap(), parse_command(&test_transfer, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::Transfer { command: CliCommand::Transfer {
amount: SpendAmount::Some(42_000_000_000), amount: SpendAmount::Some(42_000_000_000),
@ -3932,7 +3751,7 @@ mod tests {
&blockhash_string, &blockhash_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_transfer, &default_keypair_file, &mut None).unwrap(), parse_command(&test_transfer, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::Transfer { command: CliCommand::Transfer {
amount: SpendAmount::Some(42_000_000_000), amount: SpendAmount::Some(42_000_000_000),
@ -3971,7 +3790,7 @@ mod tests {
&nonce_authority_file, &nonce_authority_file,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_transfer, &default_keypair_file, &mut None).unwrap(), parse_command(&test_transfer, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::Transfer { command: CliCommand::Transfer {
amount: SpendAmount::Some(42_000_000_000), 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<Pubkey, SignerError> {
Ok(self.pubkey)
}
fn try_sign_message(&self, _message: &[u8]) -> Result<Signature, SignerError> {
Ok(Signature::new(&[1u8; 64]))
}
}
let mut config = CliConfig::default();
config.output_format = OutputFormat::JsonCompact;
let present: Box<dyn Signer> = Box::new(keypair_from_seed(&[2u8; 32]).unwrap());
let absent: Box<dyn Signer> = Box::new(NullSigner::new(&Pubkey::new(&[3u8; 32])));
let bad: Box<dyn Signer> = 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());
}
} }

View File

@ -1,7 +1,5 @@
use crate::{ use crate::{
cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult}, 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}, spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
}; };
use clap::{value_t, value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand}; 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}, commitment::{commitment_arg, COMMITMENT_ARG},
input_parsers::*, input_parsers::*,
input_validators::*, input_validators::*,
keypair::signer_from_path, keypair::DefaultSigner,
};
use solana_cli_output::{
display::{new_spinner_progress_bar, println_name_value},
*,
}; };
use solana_client::{ use solana_client::{
pubsub_client::{PubsubClient, SlotInfoMessage}, pubsub_client::{PubsubClient, SlotInfoMessage},
@ -306,7 +308,7 @@ pub fn parse_catchup(
pub fn parse_cluster_ping( pub fn parse_cluster_ping(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer_path: &str, default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let lamports = value_t_or_exit!(matches, "lamports", u64); let lamports = value_t_or_exit!(matches, "lamports", u64);
@ -326,12 +328,7 @@ pub fn parse_cluster_ping(
timeout, timeout,
commitment_config, commitment_config,
}, },
signers: vec![signer_from_path( signers: vec![default_signer.signer_from_path(matches, wallet_manager)?],
matches,
default_signer_path,
"keypair",
wallet_manager,
)?],
}) })
} }
@ -1342,12 +1339,16 @@ mod tests {
let default_keypair = Keypair::new(); let default_keypair = Keypair::new();
let (default_keypair_file, mut tmp_file) = make_tmp_file(); let (default_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap(); 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 let test_cluster_version = test_commands
.clone() .clone()
.get_matches_from(vec!["test", "cluster-date"]); .get_matches_from(vec!["test", "cluster-date"]);
assert_eq!( assert_eq!(
parse_command(&test_cluster_version, &default_keypair_file, &mut None).unwrap(), parse_command(&test_cluster_version, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::ClusterDate, command: CliCommand::ClusterDate,
signers: vec![], signers: vec![],
@ -1358,7 +1359,7 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "cluster-version"]); .get_matches_from(vec!["test", "cluster-version"]);
assert_eq!( assert_eq!(
parse_command(&test_cluster_version, &default_keypair_file, &mut None).unwrap(), parse_command(&test_cluster_version, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::ClusterVersion, command: CliCommand::ClusterVersion,
signers: vec![], signers: vec![],
@ -1367,7 +1368,7 @@ mod tests {
let test_fees = test_commands.clone().get_matches_from(vec!["test", "fees"]); let test_fees = test_commands.clone().get_matches_from(vec!["test", "fees"]);
assert_eq!( assert_eq!(
parse_command(&test_fees, &default_keypair_file, &mut None).unwrap(), parse_command(&test_fees, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::Fees, command: CliCommand::Fees,
signers: vec![], signers: vec![],
@ -1380,7 +1381,7 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "block-time", &slot.to_string()]); .get_matches_from(vec!["test", "block-time", &slot.to_string()]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::GetBlockTime { slot: Some(slot) }, command: CliCommand::GetBlockTime { slot: Some(slot) },
signers: vec![], signers: vec![],
@ -1391,7 +1392,7 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "epoch-info"]); .get_matches_from(vec!["test", "epoch-info"]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::GetEpochInfo { command: CliCommand::GetEpochInfo {
commitment_config: CommitmentConfig::recent(), commitment_config: CommitmentConfig::recent(),
@ -1404,7 +1405,7 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "genesis-hash"]); .get_matches_from(vec!["test", "genesis-hash"]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::GetGenesisHash, command: CliCommand::GetGenesisHash,
signers: vec![], signers: vec![],
@ -1413,7 +1414,7 @@ mod tests {
let test_get_slot = test_commands.clone().get_matches_from(vec!["test", "slot"]); let test_get_slot = test_commands.clone().get_matches_from(vec!["test", "slot"]);
assert_eq!( assert_eq!(
parse_command(&test_get_slot, &default_keypair_file, &mut None).unwrap(), parse_command(&test_get_slot, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::GetSlot { command: CliCommand::GetSlot {
commitment_config: CommitmentConfig::recent(), commitment_config: CommitmentConfig::recent(),
@ -1426,7 +1427,7 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "epoch"]); .get_matches_from(vec!["test", "epoch"]);
assert_eq!( assert_eq!(
parse_command(&test_get_epoch, &default_keypair_file, &mut None).unwrap(), parse_command(&test_get_epoch, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::GetEpoch { command: CliCommand::GetEpoch {
commitment_config: CommitmentConfig::recent(), commitment_config: CommitmentConfig::recent(),
@ -1439,7 +1440,7 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "total-supply"]); .get_matches_from(vec!["test", "total-supply"]);
assert_eq!( assert_eq!(
parse_command(&test_total_supply, &default_keypair_file, &mut None).unwrap(), parse_command(&test_total_supply, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::TotalSupply { command: CliCommand::TotalSupply {
commitment_config: CommitmentConfig::recent(), commitment_config: CommitmentConfig::recent(),
@ -1452,7 +1453,7 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "transaction-count"]); .get_matches_from(vec!["test", "transaction-count"]);
assert_eq!( assert_eq!(
parse_command(&test_transaction_count, &default_keypair_file, &mut None).unwrap(), parse_command(&test_transaction_count, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::GetTransactionCount { command: CliCommand::GetTransactionCount {
commitment_config: CommitmentConfig::recent(), commitment_config: CommitmentConfig::recent(),
@ -1474,7 +1475,7 @@ mod tests {
"max", "max",
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_ping, &default_keypair_file, &mut None).unwrap(), parse_command(&test_ping, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::Ping { command: CliCommand::Ping {
lamports: 1, lamports: 1,

View File

@ -18,16 +18,12 @@ macro_rules! pubkey {
}; };
} }
#[macro_use]
extern crate serde_derive; extern crate serde_derive;
pub mod checks; pub mod checks;
pub mod cli; pub mod cli;
pub mod cli_output;
pub mod cluster_query; pub mod cluster_query;
pub mod display;
pub mod nonce; pub mod nonce;
pub mod offline;
pub mod spend_utils; pub mod spend_utils;
pub mod stake; pub mod stake;
pub mod test_utils; pub mod test_utils;

View File

@ -2,17 +2,33 @@ use clap::{crate_description, crate_name, AppSettings, Arg, ArgGroup, ArgMatches
use console::style; use console::style;
use solana_clap_utils::{ 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::{ use solana_cli::cli::{
cli::{app, parse_command, process_command, CliCommandInfo, CliConfig, CliSigners}, app, parse_command, process_command, CliCommandInfo, CliConfig, SettingType,
cli_output::OutputFormat,
display::{println_name_value, println_name_value_or},
}; };
use solana_cli_config::{Config, CONFIG_FILE}; use solana_cli_config::{Config, CONFIG_FILE};
use solana_cli_output::{display::println_name_value, OutputFormat};
use solana_remote_wallet::remote_wallet::RemoteWalletManager; use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use std::{error, sync::Arc}; 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<bool, Box<dyn error::Error>> { fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error>> {
let parse_args = match matches.subcommand() { let parse_args = match matches.subcommand() {
("config", Some(matches)) => 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(""), matches.value_of("json_rpc_url").unwrap_or(""),
&config.json_rpc_url, &config.json_rpc_url,
); );
let default_signer_arg_name = "keypair".to_string();
let (_, default_signer_path) = CliConfig::compute_keypair_path_setting( 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, &config.keypair_path,
); );
let default_signer = DefaultSigner {
arg_name: default_signer_arg_name,
path: default_signer_path.clone(),
};
let CliCommandInfo { command, signers } = 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 let output_format = matches
.value_of("output_format") .value_of("output_format")

View File

@ -1,28 +1,26 @@
use crate::{ use crate::{
checks::{check_account_for_fee, check_unique_pubkeys}, checks::{check_account_for_fee, check_unique_pubkeys},
cli::{ cli::{
generate_unique_signers, log_instruction_custom_error, CliCommand, CliCommandInfo, log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError,
CliConfig, CliError, ProcessResult, SignerIndex, ProcessResult,
}, },
cli_output::CliNonceAccount,
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount}, spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
}; };
use clap::{App, Arg, ArgMatches, SubCommand}; use clap::{App, Arg, ArgMatches, SubCommand};
use solana_clap_utils::{ 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_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{ use solana_sdk::{
account::Account, account::Account,
account_utils::StateMut,
hash::Hash, hash::Hash,
message::Message, message::Message,
nonce::{ nonce::{self, State},
self,
state::{Data, Versions},
State,
},
pubkey::Pubkey, pubkey::Pubkey,
system_instruction::{ system_instruction::{
advance_nonce_account, authorize_nonce_account, create_nonce_account, advance_nonce_account, authorize_nonce_account, create_nonce_account,
@ -32,64 +30,11 @@ use solana_sdk::{
transaction::Transaction, transaction::Transaction,
}; };
use std::sync::Arc; 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 { pub trait NonceSubCommands {
fn nonce_subcommands(self) -> Self; 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<'_, '_> { impl NonceSubCommands for App<'_, '_> {
fn nonce_subcommands(self) -> Self { fn nonce_subcommands(self) -> Self {
self.subcommand( self.subcommand(
@ -219,51 +164,9 @@ impl NonceSubCommands for App<'_, '_> {
} }
} }
pub fn get_account(
rpc_client: &RpcClient,
nonce_pubkey: &Pubkey,
) -> Result<Account, CliNonceError> {
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<State, CliNonceError> {
account_identity_ok(account)?;
StateMut::<Versions>::state(account)
.map_err(|_| CliNonceError::InvalidAccountData)
.map(|v| v.convert_to_current())
}
pub fn data_from_account(account: &Account) -> Result<Data, CliNonceError> {
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( pub fn parse_authorize_nonce_account(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer_path: &str, default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap(); 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)?; signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
let payer_provided = None; let payer_provided = None;
let signer_info = generate_unique_signers( let signer_info = default_signer.generate_unique_signers(
vec![payer_provided, nonce_authority], vec![payer_provided, nonce_authority],
matches, matches,
default_signer_path,
wallet_manager, wallet_manager,
)?; )?;
@ -291,7 +193,7 @@ pub fn parse_authorize_nonce_account(
pub fn parse_nonce_create_account( pub fn parse_nonce_create_account(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer_path: &str, default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let (nonce_account, nonce_account_pubkey) = 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 nonce_authority = pubkey_of_signer(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
let payer_provided = None; let payer_provided = None;
let signer_info = generate_unique_signers( let signer_info = default_signer.generate_unique_signers(
vec![payer_provided, nonce_account], vec![payer_provided, nonce_account],
matches, matches,
default_signer_path,
wallet_manager, wallet_manager,
)?; )?;
@ -334,7 +235,7 @@ pub fn parse_get_nonce(
pub fn parse_new_nonce( pub fn parse_new_nonce(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer_path: &str, default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap(); 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)?; signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
let payer_provided = None; let payer_provided = None;
let signer_info = generate_unique_signers( let signer_info = default_signer.generate_unique_signers(
vec![payer_provided, nonce_authority], vec![payer_provided, nonce_authority],
matches, matches,
default_signer_path,
wallet_manager, wallet_manager,
)?; )?;
@ -377,7 +277,7 @@ pub fn parse_show_nonce_account(
pub fn parse_withdraw_from_nonce_account( pub fn parse_withdraw_from_nonce_account(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer_path: &str, default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap(); 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)?; signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
let payer_provided = None; let payer_provided = None;
let signer_info = generate_unique_signers( let signer_info = default_signer.generate_unique_signers(
vec![payer_provided, nonce_authority], vec![payer_provided, nonce_authority],
matches, matches,
default_signer_path,
wallet_manager, wallet_manager,
)?; )?;
@ -415,14 +314,14 @@ pub fn check_nonce_account(
match state_from_account(nonce_account)? { match state_from_account(nonce_account)? {
State::Initialized(ref data) => { State::Initialized(ref data) => {
if &data.blockhash != nonce_hash { if &data.blockhash != nonce_hash {
Err(CliNonceError::InvalidHash.into()) Err(Error::InvalidHash.into())
} else if nonce_authority != &data.authority { } else if nonce_authority != &data.authority {
Err(CliNonceError::InvalidAuthority.into()) Err(Error::InvalidAuthority.into())
} else { } else {
Ok(()) 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 crate::cli::{app, parse_command};
use solana_sdk::{ use solana_sdk::{
account::Account, account::Account,
account_utils::StateMut,
fee_calculator::FeeCalculator, fee_calculator::FeeCalculator,
hash::hash, hash::hash,
nonce::{self, State}, nonce::{self, state::Versions, State},
signature::{read_keypair_file, write_keypair, Keypair, Signer}, signature::{read_keypair_file, write_keypair, Keypair, Signer},
system_program, system_program,
}; };
@ -657,6 +557,10 @@ mod tests {
let default_keypair = Keypair::new(); let default_keypair = Keypair::new();
let (default_keypair_file, mut tmp_file) = make_tmp_file(); let (default_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap(); 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 (keypair_file, mut tmp_file) = make_tmp_file();
let nonce_account_keypair = Keypair::new(); let nonce_account_keypair = Keypair::new();
write_keypair(&nonce_account_keypair, tmp_file.as_file_mut()).unwrap(); write_keypair(&nonce_account_keypair, tmp_file.as_file_mut()).unwrap();
@ -675,12 +579,7 @@ mod tests {
&Pubkey::default().to_string(), &Pubkey::default().to_string(),
]); ]);
assert_eq!( assert_eq!(
parse_command( parse_command(&test_authorize_nonce_account, &default_signer, &mut None).unwrap(),
&test_authorize_nonce_account,
&default_keypair_file,
&mut None
)
.unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::AuthorizeNonceAccount { command: CliCommand::AuthorizeNonceAccount {
nonce_account: nonce_account_pubkey, nonce_account: nonce_account_pubkey,
@ -701,12 +600,7 @@ mod tests {
&authority_keypair_file, &authority_keypair_file,
]); ]);
assert_eq!( assert_eq!(
parse_command( parse_command(&test_authorize_nonce_account, &default_signer, &mut None).unwrap(),
&test_authorize_nonce_account,
&default_keypair_file,
&mut None
)
.unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::AuthorizeNonceAccount { command: CliCommand::AuthorizeNonceAccount {
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(), nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
@ -728,7 +622,7 @@ mod tests {
"50", "50",
]); ]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::CreateNonceAccount { command: CliCommand::CreateNonceAccount {
nonce_account: 1, nonce_account: 1,
@ -753,7 +647,7 @@ mod tests {
&authority_keypair_file, &authority_keypair_file,
]); ]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::CreateNonceAccount { command: CliCommand::CreateNonceAccount {
nonce_account: 1, nonce_account: 1,
@ -775,7 +669,7 @@ mod tests {
&nonce_account_string, &nonce_account_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_get_nonce, &default_keypair_file, &mut None).unwrap(), parse_command(&test_get_nonce, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::GetNonce(nonce_account_keypair.pubkey()), command: CliCommand::GetNonce(nonce_account_keypair.pubkey()),
signers: vec![], signers: vec![],
@ -789,7 +683,7 @@ mod tests {
.get_matches_from(vec!["test", "new-nonce", &keypair_file]); .get_matches_from(vec!["test", "new-nonce", &keypair_file]);
let nonce_account = read_keypair_file(&keypair_file).unwrap(); let nonce_account = read_keypair_file(&keypair_file).unwrap();
assert_eq!( assert_eq!(
parse_command(&test_new_nonce, &default_keypair_file, &mut None).unwrap(), parse_command(&test_new_nonce, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::NewNonce { command: CliCommand::NewNonce {
nonce_account: nonce_account.pubkey(), nonce_account: nonce_account.pubkey(),
@ -809,7 +703,7 @@ mod tests {
]); ]);
let nonce_account = read_keypair_file(&keypair_file).unwrap(); let nonce_account = read_keypair_file(&keypair_file).unwrap();
assert_eq!( assert_eq!(
parse_command(&test_new_nonce, &default_keypair_file, &mut None).unwrap(), parse_command(&test_new_nonce, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::NewNonce { command: CliCommand::NewNonce {
nonce_account: nonce_account.pubkey(), nonce_account: nonce_account.pubkey(),
@ -829,7 +723,7 @@ mod tests {
&nonce_account_string, &nonce_account_string,
]); ]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::ShowNonceAccount { command: CliCommand::ShowNonceAccount {
nonce_account_pubkey: nonce_account_keypair.pubkey(), nonce_account_pubkey: nonce_account_keypair.pubkey(),
@ -850,7 +744,7 @@ mod tests {
assert_eq!( assert_eq!(
parse_command( parse_command(
&test_withdraw_from_nonce_account, &test_withdraw_from_nonce_account,
&default_keypair_file, &default_signer,
&mut None &mut None
) )
.unwrap(), .unwrap(),
@ -878,7 +772,7 @@ mod tests {
assert_eq!( assert_eq!(
parse_command( parse_command(
&test_withdraw_from_nonce_account, &test_withdraw_from_nonce_account,
&default_keypair_file, &default_signer,
&mut None &mut None
) )
.unwrap(), .unwrap(),
@ -913,14 +807,14 @@ mod tests {
if let CliError::InvalidNonce(err) = if let CliError::InvalidNonce(err) =
check_nonce_account(&invalid_owner.unwrap(), &nonce_pubkey, &blockhash).unwrap_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); let invalid_data = Account::new_data(1, &"invalid", &system_program::ID);
if let CliError::InvalidNonce(err) = if let CliError::InvalidNonce(err) =
check_nonce_account(&invalid_data.unwrap(), &nonce_pubkey, &blockhash).unwrap_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 { let data = Versions::new_current(State::Initialized(nonce::state::Data {
@ -932,7 +826,7 @@ mod tests {
if let CliError::InvalidNonce(err) = if let CliError::InvalidNonce(err) =
check_nonce_account(&invalid_hash.unwrap(), &nonce_pubkey, &blockhash).unwrap_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 { let data = Versions::new_current(State::Initialized(nonce::state::Data {
@ -944,7 +838,7 @@ mod tests {
if let CliError::InvalidNonce(err) = if let CliError::InvalidNonce(err) =
check_nonce_account(&invalid_authority.unwrap(), &nonce_pubkey, &blockhash).unwrap_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); let data = Versions::new_current(State::Uninitialized);
@ -952,7 +846,7 @@ mod tests {
if let CliError::InvalidNonce(err) = if let CliError::InvalidNonce(err) =
check_nonce_account(&invalid_state.unwrap(), &nonce_pubkey, &blockhash).unwrap_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()); let system_account = Account::new(1, 0, &system_program::id());
assert_eq!( assert_eq!(
account_identity_ok(&system_account), account_identity_ok(&system_account),
Err(CliNonceError::UnexpectedDataSize), Err(Error::UnexpectedDataSize),
); );
let other_program = Pubkey::new(&[1u8; 32]); let other_program = Pubkey::new(&[1u8; 32]);
let other_account_no_data = Account::new(1, 0, &other_program); let other_account_no_data = Account::new(1, 0, &other_program);
assert_eq!( assert_eq!(
account_identity_ok(&other_account_no_data), 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()); let wrong_data_size_account = Account::new(1, 1, &system_program::id());
assert_eq!( assert_eq!(
state_from_account(&wrong_data_size_account), 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(); let state = state_from_account(&nonce_account).unwrap();
assert_eq!( assert_eq!(
data_from_state(&state), data_from_state(&state),
Err(CliNonceError::InvalidStateForOperation) Err(Error::InvalidStateForOperation)
); );
assert_eq!( assert_eq!(
data_from_account(&nonce_account), data_from_account(&nonce_account),
Err(CliNonceError::InvalidStateForOperation) Err(Error::InvalidStateForOperation)
); );
let data = nonce::state::Data { let data = nonce::state::Data {

View File

@ -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<Pubkey>,
pub bad_signers: Vec<Pubkey>,
}
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> {
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::<Hash>().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<Pubkey> = 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<Pubkey> = 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,
}
}

View File

@ -1,18 +1,29 @@
use crate::{ use crate::{
checks::{check_account_for_fee, check_unique_pubkeys}, checks::{check_account_for_fee, check_unique_pubkeys},
cli::{ cli::{
fee_payer_arg, generate_unique_signers, log_instruction_custom_error, nonce_authority_arg, log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError,
return_signers, CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult, ProcessResult,
SignerIndex, FEE_PAYER_ARG,
}, },
cli_output::{CliStakeHistory, CliStakeHistoryEntry, CliStakeState, CliStakeType}, nonce::check_nonce_account,
nonce::{check_nonce_account, nonce_arg, NONCE_ARG, NONCE_AUTHORITY_ARG},
offline::{blockhash_query::BlockhashQuery, *},
spend_utils::{resolve_spend_tx_and_check_account_balances, SpendAmount}, spend_utils::{resolve_spend_tx_and_check_account_balances, SpendAmount},
}; };
use clap::{App, Arg, ArgGroup, ArgMatches, SubCommand}; use clap::{App, Arg, ArgGroup, ArgMatches, SubCommand};
use solana_clap_utils::{input_parsers::*, input_validators::*, offline::*, ArgConstant}; use solana_clap_utils::{
use solana_client::{rpc_client::RpcClient, rpc_request::DELINQUENT_VALIDATOR_SLOT_DISTANCE}; 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_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{ use solana_sdk::{
account_utils::StateMut, account_utils::StateMut,
@ -142,8 +153,7 @@ impl StakeSubCommands for App<'_, '_> {
.help("Source account of funds [default: cli config keypair]"), .help("Source account of funds [default: cli config keypair]"),
) )
.offline_args() .offline_args()
.arg(nonce_arg()) .nonce_args()
.arg(nonce_authority_arg())
.arg(fee_payer_arg()) .arg(fee_payer_arg())
) )
.subcommand( .subcommand(
@ -172,8 +182,7 @@ impl StakeSubCommands for App<'_, '_> {
) )
.arg(stake_authority_arg()) .arg(stake_authority_arg())
.offline_args() .offline_args()
.arg(nonce_arg()) .nonce_args()
.arg(nonce_authority_arg())
.arg(fee_payer_arg()) .arg(fee_payer_arg())
) )
.subcommand( .subcommand(
@ -203,8 +212,7 @@ impl StakeSubCommands for App<'_, '_> {
.arg(stake_authority_arg()) .arg(stake_authority_arg())
.arg(withdraw_authority_arg()) .arg(withdraw_authority_arg())
.offline_args() .offline_args()
.arg(nonce_arg()) .nonce_args()
.arg(nonce_authority_arg())
.arg(fee_payer_arg()) .arg(fee_payer_arg())
) )
.subcommand( .subcommand(
@ -219,8 +227,7 @@ impl StakeSubCommands for App<'_, '_> {
) )
.arg(stake_authority_arg()) .arg(stake_authority_arg())
.offline_args() .offline_args()
.arg(nonce_arg()) .nonce_args()
.arg(nonce_authority_arg())
.arg(fee_payer_arg()) .arg(fee_payer_arg())
) )
.subcommand( .subcommand(
@ -260,8 +267,7 @@ impl StakeSubCommands for App<'_, '_> {
) )
.arg(stake_authority_arg()) .arg(stake_authority_arg())
.offline_args() .offline_args()
.arg(nonce_arg()) .nonce_args()
.arg(nonce_authority_arg())
.arg(fee_payer_arg()) .arg(fee_payer_arg())
) )
.subcommand( .subcommand(
@ -283,8 +289,7 @@ impl StakeSubCommands for App<'_, '_> {
) )
.arg(stake_authority_arg()) .arg(stake_authority_arg())
.offline_args() .offline_args()
.arg(nonce_arg()) .nonce_args()
.arg(nonce_authority_arg())
.arg(fee_payer_arg()) .arg(fee_payer_arg())
) )
.subcommand( .subcommand(
@ -315,8 +320,7 @@ impl StakeSubCommands for App<'_, '_> {
) )
.arg(withdraw_authority_arg()) .arg(withdraw_authority_arg())
.offline_args() .offline_args()
.arg(nonce_arg()) .nonce_args()
.arg(nonce_authority_arg())
.arg(fee_payer_arg()) .arg(fee_payer_arg())
.arg( .arg(
Arg::with_name("custodian") Arg::with_name("custodian")
@ -371,8 +375,7 @@ impl StakeSubCommands for App<'_, '_> {
.help("Keypair of the existing custodian [default: cli config pubkey]") .help("Keypair of the existing custodian [default: cli config pubkey]")
) )
.offline_args() .offline_args()
.arg(nonce_arg()) .nonce_args()
.arg(nonce_authority_arg())
.arg(fee_payer_arg()) .arg(fee_payer_arg())
) )
.subcommand( .subcommand(
@ -409,7 +412,7 @@ impl StakeSubCommands for App<'_, '_> {
pub fn parse_stake_create_account( pub fn parse_stake_create_account(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer_path: &str, default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let seed = matches.value_of("seed").map(|s| s.to_string()); 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); bulk_signers.push(nonce_authority);
} }
let signer_info = 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 { Ok(CliCommandInfo {
command: CliCommand::CreateStakeAccount { command: CliCommand::CreateStakeAccount {
@ -461,7 +464,7 @@ pub fn parse_stake_create_account(
pub fn parse_stake_delegate_stake( pub fn parse_stake_delegate_stake(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer_path: &str, default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let stake_account_pubkey = let stake_account_pubkey =
@ -483,7 +486,7 @@ pub fn parse_stake_delegate_stake(
bulk_signers.push(nonce_authority); bulk_signers.push(nonce_authority);
} }
let signer_info = 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 { Ok(CliCommandInfo {
command: CliCommand::DelegateStake { command: CliCommand::DelegateStake {
@ -503,7 +506,7 @@ pub fn parse_stake_delegate_stake(
pub fn parse_stake_authorize( pub fn parse_stake_authorize(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer_path: &str, default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let stake_account_pubkey = let stake_account_pubkey =
@ -555,7 +558,7 @@ pub fn parse_stake_authorize(
bulk_signers.push(nonce_authority); bulk_signers.push(nonce_authority);
} }
let signer_info = 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 let new_authorizations = new_authorizations
.into_iter() .into_iter()
@ -586,7 +589,7 @@ pub fn parse_stake_authorize(
pub fn parse_split_stake( pub fn parse_split_stake(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer_path: &str, default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let stake_account_pubkey = let stake_account_pubkey =
@ -610,7 +613,7 @@ pub fn parse_split_stake(
bulk_signers.push(nonce_authority); bulk_signers.push(nonce_authority);
} }
let signer_info = 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 { Ok(CliCommandInfo {
command: CliCommand::SplitStake { command: CliCommand::SplitStake {
@ -631,7 +634,7 @@ pub fn parse_split_stake(
pub fn parse_merge_stake( pub fn parse_merge_stake(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer_path: &str, default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let stake_account_pubkey = let stake_account_pubkey =
@ -653,7 +656,7 @@ pub fn parse_merge_stake(
bulk_signers.push(nonce_authority); bulk_signers.push(nonce_authority);
} }
let signer_info = 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 { Ok(CliCommandInfo {
command: CliCommand::MergeStake { command: CliCommand::MergeStake {
@ -672,7 +675,7 @@ pub fn parse_merge_stake(
pub fn parse_stake_deactivate_stake( pub fn parse_stake_deactivate_stake(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer_path: &str, default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let stake_account_pubkey = let stake_account_pubkey =
@ -691,7 +694,7 @@ pub fn parse_stake_deactivate_stake(
bulk_signers.push(nonce_authority); bulk_signers.push(nonce_authority);
} }
let signer_info = 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 { Ok(CliCommandInfo {
command: CliCommand::DeactivateStake { command: CliCommand::DeactivateStake {
@ -709,7 +712,7 @@ pub fn parse_stake_deactivate_stake(
pub fn parse_stake_withdraw_stake( pub fn parse_stake_withdraw_stake(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer_path: &str, default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let stake_account_pubkey = let stake_account_pubkey =
@ -735,7 +738,7 @@ pub fn parse_stake_withdraw_stake(
bulk_signers.push(custodian); bulk_signers.push(custodian);
} }
let signer_info = 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 { Ok(CliCommandInfo {
command: CliCommand::WithdrawStake { command: CliCommand::WithdrawStake {
@ -756,7 +759,7 @@ pub fn parse_stake_withdraw_stake(
pub fn parse_stake_set_lockup( pub fn parse_stake_set_lockup(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer_path: &str, default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let stake_account_pubkey = let stake_account_pubkey =
@ -779,7 +782,7 @@ pub fn parse_stake_set_lockup(
bulk_signers.push(nonce_authority); bulk_signers.push(nonce_authority);
} }
let signer_info = 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 { Ok(CliCommandInfo {
command: CliCommand::StakeSetLockup { command: CliCommand::StakeSetLockup {
@ -939,7 +942,7 @@ pub fn process_create_stake_account(
let mut tx = Transaction::new_unsigned(message); let mut tx = Transaction::new_unsigned(message);
if sign_only { if sign_only {
tx.try_partial_sign(&config.signers, recent_blockhash)?; tx.try_partial_sign(&config.signers, recent_blockhash)?;
return_signers(&tx, &config) return_signers(&tx, &config.output_format)
} else { } else {
tx.try_sign(&config.signers, recent_blockhash)?; tx.try_sign(&config.signers, recent_blockhash)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx); let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
@ -994,7 +997,7 @@ pub fn process_stake_authorize(
if sign_only { if sign_only {
tx.try_partial_sign(&config.signers, recent_blockhash)?; tx.try_partial_sign(&config.signers, recent_blockhash)?;
return_signers(&tx, &config) return_signers(&tx, &config.output_format)
} else { } else {
tx.try_sign(&config.signers, recent_blockhash)?; tx.try_sign(&config.signers, recent_blockhash)?;
if let Some(nonce_account) = &nonce_account { if let Some(nonce_account) = &nonce_account {
@ -1048,7 +1051,7 @@ pub fn process_deactivate_stake_account(
if sign_only { if sign_only {
tx.try_partial_sign(&config.signers, recent_blockhash)?; tx.try_partial_sign(&config.signers, recent_blockhash)?;
return_signers(&tx, &config) return_signers(&tx, &config.output_format)
} else { } else {
tx.try_sign(&config.signers, recent_blockhash)?; tx.try_sign(&config.signers, recent_blockhash)?;
if let Some(nonce_account) = &nonce_account { if let Some(nonce_account) = &nonce_account {
@ -1111,7 +1114,7 @@ pub fn process_withdraw_stake(
if sign_only { if sign_only {
tx.try_partial_sign(&config.signers, recent_blockhash)?; tx.try_partial_sign(&config.signers, recent_blockhash)?;
return_signers(&tx, &config) return_signers(&tx, &config.output_format)
} else { } else {
tx.try_sign(&config.signers, recent_blockhash)?; tx.try_sign(&config.signers, recent_blockhash)?;
if let Some(nonce_account) = &nonce_account { if let Some(nonce_account) = &nonce_account {
@ -1245,7 +1248,7 @@ pub fn process_split_stake(
if sign_only { if sign_only {
tx.try_partial_sign(&config.signers, recent_blockhash)?; tx.try_partial_sign(&config.signers, recent_blockhash)?;
return_signers(&tx, &config) return_signers(&tx, &config.output_format)
} else { } else {
tx.try_sign(&config.signers, recent_blockhash)?; tx.try_sign(&config.signers, recent_blockhash)?;
if let Some(nonce_account) = &nonce_account { if let Some(nonce_account) = &nonce_account {
@ -1338,7 +1341,7 @@ pub fn process_merge_stake(
if sign_only { if sign_only {
tx.try_partial_sign(&config.signers, recent_blockhash)?; tx.try_partial_sign(&config.signers, recent_blockhash)?;
return_signers(&tx, &config) return_signers(&tx, &config.output_format)
} else { } else {
tx.try_sign(&config.signers, recent_blockhash)?; tx.try_sign(&config.signers, recent_blockhash)?;
if let Some(nonce_account) = &nonce_account { if let Some(nonce_account) = &nonce_account {
@ -1395,7 +1398,7 @@ pub fn process_stake_set_lockup(
if sign_only { if sign_only {
tx.try_partial_sign(&config.signers, recent_blockhash)?; tx.try_partial_sign(&config.signers, recent_blockhash)?;
return_signers(&tx, &config) return_signers(&tx, &config.output_format)
} else { } else {
tx.try_sign(&config.signers, recent_blockhash)?; tx.try_sign(&config.signers, recent_blockhash)?;
if let Some(nonce_account) = &nonce_account { if let Some(nonce_account) = &nonce_account {
@ -1648,7 +1651,7 @@ pub fn process_delegate_stake(
if sign_only { if sign_only {
tx.try_partial_sign(&config.signers, recent_blockhash)?; tx.try_partial_sign(&config.signers, recent_blockhash)?;
return_signers(&tx, &config) return_signers(&tx, &config.output_format)
} else { } else {
tx.try_sign(&config.signers, recent_blockhash)?; tx.try_sign(&config.signers, recent_blockhash)?;
if let Some(nonce_account) = &nonce_account { if let Some(nonce_account) = &nonce_account {
@ -1670,6 +1673,7 @@ pub fn process_delegate_stake(
mod tests { mod tests {
use super::*; use super::*;
use crate::cli::{app, parse_command}; use crate::cli::{app, parse_command};
use solana_client::blockhash_query;
use solana_sdk::{ use solana_sdk::{
hash::Hash, hash::Hash,
signature::{ signature::{
@ -1690,6 +1694,10 @@ mod tests {
let default_keypair = Keypair::new(); let default_keypair = Keypair::new();
let (default_keypair_file, mut tmp_file) = make_tmp_file(); let (default_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap(); 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 (keypair_file, mut tmp_file) = make_tmp_file();
let stake_account_keypair = Keypair::new(); let stake_account_keypair = Keypair::new();
write_keypair(&stake_account_keypair, tmp_file.as_file_mut()).unwrap(); write_keypair(&stake_account_keypair, tmp_file.as_file_mut()).unwrap();
@ -1717,7 +1725,7 @@ mod tests {
&new_withdraw_string, &new_withdraw_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(), parse_command(&test_stake_authorize, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::StakeAuthorize { command: CliCommand::StakeAuthorize {
stake_account_pubkey, stake_account_pubkey,
@ -1751,7 +1759,7 @@ mod tests {
&withdraw_authority_keypair_file, &withdraw_authority_keypair_file,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(), parse_command(&test_stake_authorize, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::StakeAuthorize { command: CliCommand::StakeAuthorize {
stake_account_pubkey, stake_account_pubkey,
@ -1789,7 +1797,7 @@ mod tests {
&withdraw_authority_keypair_file, &withdraw_authority_keypair_file,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(), parse_command(&test_stake_authorize, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::StakeAuthorize { command: CliCommand::StakeAuthorize {
stake_account_pubkey, stake_account_pubkey,
@ -1819,7 +1827,7 @@ mod tests {
&new_stake_string, &new_stake_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(), parse_command(&test_stake_authorize, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::StakeAuthorize { command: CliCommand::StakeAuthorize {
stake_account_pubkey, stake_account_pubkey,
@ -1843,7 +1851,7 @@ mod tests {
&stake_authority_keypair_file, &stake_authority_keypair_file,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(), parse_command(&test_stake_authorize, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::StakeAuthorize { command: CliCommand::StakeAuthorize {
stake_account_pubkey, stake_account_pubkey,
@ -1873,7 +1881,7 @@ mod tests {
&withdraw_authority_keypair_file, &withdraw_authority_keypair_file,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(), parse_command(&test_stake_authorize, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::StakeAuthorize { command: CliCommand::StakeAuthorize {
stake_account_pubkey, stake_account_pubkey,
@ -1900,7 +1908,7 @@ mod tests {
&new_withdraw_string, &new_withdraw_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(), parse_command(&test_stake_authorize, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::StakeAuthorize { command: CliCommand::StakeAuthorize {
stake_account_pubkey, stake_account_pubkey,
@ -1928,7 +1936,7 @@ mod tests {
&withdraw_authority_keypair_file, &withdraw_authority_keypair_file,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(), parse_command(&test_stake_authorize, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::StakeAuthorize { command: CliCommand::StakeAuthorize {
stake_account_pubkey, stake_account_pubkey,
@ -1966,7 +1974,7 @@ mod tests {
"--sign-only", "--sign-only",
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_authorize, &default_keypair_file, &mut None).unwrap(), parse_command(&test_authorize, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::StakeAuthorize { command: CliCommand::StakeAuthorize {
stake_account_pubkey, stake_account_pubkey,
@ -1999,7 +2007,7 @@ mod tests {
&pubkey.to_string(), &pubkey.to_string(),
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_authorize, &default_keypair_file, &mut None).unwrap(), parse_command(&test_authorize, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::StakeAuthorize { command: CliCommand::StakeAuthorize {
stake_account_pubkey, stake_account_pubkey,
@ -2045,7 +2053,7 @@ mod tests {
&pubkey2.to_string(), &pubkey2.to_string(),
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_authorize, &default_keypair_file, &mut None).unwrap(), parse_command(&test_authorize, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::StakeAuthorize { command: CliCommand::StakeAuthorize {
stake_account_pubkey, stake_account_pubkey,
@ -2077,7 +2085,7 @@ mod tests {
&blockhash_string, &blockhash_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_authorize, &default_keypair_file, &mut None).unwrap(), parse_command(&test_authorize, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::StakeAuthorize { command: CliCommand::StakeAuthorize {
stake_account_pubkey, stake_account_pubkey,
@ -2114,7 +2122,7 @@ mod tests {
&nonce_keypair_file, &nonce_keypair_file,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_authorize, &default_keypair_file, &mut None).unwrap(), parse_command(&test_authorize, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::StakeAuthorize { command: CliCommand::StakeAuthorize {
stake_account_pubkey, stake_account_pubkey,
@ -2150,7 +2158,7 @@ mod tests {
&fee_payer_keypair_file, &fee_payer_keypair_file,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_authorize, &default_keypair_file, &mut None).unwrap(), parse_command(&test_authorize, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::StakeAuthorize { command: CliCommand::StakeAuthorize {
stake_account_pubkey, stake_account_pubkey,
@ -2184,7 +2192,7 @@ mod tests {
&signer, &signer,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_authorize, &default_keypair_file, &mut None).unwrap(), parse_command(&test_authorize, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::StakeAuthorize { command: CliCommand::StakeAuthorize {
stake_account_pubkey, stake_account_pubkey,
@ -2225,7 +2233,7 @@ mod tests {
"43", "43",
]); ]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::CreateStakeAccount { command: CliCommand::CreateStakeAccount {
stake_account: 1, stake_account: 1,
@ -2266,12 +2274,7 @@ mod tests {
]); ]);
assert_eq!( assert_eq!(
parse_command( parse_command(&test_create_stake_account2, &default_signer, &mut None).unwrap(),
&test_create_stake_account2,
&default_keypair_file,
&mut None
)
.unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::CreateStakeAccount { command: CliCommand::CreateStakeAccount {
stake_account: 1, stake_account: 1,
@ -2324,12 +2327,7 @@ mod tests {
]); ]);
assert_eq!( assert_eq!(
parse_command( parse_command(&test_create_stake_account2, &default_signer, &mut None).unwrap(),
&test_create_stake_account2,
&default_keypair_file,
&mut None
)
.unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::CreateStakeAccount { command: CliCommand::CreateStakeAccount {
stake_account: 1, stake_account: 1,
@ -2365,7 +2363,7 @@ mod tests {
&vote_account_string, &vote_account_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(), parse_command(&test_delegate_stake, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::DelegateStake { command: CliCommand::DelegateStake {
stake_account_pubkey, stake_account_pubkey,
@ -2394,7 +2392,7 @@ mod tests {
&stake_authority_keypair_file, &stake_authority_keypair_file,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(), parse_command(&test_delegate_stake, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::DelegateStake { command: CliCommand::DelegateStake {
stake_account_pubkey, stake_account_pubkey,
@ -2425,7 +2423,7 @@ mod tests {
&vote_account_string, &vote_account_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(), parse_command(&test_delegate_stake, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::DelegateStake { command: CliCommand::DelegateStake {
stake_account_pubkey, stake_account_pubkey,
@ -2454,7 +2452,7 @@ mod tests {
&blockhash_string, &blockhash_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(), parse_command(&test_delegate_stake, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::DelegateStake { command: CliCommand::DelegateStake {
stake_account_pubkey, stake_account_pubkey,
@ -2484,7 +2482,7 @@ mod tests {
"--sign-only", "--sign-only",
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(), parse_command(&test_delegate_stake, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::DelegateStake { command: CliCommand::DelegateStake {
stake_account_pubkey, stake_account_pubkey,
@ -2518,7 +2516,7 @@ mod tests {
&key1.to_string(), &key1.to_string(),
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(), parse_command(&test_delegate_stake, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::DelegateStake { command: CliCommand::DelegateStake {
stake_account_pubkey, stake_account_pubkey,
@ -2564,7 +2562,7 @@ mod tests {
&key2.to_string(), &key2.to_string(),
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(), parse_command(&test_delegate_stake, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::DelegateStake { command: CliCommand::DelegateStake {
stake_account_pubkey, stake_account_pubkey,
@ -2601,7 +2599,7 @@ mod tests {
&fee_payer_keypair_file, &fee_payer_keypair_file,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(), parse_command(&test_delegate_stake, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::DelegateStake { command: CliCommand::DelegateStake {
stake_account_pubkey, stake_account_pubkey,
@ -2631,7 +2629,7 @@ mod tests {
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_withdraw_stake, &default_keypair_file, &mut None).unwrap(), parse_command(&test_withdraw_stake, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::WithdrawStake { command: CliCommand::WithdrawStake {
stake_account_pubkey, stake_account_pubkey,
@ -2661,7 +2659,7 @@ mod tests {
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_withdraw_stake, &default_keypair_file, &mut None).unwrap(), parse_command(&test_withdraw_stake, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::WithdrawStake { command: CliCommand::WithdrawStake {
stake_account_pubkey, stake_account_pubkey,
@ -2696,7 +2694,7 @@ mod tests {
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_withdraw_stake, &default_keypair_file, &mut None).unwrap(), parse_command(&test_withdraw_stake, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::WithdrawStake { command: CliCommand::WithdrawStake {
stake_account_pubkey, stake_account_pubkey,
@ -2739,7 +2737,7 @@ mod tests {
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_withdraw_stake, &default_keypair_file, &mut None).unwrap(), parse_command(&test_withdraw_stake, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::WithdrawStake { command: CliCommand::WithdrawStake {
stake_account_pubkey, stake_account_pubkey,
@ -2772,7 +2770,7 @@ mod tests {
&stake_account_string, &stake_account_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_deactivate_stake, &default_keypair_file, &mut None).unwrap(), parse_command(&test_deactivate_stake, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::DeactivateStake { command: CliCommand::DeactivateStake {
stake_account_pubkey, stake_account_pubkey,
@ -2796,7 +2794,7 @@ mod tests {
&stake_authority_keypair_file, &stake_authority_keypair_file,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_deactivate_stake, &default_keypair_file, &mut None).unwrap(), parse_command(&test_deactivate_stake, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::DeactivateStake { command: CliCommand::DeactivateStake {
stake_account_pubkey, stake_account_pubkey,
@ -2827,7 +2825,7 @@ mod tests {
&blockhash_string, &blockhash_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_deactivate_stake, &default_keypair_file, &mut None).unwrap(), parse_command(&test_deactivate_stake, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::DeactivateStake { command: CliCommand::DeactivateStake {
stake_account_pubkey, stake_account_pubkey,
@ -2854,7 +2852,7 @@ mod tests {
"--sign-only", "--sign-only",
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_deactivate_stake, &default_keypair_file, &mut None).unwrap(), parse_command(&test_deactivate_stake, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::DeactivateStake { command: CliCommand::DeactivateStake {
stake_account_pubkey, stake_account_pubkey,
@ -2885,7 +2883,7 @@ mod tests {
&key1.to_string(), &key1.to_string(),
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_deactivate_stake, &default_keypair_file, &mut None).unwrap(), parse_command(&test_deactivate_stake, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::DeactivateStake { command: CliCommand::DeactivateStake {
stake_account_pubkey, stake_account_pubkey,
@ -2928,7 +2926,7 @@ mod tests {
&key2.to_string(), &key2.to_string(),
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_deactivate_stake, &default_keypair_file, &mut None).unwrap(), parse_command(&test_deactivate_stake, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::DeactivateStake { command: CliCommand::DeactivateStake {
stake_account_pubkey, stake_account_pubkey,
@ -2959,7 +2957,7 @@ mod tests {
&fee_payer_keypair_file, &fee_payer_keypair_file,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_deactivate_stake, &default_keypair_file, &mut None).unwrap(), parse_command(&test_deactivate_stake, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::DeactivateStake { command: CliCommand::DeactivateStake {
stake_account_pubkey, stake_account_pubkey,
@ -2993,7 +2991,7 @@ mod tests {
"50", "50",
]); ]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::SplitStake { command: CliCommand::SplitStake {
stake_account_pubkey: stake_account_keypair.pubkey(), stake_account_pubkey: stake_account_keypair.pubkey(),
@ -3054,7 +3052,7 @@ mod tests {
&stake_signer, &stake_signer,
]); ]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::SplitStake { command: CliCommand::SplitStake {
stake_account_pubkey: stake_account_keypair.pubkey(), stake_account_pubkey: stake_account_keypair.pubkey(),
@ -3094,7 +3092,7 @@ mod tests {
&source_stake_account_pubkey.to_string(), &source_stake_account_pubkey.to_string(),
]); ]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::MergeStake { command: CliCommand::MergeStake {
stake_account_pubkey: stake_account_keypair.pubkey(), stake_account_pubkey: stake_account_keypair.pubkey(),

View File

@ -1,6 +1,5 @@
use crate::{ use crate::{
cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult}, cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult},
cli_output::{CliValidatorInfo, CliValidatorInfoVec},
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount}, spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
}; };
use bincode::deserialize; use bincode::deserialize;
@ -13,8 +12,9 @@ use solana_account_decoder::validator_info::{
use solana_clap_utils::{ use solana_clap_utils::{
input_parsers::pubkey_of, input_parsers::pubkey_of,
input_validators::{is_pubkey, is_url}, 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_client::rpc_client::RpcClient;
use solana_config_program::{config_instruction, get_config_data, ConfigKeys, ConfigState}; use solana_config_program::{config_instruction, get_config_data, ConfigKeys, ConfigState};
use solana_remote_wallet::remote_wallet::RemoteWalletManager; use solana_remote_wallet::remote_wallet::RemoteWalletManager;
@ -212,7 +212,7 @@ impl ValidatorInfoSubCommands for App<'_, '_> {
pub fn parse_validator_info_command( pub fn parse_validator_info_command(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer_path: &str, default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let info_pubkey = pubkey_of(matches, "info_pubkey"); let info_pubkey = pubkey_of(matches, "info_pubkey");
@ -224,12 +224,7 @@ pub fn parse_validator_info_command(
force_keybase: matches.is_present("force"), force_keybase: matches.is_present("force"),
info_pubkey, info_pubkey,
}, },
signers: vec![signer_from_path( signers: vec![default_signer.signer_from_path(matches, wallet_manager)?],
matches,
default_signer_path,
"keypair",
wallet_manager,
)?],
}) })
} }

View File

@ -1,10 +1,9 @@
use crate::{ use crate::{
checks::{check_account_for_fee, check_unique_pubkeys}, checks::{check_account_for_fee, check_unique_pubkeys},
cli::{ cli::{
generate_unique_signers, log_instruction_custom_error, CliCommand, CliCommandInfo, log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError,
CliConfig, CliError, ProcessResult, SignerIndex, ProcessResult,
}, },
cli_output::{CliEpochVotingHistory, CliLockout, CliVoteAccount},
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount}, spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
}; };
use clap::{value_t_or_exit, App, Arg, ArgMatches, SubCommand}; use clap::{value_t_or_exit, App, Arg, ArgMatches, SubCommand};
@ -12,7 +11,9 @@ use solana_clap_utils::{
commitment::{commitment_arg, COMMITMENT_ARG}, commitment::{commitment_arg, COMMITMENT_ARG},
input_parsers::*, input_parsers::*,
input_validators::*, input_validators::*,
keypair::{DefaultSigner, SignerIndex},
}; };
use solana_cli_output::{CliEpochVotingHistory, CliLockout, CliVoteAccount};
use solana_client::rpc_client::RpcClient; use solana_client::rpc_client::RpcClient;
use solana_remote_wallet::remote_wallet::RemoteWalletManager; use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{ use solana_sdk::{
@ -250,7 +251,7 @@ impl VoteSubCommands for App<'_, '_> {
pub fn parse_create_vote_account( pub fn parse_create_vote_account(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer_path: &str, default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let (vote_account, vote_account_pubkey) = signer_of(matches, "vote_account", wallet_manager)?; 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 authorized_withdrawer = pubkey_of_signer(matches, "authorized_withdrawer", wallet_manager)?;
let payer_provided = None; 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], vec![payer_provided, vote_account, identity_account],
matches, matches,
default_signer_path,
wallet_manager, wallet_manager,
)?; )?;
@ -284,7 +284,7 @@ pub fn parse_create_vote_account(
pub fn parse_vote_authorize( pub fn parse_vote_authorize(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer_path: &str, default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
vote_authorize: VoteAuthorize, vote_authorize: VoteAuthorize,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
@ -295,10 +295,9 @@ pub fn parse_vote_authorize(
let (authorized, _) = signer_of(matches, "authorized", wallet_manager)?; let (authorized, _) = signer_of(matches, "authorized", wallet_manager)?;
let payer_provided = None; let payer_provided = None;
let signer_info = generate_unique_signers( let signer_info = default_signer.generate_unique_signers(
vec![payer_provided, authorized], vec![payer_provided, authorized],
matches, matches,
default_signer_path,
wallet_manager, wallet_manager,
)?; )?;
@ -314,7 +313,7 @@ pub fn parse_vote_authorize(
pub fn parse_vote_update_validator( pub fn parse_vote_update_validator(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer_path: &str, default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let vote_account_pubkey = let vote_account_pubkey =
@ -325,10 +324,9 @@ pub fn parse_vote_update_validator(
signer_of(matches, "authorized_withdrawer", wallet_manager)?; signer_of(matches, "authorized_withdrawer", wallet_manager)?;
let payer_provided = None; 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], vec![payer_provided, authorized_withdrawer, new_identity_account],
matches, matches,
default_signer_path,
wallet_manager, wallet_manager,
)?; )?;
@ -344,7 +342,7 @@ pub fn parse_vote_update_validator(
pub fn parse_vote_update_commission( pub fn parse_vote_update_commission(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer_path: &str, default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let vote_account_pubkey = let vote_account_pubkey =
@ -354,10 +352,9 @@ pub fn parse_vote_update_commission(
let commission = value_t_or_exit!(matches, "commission", u8); let commission = value_t_or_exit!(matches, "commission", u8);
let payer_provided = None; let payer_provided = None;
let signer_info = generate_unique_signers( let signer_info = default_signer.generate_unique_signers(
vec![payer_provided, authorized_withdrawer], vec![payer_provided, authorized_withdrawer],
matches, matches,
default_signer_path,
wallet_manager, wallet_manager,
)?; )?;
@ -391,7 +388,7 @@ pub fn parse_vote_get_account_command(
pub fn parse_withdraw_from_vote_account( pub fn parse_withdraw_from_vote_account(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer_path: &str, default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let vote_account_pubkey = let vote_account_pubkey =
@ -404,10 +401,9 @@ pub fn parse_withdraw_from_vote_account(
signer_of(matches, "authorized_withdrawer", wallet_manager)?; signer_of(matches, "authorized_withdrawer", wallet_manager)?;
let payer_provided = None; let payer_provided = None;
let signer_info = generate_unique_signers( let signer_info = default_signer.generate_unique_signers(
vec![payer_provided, withdraw_authority], vec![payer_provided, withdraw_authority],
matches, matches,
default_signer_path,
wallet_manager, wallet_manager,
)?; )?;
@ -760,6 +756,10 @@ mod tests {
let default_keypair = Keypair::new(); let default_keypair = Keypair::new();
let (default_keypair_file, mut tmp_file) = make_tmp_file(); let (default_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap(); 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![ let test_authorize_voter = test_commands.clone().get_matches_from(vec![
"test", "test",
@ -769,7 +769,7 @@ mod tests {
&pubkey2_string, &pubkey2_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_authorize_voter, &default_keypair_file, &mut None).unwrap(), parse_command(&test_authorize_voter, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::VoteAuthorize { command: CliCommand::VoteAuthorize {
vote_account_pubkey: pubkey, vote_account_pubkey: pubkey,
@ -792,7 +792,7 @@ mod tests {
&pubkey2_string, &pubkey2_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_authorize_voter, &default_keypair_file, &mut None).unwrap(), parse_command(&test_authorize_voter, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::VoteAuthorize { command: CliCommand::VoteAuthorize {
vote_account_pubkey: pubkey, vote_account_pubkey: pubkey,
@ -822,7 +822,7 @@ mod tests {
"10", "10",
]); ]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::CreateVoteAccount { command: CliCommand::CreateVoteAccount {
vote_account: 1, vote_account: 1,
@ -851,7 +851,7 @@ mod tests {
&identity_keypair_file, &identity_keypair_file,
]); ]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::CreateVoteAccount { command: CliCommand::CreateVoteAccount {
vote_account: 1, vote_account: 1,
@ -884,7 +884,7 @@ mod tests {
&authed.to_string(), &authed.to_string(),
]); ]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::CreateVoteAccount { command: CliCommand::CreateVoteAccount {
vote_account: 1, vote_account: 1,
@ -915,7 +915,7 @@ mod tests {
&authed.to_string(), &authed.to_string(),
]); ]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::CreateVoteAccount { command: CliCommand::CreateVoteAccount {
vote_account: 1, vote_account: 1,
@ -941,7 +941,7 @@ mod tests {
&keypair_file, &keypair_file,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_update_validator, &default_keypair_file, &mut None).unwrap(), parse_command(&test_update_validator, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::VoteUpdateValidator { command: CliCommand::VoteUpdateValidator {
vote_account_pubkey: pubkey, vote_account_pubkey: pubkey,
@ -964,7 +964,7 @@ mod tests {
&keypair_file, &keypair_file,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_update_commission, &default_keypair_file, &mut None).unwrap(), parse_command(&test_update_commission, &default_signer, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::VoteUpdateCommission { command: CliCommand::VoteUpdateCommission {
vote_account_pubkey: pubkey, vote_account_pubkey: pubkey,
@ -987,12 +987,7 @@ mod tests {
"42", "42",
]); ]);
assert_eq!( assert_eq!(
parse_command( parse_command(&test_withdraw_from_vote_account, &default_signer, &mut None).unwrap(),
&test_withdraw_from_vote_account,
&default_keypair_file,
&mut None
)
.unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::WithdrawFromVoteAccount { command: CliCommand::WithdrawFromVoteAccount {
vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(), vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(),
@ -1013,12 +1008,7 @@ mod tests {
"ALL", "ALL",
]); ]);
assert_eq!( assert_eq!(
parse_command( parse_command(&test_withdraw_from_vote_account, &default_signer, &mut None).unwrap(),
&test_withdraw_from_vote_account,
&default_keypair_file,
&mut None
)
.unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::WithdrawFromVoteAccount { command: CliCommand::WithdrawFromVoteAccount {
vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(), vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(),
@ -1044,12 +1034,7 @@ mod tests {
&withdraw_authority_file, &withdraw_authority_file,
]); ]);
assert_eq!( assert_eq!(
parse_command( parse_command(&test_withdraw_from_vote_account, &default_signer, &mut None).unwrap(),
&test_withdraw_from_vote_account,
&default_keypair_file,
&mut None
)
.unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::WithdrawFromVoteAccount { command: CliCommand::WithdrawFromVoteAccount {
vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(), vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(),

View File

@ -1,15 +1,14 @@
use solana_cli::test_utils::check_balance; use solana_cli::test_utils::check_balance;
use solana_cli::{ use solana_cli::{
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig}, 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, 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::contact_info::ContactInfo;
use solana_core::validator::{TestValidator, TestValidatorOptions}; use solana_core::validator::{TestValidator, TestValidatorOptions};
use solana_faucet::faucet::run_local_faucet; use solana_faucet::faucet::run_local_faucet;
@ -299,8 +298,8 @@ fn test_create_account_with_seed() {
check_balance(0, &rpc_client, &to_address); check_balance(0, &rpc_client, &to_address);
// Fetch nonce hash // Fetch nonce hash
let nonce_hash = nonce::get_account(&rpc_client, &nonce_address) let nonce_hash = nonce_utils::get_account(&rpc_client, &nonce_address)
.and_then(|ref a| nonce::data_from_account(a)) .and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap() .unwrap()
.blockhash; .blockhash;

View File

@ -3,15 +3,14 @@ use serde_json::Value;
use solana_cli::test_utils::check_balance; use solana_cli::test_utils::check_balance;
use solana_cli::{ use solana_cli::{
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig, PayCommand}, 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, 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_core::validator::TestValidator;
use solana_faucet::faucet::run_local_faucet; use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{ use solana_sdk::{
@ -410,8 +409,8 @@ fn test_nonced_pay_tx() {
check_balance(minimum_nonce_balance, &rpc_client, &nonce_account.pubkey()); check_balance(minimum_nonce_balance, &rpc_client, &nonce_account.pubkey());
// Fetch nonce hash // Fetch nonce hash
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey()) let nonce_hash = nonce_utils::get_account(&rpc_client, &nonce_account.pubkey())
.and_then(|ref a| nonce::data_from_account(a)) .and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap() .unwrap()
.blockhash; .blockhash;
@ -433,8 +432,8 @@ fn test_nonced_pay_tx() {
check_balance(10, &rpc_client, &bob_pubkey); check_balance(10, &rpc_client, &bob_pubkey);
// Verify that nonce has been used // Verify that nonce has been used
let nonce_hash2 = nonce::get_account(&rpc_client, &nonce_account.pubkey()) let nonce_hash2 = nonce_utils::get_account(&rpc_client, &nonce_account.pubkey())
.and_then(|ref a| nonce::data_from_account(a)) .and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap() .unwrap()
.blockhash; .blockhash;
assert_ne!(nonce_hash, nonce_hash2); assert_ne!(nonce_hash, nonce_hash2);

View File

@ -1,15 +1,14 @@
use solana_cli::test_utils::check_balance; use solana_cli::test_utils::check_balance;
use solana_cli::{ use solana_cli::{
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig}, 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, 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_core::validator::{TestValidator, TestValidatorOptions};
use solana_faucet::faucet::run_local_faucet; use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{ use solana_sdk::{
@ -502,8 +501,8 @@ fn test_nonced_stake_delegation_and_deactivation() {
process_command(&config).unwrap(); process_command(&config).unwrap();
// Fetch nonce hash // Fetch nonce hash
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey()) let nonce_hash = nonce_utils::get_account(&rpc_client, &nonce_account.pubkey())
.and_then(|ref a| nonce::data_from_account(a)) .and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap() .unwrap()
.blockhash; .blockhash;
@ -526,8 +525,8 @@ fn test_nonced_stake_delegation_and_deactivation() {
process_command(&config).unwrap(); process_command(&config).unwrap();
// Fetch nonce hash // Fetch nonce hash
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey()) let nonce_hash = nonce_utils::get_account(&rpc_client, &nonce_account.pubkey())
.and_then(|ref a| nonce::data_from_account(a)) .and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap() .unwrap()
.blockhash; .blockhash;
@ -742,8 +741,8 @@ fn test_stake_authorize() {
process_command(&config).unwrap(); process_command(&config).unwrap();
// Fetch nonce hash // Fetch nonce hash
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey()) let nonce_hash = nonce_utils::get_account(&rpc_client, &nonce_account.pubkey())
.and_then(|ref a| nonce::data_from_account(a)) .and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap() .unwrap()
.blockhash; .blockhash;
@ -788,8 +787,8 @@ fn test_stake_authorize() {
}; };
assert_eq!(current_authority, online_authority_pubkey); assert_eq!(current_authority, online_authority_pubkey);
let new_nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey()) let new_nonce_hash = nonce_utils::get_account(&rpc_client, &nonce_account.pubkey())
.and_then(|ref a| nonce::data_from_account(a)) .and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap() .unwrap()
.blockhash; .blockhash;
assert_ne!(nonce_hash, new_nonce_hash); 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()); check_balance(minimum_nonce_balance, &rpc_client, &nonce_account.pubkey());
// Fetch nonce hash // Fetch nonce hash
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey()) let nonce_hash = nonce_utils::get_account(&rpc_client, &nonce_account.pubkey())
.and_then(|ref a| nonce::data_from_account(a)) .and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap() .unwrap()
.blockhash; .blockhash;
@ -1281,8 +1280,8 @@ fn test_stake_set_lockup() {
check_balance(minimum_nonce_balance, &rpc_client, &nonce_account_pubkey); check_balance(minimum_nonce_balance, &rpc_client, &nonce_account_pubkey);
// Fetch nonce hash // Fetch nonce hash
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey()) let nonce_hash = nonce_utils::get_account(&rpc_client, &nonce_account.pubkey())
.and_then(|ref a| nonce::data_from_account(a)) .and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap() .unwrap()
.blockhash; .blockhash;
@ -1400,8 +1399,8 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
process_command(&config).unwrap(); process_command(&config).unwrap();
// Fetch nonce hash // Fetch nonce hash
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey()) let nonce_hash = nonce_utils::get_account(&rpc_client, &nonce_account.pubkey())
.and_then(|ref a| nonce::data_from_account(a)) .and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap() .unwrap()
.blockhash; .blockhash;
@ -1451,8 +1450,8 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
check_balance(50_000, &rpc_client, &stake_pubkey); check_balance(50_000, &rpc_client, &stake_pubkey);
// Fetch nonce hash // Fetch nonce hash
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey()) let nonce_hash = nonce_utils::get_account(&rpc_client, &nonce_account.pubkey())
.and_then(|ref a| nonce::data_from_account(a)) .and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap() .unwrap()
.blockhash; .blockhash;
@ -1495,8 +1494,8 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
check_balance(42, &rpc_client, &recipient_pubkey); check_balance(42, &rpc_client, &recipient_pubkey);
// Fetch nonce hash // Fetch nonce hash
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey()) let nonce_hash = nonce_utils::get_account(&rpc_client, &nonce_account.pubkey())
.and_then(|ref a| nonce::data_from_account(a)) .and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap() .unwrap()
.blockhash; .blockhash;

View File

@ -1,15 +1,14 @@
use solana_cli::test_utils::check_balance; use solana_cli::test_utils::check_balance;
use solana_cli::{ use solana_cli::{
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig}, 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, 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_core::validator::{TestValidator, TestValidatorOptions};
use solana_faucet::faucet::run_local_faucet; use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{ use solana_sdk::{
@ -147,8 +146,8 @@ fn test_transfer() {
check_balance(49_987 - minimum_nonce_balance, &rpc_client, &sender_pubkey); check_balance(49_987 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
// Fetch nonce hash // Fetch nonce hash
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey()) let nonce_hash = nonce_utils::get_account(&rpc_client, &nonce_account.pubkey())
.and_then(|ref a| nonce::data_from_account(a)) .and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap() .unwrap()
.blockhash; .blockhash;
@ -171,8 +170,8 @@ fn test_transfer() {
process_command(&config).unwrap(); process_command(&config).unwrap();
check_balance(49_976 - minimum_nonce_balance, &rpc_client, &sender_pubkey); check_balance(49_976 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
check_balance(30, &rpc_client, &recipient_pubkey); check_balance(30, &rpc_client, &recipient_pubkey);
let new_nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey()) let new_nonce_hash = nonce_utils::get_account(&rpc_client, &nonce_account.pubkey())
.and_then(|ref a| nonce::data_from_account(a)) .and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap() .unwrap()
.blockhash; .blockhash;
assert_ne!(nonce_hash, new_nonce_hash); 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); check_balance(49_975 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
// Fetch nonce hash // Fetch nonce hash
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey()) let nonce_hash = nonce_utils::get_account(&rpc_client, &nonce_account.pubkey())
.and_then(|ref a| nonce::data_from_account(a)) .and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap() .unwrap()
.blockhash; .blockhash;

View File

@ -1,10 +1,12 @@
use solana_cli::{ use solana_cli::{
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig}, cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
offline::{blockhash_query::BlockhashQuery, *},
spend_utils::SpendAmount, spend_utils::SpendAmount,
test_utils::check_balance, 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_core::validator::TestValidator;
use solana_faucet::faucet::run_local_faucet; use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{ use solana_sdk::{

View File

@ -11,6 +11,7 @@ edition = "2018"
[dependencies] [dependencies]
bincode = "1.3.1" bincode = "1.3.1"
bs58 = "0.3.1" bs58 = "0.3.1"
clap = "2.33.0"
indicatif = "0.14.0" indicatif = "0.14.0"
jsonrpc-core = "14.1.0" jsonrpc-core = "14.1.0"
log = "0.4.8" log = "0.4.8"
@ -20,6 +21,7 @@ serde = "1.0.110"
serde_derive = "1.0.103" serde_derive = "1.0.103"
serde_json = "1.0.53" serde_json = "1.0.53"
solana-account-decoder = { path = "../account-decoder", version = "1.2.30" } 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-net-utils = { path = "../net-utils", version = "1.2.30" }
solana-sdk = { path = "../sdk", version = "1.2.30" } solana-sdk = { path = "../sdk", version = "1.2.30" }
solana-transaction-status = { path = "../transaction-status", version = "1.2.30" } solana-transaction-status = { path = "../transaction-status", version = "1.2.30" }

View File

@ -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)] #[derive(Debug, PartialEq)]
pub enum Source { pub enum Source {
@ -17,8 +24,8 @@ impl Source {
Ok(res) Ok(res)
} }
Self::NonceAccount(ref pubkey) => { Self::NonceAccount(ref pubkey) => {
let data = nonce::get_account(rpc_client, pubkey) let data = nonce_utils::get_account(rpc_client, pubkey)
.and_then(|ref a| nonce::data_from_account(a))?; .and_then(|ref a| nonce_utils::data_from_account(a))?;
Ok((data.blockhash, data.fee_calculator)) Ok((data.blockhash, data.fee_calculator))
} }
} }
@ -35,8 +42,8 @@ impl Source {
Ok(res) Ok(res)
} }
Self::NonceAccount(ref pubkey) => { Self::NonceAccount(ref pubkey) => {
let res = nonce::get_account(rpc_client, pubkey) let res = nonce_utils::get_account(rpc_client, pubkey)
.and_then(|ref a| nonce::data_from_account(a)) .and_then(|ref a| nonce_utils::data_from_account(a))
.and_then(|d| { .and_then(|d| {
if d.blockhash == *blockhash { if d.blockhash == *blockhash {
Ok(Some(d.fee_calculator)) Ok(Some(d.fee_calculator))
@ -73,7 +80,7 @@ impl BlockhashQuery {
pub fn new_from_matches(matches: &ArgMatches<'_>) -> Self { pub fn new_from_matches(matches: &ArgMatches<'_>) -> Self {
let blockhash = value_of(matches, BLOCKHASH_ARG.name); let blockhash = value_of(matches, BLOCKHASH_ARG.name);
let sign_only = matches.is_present(SIGN_ONLY_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) BlockhashQuery::new(blockhash, sign_only, nonce_account)
} }
@ -103,17 +110,15 @@ impl Default for BlockhashQuery {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::{nonce::nonce_arg, offline::blockhash_query::BlockhashQuery}; use crate::{
use clap::App; blockhash_query,
use serde_json::{self, json, Value};
use solana_account_decoder::{UiAccount, UiAccountEncoding};
use solana_client::{
rpc_request::RpcRequest, rpc_request::RpcRequest,
rpc_response::{Response, RpcFeeCalculator, RpcResponseContext}, rpc_response::{Response, RpcFeeCalculator, RpcResponseContext},
}; };
use solana_sdk::{ use clap::App;
account::Account, fee_calculator::FeeCalculator, hash::hash, nonce, system_program, 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; use std::collections::HashMap;
#[test] #[test]
@ -166,9 +171,7 @@ mod tests {
#[test] #[test]
fn test_blockhash_query_new_from_matches_ok() { fn test_blockhash_query_new_from_matches_ok() {
let test_commands = App::new("blockhash_query_test") let test_commands = App::new("blockhash_query_test").nonce_args().offline_args();
.arg(nonce_arg())
.offline_args();
let blockhash = hash(&[1u8]); let blockhash = hash(&[1u8]);
let blockhash_string = blockhash.to_string(); let blockhash_string = blockhash.to_string();

View File

@ -1,9 +1,11 @@
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
pub mod blockhash_query;
pub mod client_error; pub mod client_error;
pub mod http_sender; pub mod http_sender;
pub mod mock_sender; pub mod mock_sender;
pub mod nonce_utils;
pub mod perf_utils; pub mod perf_utils;
pub mod pubsub_client; pub mod pubsub_client;
pub mod rpc_client; pub mod rpc_client;

68
client/src/nonce_utils.rs Normal file
View File

@ -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<Account, Error> {
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<State, Error> {
account_identity_ok(account)?;
StateMut::<Versions>::state(account)
.map_err(|_| Error::InvalidAccountData)
.map(|v| v.convert_to_current())
}
pub fn data_from_account(account: &Account) -> Result<Data, Error> {
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),
}
}

View File

@ -20,7 +20,7 @@ regex = "1"
serde_json = "1.0.53" serde_json = "1.0.53"
serde_yaml = "0.8.12" serde_yaml = "0.8.12"
solana-clap-utils = { path = "../clap-utils", version = "1.2.30" } 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-ledger = { path = "../ledger", version = "1.2.30" }
solana-logger = { path = "../logger", version = "1.2.30" } solana-logger = { path = "../logger", version = "1.2.30" }
solana-measure = { path = "../measure", version = "1.2.30" } solana-measure = { path = "../measure", version = "1.2.30" }

View File

@ -4,7 +4,7 @@ use solana_clap_utils::{
input_parsers::pubkey_of, input_parsers::pubkey_of,
input_validators::{is_slot, is_valid_pubkey}, 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_ledger::{blockstore::Blockstore, blockstore_db::AccessType};
use solana_sdk::{clock::Slot, pubkey::Pubkey, signature::Signature}; use solana_sdk::{clock::Slot, pubkey::Pubkey, signature::Signature};
use solana_transaction_status::UiTransactionEncoding; use solana_transaction_status::UiTransactionEncoding;

View File

@ -124,7 +124,7 @@ fn output_slot(
}) })
.map(|transaction_status| transaction_status.into()); .map(|transaction_status| transaction_status.into());
solana_cli::display::println_transaction( solana_cli_output::display::println_transaction(
&transaction, &transaction,
&transaction_status, &transaction_status,
" ", " ",

View File

@ -13,13 +13,7 @@ use std::ffi::OsString;
use std::process::exit; use std::process::exit;
fn fee_payer_arg<'a, 'b>() -> Arg<'a, 'b> { fn fee_payer_arg<'a, 'b>() -> Arg<'a, 'b> {
Arg::with_name("fee_payer") solana_clap_utils::fee_payer::fee_payer_arg().required(true)
.long("fee-payer")
.required(true)
.takes_value(true)
.value_name("KEYPAIR")
.validator(is_valid_signer)
.help("Fee payer")
} }
fn funding_keypair_arg<'a, 'b>() -> Arg<'a, 'b> { fn funding_keypair_arg<'a, 'b>() -> Arg<'a, 'b> {

View File

@ -15,6 +15,7 @@ serde_yaml = "0.8.12"
solana-clap-utils = { path = "../clap-utils", version = "1.2.30" } solana-clap-utils = { path = "../clap-utils", version = "1.2.30" }
solana-client = { path = "../client", version = "1.2.30" } solana-client = { path = "../client", version = "1.2.30" }
solana-cli-config = { path = "../cli-config", 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-logger = { path = "../logger", version = "1.2.30" }
solana-metrics = { path = "../metrics", version = "1.2.30" } solana-metrics = { path = "../metrics", version = "1.2.30" }
solana-notifier = { path = "../notifier", version = "1.2.30" } solana-notifier = { path = "../notifier", version = "1.2.30" }

View File

@ -14,7 +14,7 @@ log = "0.4.8"
humantime = "2.0.0" humantime = "2.0.0"
solana-clap-utils = { path = "../clap-utils", 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-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-client = { path = "../client", version = "1.2.30" }
solana-logger = { path = "../logger", version = "1.2.30" } solana-logger = { path = "../logger", version = "1.2.30" }
solana-metrics = { path = "../metrics", version = "1.2.30" } solana-metrics = { path = "../metrics", version = "1.2.30" }

View File

@ -184,7 +184,7 @@ fn process_confirmed_block(notifier: &Notifier, slot: Slot, confirmed_block: Con
if notify { if notify {
let mut w = Vec::new(); let mut w = Vec::new();
if solana_cli::display::write_transaction( if solana_cli_output::display::write_transaction(
&mut w, &mut w,
&transaction, &transaction,
&rpc_transaction.meta, &rpc_transaction.meta,