diff --git a/clap-utils/src/input_validators.rs b/clap-utils/src/input_validators.rs index 26907f5374..948765b2dd 100644 --- a/clap-utils/src/input_validators.rs +++ b/clap-utils/src/input_validators.rs @@ -43,6 +43,11 @@ pub fn is_pubkey_or_keypair(string: String) -> Result<(), String> { is_pubkey(string.clone()).or_else(|_| is_keypair(string)) } +// Return an error if string cannot be parsed as pubkey or keypair file or keypair ask keyword +pub fn is_pubkey_or_keypair_or_ask_keyword(string: String) -> Result<(), String> { + is_pubkey(string.clone()).or_else(|_| is_keypair_or_ask_keyword(string)) +} + // Return an error if string cannot be parsed as pubkey=signature string pub fn is_pubkey_sig(string: String) -> Result<(), String> { let mut signer = string.split('='); diff --git a/cli/src/cli.rs b/cli/src/cli.rs index 3661c5b055..39584e2430 100644 --- a/cli/src/cli.rs +++ b/cli/src/cli.rs @@ -72,6 +72,80 @@ impl std::ops::Deref for KeypairEq { } } +#[derive(Debug)] +pub enum SigningAuthority { + Online(Keypair), + // We hold a random keypair alongside our legit pubkey in order + // to generate a placeholder signature in the transaction + Offline(Pubkey, Keypair), +} + +impl SigningAuthority { + pub fn new_from_matches( + matches: &ArgMatches<'_>, + name: &str, + signers: Option<&[(Pubkey, Signature)]>, + ) -> Result { + keypair_of(matches, name) + .map(|keypair| keypair.into()) + .or_else(|| { + pubkey_of(matches, name) + .filter(|pubkey| { + signers + .and_then(|signers| { + signers.iter().find(|(signer, _sig)| *signer == *pubkey) + }) + .is_some() + }) + .map(|pubkey| pubkey.into()) + }) + .ok_or_else(|| CliError::BadParameter("Invalid authority".to_string())) + } + + pub fn keypair(&self) -> &Keypair { + match self { + SigningAuthority::Online(keypair) => keypair, + SigningAuthority::Offline(_pubkey, keypair) => keypair, + } + } + + pub fn pubkey(&self) -> Pubkey { + match self { + SigningAuthority::Online(keypair) => keypair.pubkey(), + SigningAuthority::Offline(pubkey, _keypair) => *pubkey, + } + } +} + +impl From for SigningAuthority { + fn from(keypair: Keypair) -> Self { + SigningAuthority::Online(keypair) + } +} + +impl From for SigningAuthority { + fn from(pubkey: Pubkey) -> Self { + SigningAuthority::Offline(pubkey, Keypair::new()) + } +} + +impl PartialEq for SigningAuthority { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (SigningAuthority::Online(keypair1), SigningAuthority::Online(keypair2)) => { + keypair1.pubkey() == keypair2.pubkey() + } + (SigningAuthority::Online(keypair), SigningAuthority::Offline(pubkey, _)) + | (SigningAuthority::Offline(pubkey, _), SigningAuthority::Online(keypair)) => { + keypair.pubkey() == *pubkey + } + (SigningAuthority::Offline(pubkey1, _), SigningAuthority::Offline(pubkey2, _)) => { + pubkey1 == pubkey2 + } + } + } +} + #[derive(Default, Debug, PartialEq)] pub struct PayCommand { pub lamports: u64, @@ -84,7 +158,7 @@ pub struct PayCommand { pub signers: Option>, pub blockhash: Option, pub nonce_account: Option, - pub nonce_authority: Option, + pub nonce_authority: Option, } #[derive(Debug, PartialEq)] @@ -136,7 +210,7 @@ pub enum CliCommand { // Nonce commands AuthorizeNonceAccount { nonce_account: Pubkey, - nonce_authority: Option, + nonce_authority: Option, new_authority: Pubkey, }, CreateNonceAccount { @@ -148,7 +222,7 @@ pub enum CliCommand { GetNonce(Pubkey), NewNonce { nonce_account: Pubkey, - nonce_authority: Option, + nonce_authority: Option, }, ShowNonceAccount { nonce_account_pubkey: Pubkey, @@ -156,7 +230,7 @@ pub enum CliCommand { }, WithdrawFromNonceAccount { nonce_account: Pubkey, - nonce_authority: Option, + nonce_authority: Option, destination_account_pubkey: Pubkey, lamports: u64, }, @@ -173,23 +247,23 @@ pub enum CliCommand { }, DeactivateStake { stake_account_pubkey: Pubkey, - stake_authority: Option, + stake_authority: Option, sign_only: bool, signers: Option>, blockhash: Option, nonce_account: Option, - nonce_authority: Option, + nonce_authority: Option, }, DelegateStake { stake_account_pubkey: Pubkey, vote_account_pubkey: Pubkey, - stake_authority: Option, + stake_authority: Option, force: bool, sign_only: bool, signers: Option>, blockhash: Option, nonce_account: Option, - nonce_authority: Option, + nonce_authority: Option, }, RedeemVoteCredits(Pubkey, Pubkey), ShowStakeHistory { @@ -203,18 +277,18 @@ pub enum CliCommand { stake_account_pubkey: Pubkey, new_authorized_pubkey: Pubkey, stake_authorize: StakeAuthorize, - authority: Option, + authority: Option, sign_only: bool, signers: Option>, blockhash: Option, nonce_account: Option, - nonce_authority: Option, + nonce_authority: Option, }, WithdrawStake { stake_account_pubkey: Pubkey, destination_account_pubkey: Pubkey, lamports: u64, - withdraw_authority: Option, + withdraw_authority: Option, }, // Storage Commands CreateStorageAccount { @@ -528,11 +602,11 @@ pub fn parse_command(matches: &ArgMatches<'_>) -> Result>, blockhash: Option, nonce_account: Option, - nonce_authority: Option<&Keypair>, + nonce_authority: Option<&SigningAuthority>, ) -> ProcessResult { check_unique_pubkeys( (&config.keypair.pubkey(), "cli keypair".to_string()), @@ -946,7 +1020,9 @@ fn process_pay( if timestamp == None && *witnesses == None { let mut tx = if let Some(nonce_account) = &nonce_account { - let nonce_authority: &Keypair = nonce_authority.unwrap_or(&config.keypair); + let nonce_authority: &Keypair = nonce_authority + .map(|authority| authority.keypair()) + .unwrap_or(&config.keypair); system_transaction::nonced_transfer( &config.keypair, to, @@ -967,9 +1043,11 @@ fn process_pay( return_signers(&tx) } else { if let Some(nonce_account) = &nonce_account { - let nonce_authority: &Keypair = nonce_authority.unwrap_or(&config.keypair); + let nonce_authority: Pubkey = nonce_authority + .map(|authority| authority.pubkey()) + .unwrap_or_else(|| config.keypair.pubkey()); let nonce_account = rpc_client.get_account(nonce_account)?; - check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &blockhash)?; + check_nonce_account(&nonce_account, &nonce_authority, &blockhash)?; } check_account_for_fee( rpc_client, @@ -1222,7 +1300,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult { &rpc_client, config, nonce_account, - nonce_authority.as_deref(), + nonce_authority.as_ref(), new_authority, ), // Create nonce account @@ -1247,12 +1325,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult { CliCommand::NewNonce { nonce_account, ref nonce_authority, - } => process_new_nonce( - &rpc_client, - config, - nonce_account, - nonce_authority.as_deref(), - ), + } => process_new_nonce(&rpc_client, config, nonce_account, nonce_authority.as_ref()), // Show the contents of a nonce account CliCommand::ShowNonceAccount { nonce_account_pubkey, @@ -1268,7 +1341,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult { &rpc_client, config, &nonce_account, - nonce_authority.as_deref(), + nonce_authority.as_ref(), &destination_account_pubkey, *lamports, ), @@ -1313,12 +1386,12 @@ pub fn process_command(config: &CliConfig) -> ProcessResult { &rpc_client, config, &stake_account_pubkey, - stake_authority.as_deref(), + stake_authority.as_ref(), *sign_only, signers, *blockhash, *nonce_account, - nonce_authority.as_deref(), + nonce_authority.as_ref(), ), CliCommand::DelegateStake { stake_account_pubkey, @@ -1335,13 +1408,13 @@ pub fn process_command(config: &CliConfig) -> ProcessResult { config, &stake_account_pubkey, &vote_account_pubkey, - stake_authority.as_deref(), + stake_authority.as_ref(), *force, *sign_only, signers, *blockhash, *nonce_account, - nonce_authority.as_deref(), + nonce_authority.as_ref(), ), CliCommand::RedeemVoteCredits(stake_account_pubkey, vote_account_pubkey) => { process_redeem_vote_credits( @@ -1379,12 +1452,12 @@ pub fn process_command(config: &CliConfig) -> ProcessResult { &stake_account_pubkey, &new_authorized_pubkey, *stake_authorize, - authority.as_deref(), + authority.as_ref(), *sign_only, signers, *blockhash, *nonce_account, - nonce_authority.as_deref(), + nonce_authority.as_ref(), ), CliCommand::WithdrawStake { @@ -1398,7 +1471,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult { &stake_account_pubkey, &destination_account_pubkey, *lamports, - withdraw_authority.as_deref(), + withdraw_authority.as_ref(), ), // Storage Commands @@ -1570,7 +1643,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult { signers, *blockhash, *nonce_account, - nonce_authority.as_deref(), + nonce_authority.as_ref(), ), CliCommand::ShowAccount { pubkey, @@ -1926,7 +1999,7 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, ' .long(NONCE_AUTHORITY_ARG.long) .takes_value(true) .requires(NONCE_ARG.name) - .validator(is_keypair_or_ask_keyword) + .validator(is_pubkey_or_keypair_or_ask_keyword) .help(NONCE_AUTHORITY_ARG.help), ) .arg( @@ -2483,6 +2556,67 @@ mod tests { } ); + // Test Pay Subcommand w/ Nonce and Offline Nonce Authority + let keypair = read_keypair_file(&keypair_file).unwrap(); + let authority_pubkey = keypair.pubkey(); + let authority_pubkey_string = format!("{}", authority_pubkey); + let sig = keypair.sign_message(&[0u8]); + let signer_arg = format!("{}={}", authority_pubkey, sig); + let test_pay = test_commands.clone().get_matches_from(vec![ + "test", + "pay", + &pubkey_string, + "50", + "lamports", + "--blockhash", + &blockhash_string, + "--nonce", + &pubkey_string, + "--nonce-authority", + &authority_pubkey_string, + "--signer", + &signer_arg, + ]); + assert_eq!( + parse_command(&test_pay).unwrap(), + CliCommandInfo { + command: CliCommand::Pay(PayCommand { + lamports: 50, + to: pubkey, + blockhash: Some(blockhash), + nonce_account: Some(pubkey), + nonce_authority: Some(authority_pubkey.into()), + signers: Some(vec![(authority_pubkey, sig)]), + ..PayCommand::default() + }), + require_keypair: true + } + ); + + // Test Pay Subcommand w/ Nonce and Offline Nonce Authority + // authority pubkey not in signers fails + let keypair = read_keypair_file(&keypair_file).unwrap(); + let authority_pubkey = keypair.pubkey(); + let authority_pubkey_string = format!("{}", authority_pubkey); + let sig = keypair.sign_message(&[0u8]); + let signer_arg = format!("{}={}", Pubkey::new_rand(), sig); + let test_pay = test_commands.clone().get_matches_from(vec![ + "test", + "pay", + &pubkey_string, + "50", + "lamports", + "--blockhash", + &blockhash_string, + "--nonce", + &pubkey_string, + "--nonce-authority", + &authority_pubkey_string, + "--signer", + &signer_arg, + ]); + assert!(parse_command(&test_pay).is_err()); + // Test Send-Signature Subcommand let test_send_signature = test_commands.clone().get_matches_from(vec![ "test", diff --git a/cli/src/nonce.rs b/cli/src/nonce.rs index e4e5b02640..9038b886b0 100644 --- a/cli/src/nonce.rs +++ b/cli/src/nonce.rs @@ -1,7 +1,7 @@ use crate::cli::{ build_balance_message, check_account_for_fee, check_unique_pubkeys, log_instruction_custom_error, required_lamports_from, CliCommand, CliCommandInfo, CliConfig, - CliError, ProcessResult, + CliError, ProcessResult, SigningAuthority, }; use clap::{App, Arg, ArgMatches, SubCommand}; use solana_clap_utils::{input_parsers::*, input_validators::*, ArgConstant}; @@ -54,7 +54,7 @@ fn nonce_authority_arg<'a, 'b>() -> Arg<'a, 'b> { .long("nonce-authority") .takes_value(true) .value_name("KEYPAIR") - .validator(is_keypair_or_ask_keyword) + .validator(is_pubkey_or_keypair_or_ask_keyword) .help("Specify nonce authority if different from account") } @@ -222,7 +222,15 @@ impl NonceSubCommands for App<'_, '_> { pub fn parse_authorize_nonce_account(matches: &ArgMatches<'_>) -> Result { let nonce_account = pubkey_of(matches, "nonce_account_keypair").unwrap(); let new_authority = pubkey_of(matches, "new_authority").unwrap(); - let nonce_authority = keypair_of(matches, "nonce_authority").map(|kp| kp.into()); + let nonce_authority = if matches.is_present(NONCE_AUTHORITY_ARG.name) { + Some(SigningAuthority::new_from_matches( + &matches, + NONCE_AUTHORITY_ARG.name, + None, + )?) + } else { + None + }; Ok(CliCommandInfo { command: CliCommand::AuthorizeNonceAccount { @@ -262,7 +270,15 @@ pub fn parse_get_nonce(matches: &ArgMatches<'_>) -> Result) -> Result { let nonce_account = pubkey_of(matches, "nonce_account_keypair").unwrap(); - let nonce_authority = keypair_of(matches, "nonce_authority").map(|kp| kp.into()); + let nonce_authority = if matches.is_present(NONCE_AUTHORITY_ARG.name) { + Some(SigningAuthority::new_from_matches( + &matches, + NONCE_AUTHORITY_ARG.name, + None, + )?) + } else { + None + }; Ok(CliCommandInfo { command: CliCommand::NewNonce { @@ -292,7 +308,15 @@ pub fn parse_withdraw_from_nonce_account( let nonce_account = pubkey_of(matches, "nonce_account_keypair").unwrap(); let destination_account_pubkey = pubkey_of(matches, "destination_account_pubkey").unwrap(); let lamports = required_lamports_from(matches, "amount", "unit")?; - let nonce_authority = keypair_of(matches, "nonce_authority").map(|kp| kp.into()); + let nonce_authority = if matches.is_present(NONCE_AUTHORITY_ARG.name) { + Some(SigningAuthority::new_from_matches( + &matches, + NONCE_AUTHORITY_ARG.name, + None, + )?) + } else { + None + }; Ok(CliCommandInfo { command: CliCommand::WithdrawFromNonceAccount { @@ -337,12 +361,14 @@ pub fn process_authorize_nonce_account( rpc_client: &RpcClient, config: &CliConfig, nonce_account: &Pubkey, - nonce_authority: Option<&Keypair>, + nonce_authority: Option<&SigningAuthority>, new_authority: &Pubkey, ) -> ProcessResult { let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; - let nonce_authority = nonce_authority.unwrap_or(&config.keypair); + let nonce_authority = nonce_authority + .map(|a| a.keypair()) + .unwrap_or(&config.keypair); let ix = nonce_authorize(nonce_account, &nonce_authority.pubkey(), new_authority); let mut tx = Transaction::new_signed_with_payer( vec![ix], @@ -472,7 +498,7 @@ pub fn process_new_nonce( rpc_client: &RpcClient, config: &CliConfig, nonce_account: &Pubkey, - nonce_authority: Option<&Keypair>, + nonce_authority: Option<&SigningAuthority>, ) -> ProcessResult { check_unique_pubkeys( (&config.keypair.pubkey(), "cli keypair".to_string()), @@ -486,7 +512,9 @@ pub fn process_new_nonce( .into()); } - let nonce_authority = nonce_authority.unwrap_or(&config.keypair); + let nonce_authority = nonce_authority + .map(|a| a.keypair()) + .unwrap_or(&config.keypair); let ix = nonce_advance(&nonce_account, &nonce_authority.pubkey()); let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; let mut tx = Transaction::new_signed_with_payer( @@ -553,13 +581,15 @@ pub fn process_withdraw_from_nonce_account( rpc_client: &RpcClient, config: &CliConfig, nonce_account: &Pubkey, - nonce_authority: Option<&Keypair>, + nonce_authority: Option<&SigningAuthority>, destination_account_pubkey: &Pubkey, lamports: u64, ) -> ProcessResult { let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; - let nonce_authority = nonce_authority.unwrap_or(&config.keypair); + let nonce_authority = nonce_authority + .map(|a| a.keypair()) + .unwrap_or(&config.keypair); let ix = nonce_withdraw( nonce_account, &nonce_authority.pubkey(), diff --git a/cli/src/stake.rs b/cli/src/stake.rs index 61b1f4bd07..f6557d7c3d 100644 --- a/cli/src/stake.rs +++ b/cli/src/stake.rs @@ -3,7 +3,7 @@ use crate::{ build_balance_message, check_account_for_fee, check_unique_pubkeys, get_blockhash_fee_calculator, log_instruction_custom_error, replace_signatures, required_lamports_from, return_signers, CliCommand, CliCommandInfo, CliConfig, CliError, - ProcessResult, + ProcessResult, SigningAuthority, }, nonce::{check_nonce_account, NONCE_ARG, NONCE_AUTHORITY_ARG}, }; @@ -48,7 +48,7 @@ fn stake_authority_arg<'a, 'b>() -> Arg<'a, 'b> { .long(STAKE_AUTHORITY_ARG.long) .takes_value(true) .value_name("KEYPAIR") - .validator(is_keypair_or_ask_keyword) + .validator(is_pubkey_or_keypair_or_ask_keyword) .help(STAKE_AUTHORITY_ARG.help) } @@ -57,7 +57,7 @@ fn withdraw_authority_arg<'a, 'b>() -> Arg<'a, 'b> { .long(WITHDRAW_AUTHORITY_ARG.long) .takes_value(true) .value_name("KEYPAIR") - .validator(is_keypair_or_ask_keyword) + .validator(is_pubkey_or_keypair_or_ask_keyword) .help(WITHDRAW_AUTHORITY_ARG.help) } @@ -518,23 +518,27 @@ pub fn parse_stake_create_account(matches: &ArgMatches<'_>) -> Result) -> Result { let stake_account_pubkey = pubkey_of(matches, "stake_account_pubkey").unwrap(); let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap(); - let stake_authority = if matches.is_present(STAKE_AUTHORITY_ARG.name) { - let authority = keypair_of(&matches, STAKE_AUTHORITY_ARG.name) - .ok_or_else(|| CliError::BadParameter("Invalid keypair for stake-authority".into()))?; - Some(authority.into()) - } else { - None - }; let force = matches.is_present("force"); let sign_only = matches.is_present("sign_only"); let signers = pubkeys_sigs_of(&matches, "signer"); let blockhash = value_of(matches, "blockhash"); let require_keypair = signers.is_none(); let nonce_account = pubkey_of(&matches, NONCE_ARG.name); + let stake_authority = if matches.is_present(STAKE_AUTHORITY_ARG.name) { + Some(SigningAuthority::new_from_matches( + &matches, + STAKE_AUTHORITY_ARG.name, + signers.as_deref(), + )?) + } else { + None + }; let nonce_authority = if matches.is_present(NONCE_AUTHORITY_ARG.name) { - let authority = keypair_of(&matches, NONCE_AUTHORITY_ARG.name) - .ok_or_else(|| CliError::BadParameter("Invalid keypair for nonce-authority".into()))?; - Some(authority.into()) + Some(SigningAuthority::new_from_matches( + &matches, + NONCE_AUTHORITY_ARG.name, + signers.as_deref(), + )?) } else { None }; @@ -565,22 +569,25 @@ pub fn parse_stake_authorize( StakeAuthorize::Staker => STAKE_AUTHORITY_ARG.name, StakeAuthorize::Withdrawer => WITHDRAW_AUTHORITY_ARG.name, }; + let sign_only = matches.is_present("sign_only"); + let signers = pubkeys_sigs_of(&matches, "signer"); let authority = if matches.is_present(authority_flag) { - let authority = keypair_of(&matches, authority_flag).ok_or_else(|| { - CliError::BadParameter(format!("Invalid keypair for {}", authority_flag)) - })?; - Some(authority.into()) + Some(SigningAuthority::new_from_matches( + &matches, + authority_flag, + signers.as_deref(), + )?) } else { None }; - let sign_only = matches.is_present("sign_only"); - let signers = pubkeys_sigs_of(&matches, "signer"); let blockhash = value_of(matches, "blockhash"); let nonce_account = pubkey_of(&matches, NONCE_ARG.name); let nonce_authority = if matches.is_present(NONCE_AUTHORITY_ARG.name) { - let authority = keypair_of(&matches, NONCE_AUTHORITY_ARG.name) - .ok_or_else(|| CliError::BadParameter("Invalid keypair for nonce-authority".into()))?; - Some(authority.into()) + Some(SigningAuthority::new_from_matches( + &matches, + NONCE_AUTHORITY_ARG.name, + signers.as_deref(), + )?) } else { None }; @@ -613,22 +620,26 @@ pub fn parse_redeem_vote_credits(matches: &ArgMatches<'_>) -> Result) -> Result { let stake_account_pubkey = pubkey_of(matches, "stake_account_pubkey").unwrap(); - let stake_authority = if matches.is_present(STAKE_AUTHORITY_ARG.name) { - let authority = keypair_of(&matches, STAKE_AUTHORITY_ARG.name) - .ok_or_else(|| CliError::BadParameter("Invalid keypair for stake-authority".into()))?; - Some(authority.into()) - } else { - None - }; let sign_only = matches.is_present("sign_only"); let signers = pubkeys_sigs_of(&matches, "signer"); let blockhash = value_of(matches, "blockhash"); let require_keypair = signers.is_none(); let nonce_account = pubkey_of(&matches, NONCE_ARG.name); + let stake_authority = if matches.is_present(STAKE_AUTHORITY_ARG.name) { + Some(SigningAuthority::new_from_matches( + &matches, + STAKE_AUTHORITY_ARG.name, + signers.as_deref(), + )?) + } else { + None + }; let nonce_authority = if matches.is_present(NONCE_AUTHORITY_ARG.name) { - let authority = keypair_of(&matches, NONCE_AUTHORITY_ARG.name) - .ok_or_else(|| CliError::BadParameter("Invalid keypair for nonce-authority".into()))?; - Some(authority.into()) + Some(SigningAuthority::new_from_matches( + &matches, + NONCE_AUTHORITY_ARG.name, + signers.as_deref(), + )?) } else { None }; @@ -652,10 +663,11 @@ pub fn parse_stake_withdraw_stake(matches: &ArgMatches<'_>) -> Result, + authority: Option<&SigningAuthority>, sign_only: bool, signers: &Option>, blockhash: Option, nonce_account: Option, - nonce_authority: Option<&Keypair>, + nonce_authority: Option<&SigningAuthority>, ) -> ProcessResult { check_unique_pubkeys( (stake_account_pubkey, "stake_account_pubkey".to_string()), (authorized_pubkey, "new_authorized_pubkey".to_string()), )?; - let authority = authority.unwrap_or(&config.keypair); + let authority = authority.map(|a| a.keypair()).unwrap_or(&config.keypair); let (recent_blockhash, fee_calculator) = get_blockhash_fee_calculator(rpc_client, sign_only, blockhash)?; let ixs = vec![stake_instruction::authorize( @@ -811,12 +823,14 @@ pub fn process_stake_authorize( stake_authorize, // stake or withdraw )]; + let (nonce_authority, nonce_authority_pubkey) = nonce_authority + .map(|a| (a.keypair(), a.pubkey())) + .unwrap_or((&config.keypair, config.keypair.pubkey())); let mut tx = if let Some(nonce_account) = &nonce_account { - let nonce_authority: &Keypair = nonce_authority.unwrap_or(&config.keypair); Transaction::new_signed_with_nonce( ixs, Some(&config.keypair.pubkey()), - &[&config.keypair, authority, nonce_authority], + &[&config.keypair, nonce_authority, authority], nonce_account, &nonce_authority.pubkey(), recent_blockhash, @@ -836,9 +850,8 @@ pub fn process_stake_authorize( return_signers(&tx) } else { if let Some(nonce_account) = &nonce_account { - let nonce_authority: &Keypair = nonce_authority.unwrap_or(&config.keypair); let nonce_account = rpc_client.get_account(nonce_account)?; - check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?; + check_nonce_account(&nonce_account, &nonce_authority_pubkey, &recent_blockhash)?; } check_account_for_fee( rpc_client, @@ -855,22 +868,26 @@ pub fn process_deactivate_stake_account( rpc_client: &RpcClient, config: &CliConfig, stake_account_pubkey: &Pubkey, - stake_authority: Option<&Keypair>, + stake_authority: Option<&SigningAuthority>, sign_only: bool, signers: &Option>, blockhash: Option, nonce_account: Option, - nonce_authority: Option<&Keypair>, + nonce_authority: Option<&SigningAuthority>, ) -> ProcessResult { let (recent_blockhash, fee_calculator) = get_blockhash_fee_calculator(rpc_client, sign_only, blockhash)?; - let stake_authority = stake_authority.unwrap_or(&config.keypair); + let stake_authority = stake_authority + .map(|a| a.keypair()) + .unwrap_or(&config.keypair); let ixs = vec![stake_instruction::deactivate_stake( stake_account_pubkey, &stake_authority.pubkey(), )]; + let (nonce_authority, nonce_authority_pubkey) = nonce_authority + .map(|a| (a.keypair(), a.pubkey())) + .unwrap_or((&config.keypair, config.keypair.pubkey())); let mut tx = if let Some(nonce_account) = &nonce_account { - let nonce_authority: &Keypair = nonce_authority.unwrap_or(&config.keypair); Transaction::new_signed_with_nonce( ixs, Some(&config.keypair.pubkey()), @@ -894,9 +911,8 @@ pub fn process_deactivate_stake_account( return_signers(&tx) } else { if let Some(nonce_account) = &nonce_account { - let nonce_authority: &Keypair = nonce_authority.unwrap_or(&config.keypair); let nonce_account = rpc_client.get_account(nonce_account)?; - check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?; + check_nonce_account(&nonce_account, &nonce_authority_pubkey, &recent_blockhash)?; } check_account_for_fee( rpc_client, @@ -915,10 +931,12 @@ pub fn process_withdraw_stake( stake_account_pubkey: &Pubkey, destination_account_pubkey: &Pubkey, lamports: u64, - withdraw_authority: Option<&Keypair>, + withdraw_authority: Option<&SigningAuthority>, ) -> ProcessResult { let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; - let withdraw_authority = withdraw_authority.unwrap_or(&config.keypair); + let withdraw_authority = withdraw_authority + .map(|a| a.keypair()) + .unwrap_or(&config.keypair); let ixs = vec![stake_instruction::withdraw( stake_account_pubkey, @@ -1097,19 +1115,21 @@ pub fn process_delegate_stake( config: &CliConfig, stake_account_pubkey: &Pubkey, vote_account_pubkey: &Pubkey, - stake_authority: Option<&Keypair>, + stake_authority: Option<&SigningAuthority>, force: bool, sign_only: bool, signers: &Option>, blockhash: Option, nonce_account: Option, - nonce_authority: Option<&Keypair>, + nonce_authority: Option<&SigningAuthority>, ) -> ProcessResult { check_unique_pubkeys( (&config.keypair.pubkey(), "cli keypair".to_string()), (stake_account_pubkey, "stake_account_pubkey".to_string()), )?; - let stake_authority = stake_authority.unwrap_or(&config.keypair); + let stake_authority = stake_authority + .map(|a| a.keypair()) + .unwrap_or(&config.keypair); // Sanity check the vote account to ensure it is attached to a validator that has recently // voted at the tip of the ledger @@ -1159,8 +1179,10 @@ pub fn process_delegate_stake( &stake_authority.pubkey(), vote_account_pubkey, )]; + let (nonce_authority, nonce_authority_pubkey) = nonce_authority + .map(|a| (a.keypair(), a.pubkey())) + .unwrap_or((&config.keypair, config.keypair.pubkey())); let mut tx = if let Some(nonce_account) = &nonce_account { - let nonce_authority: &Keypair = nonce_authority.unwrap_or(&config.keypair); Transaction::new_signed_with_nonce( ixs, Some(&config.keypair.pubkey()), @@ -1184,9 +1206,8 @@ pub fn process_delegate_stake( return_signers(&tx) } else { if let Some(nonce_account) = &nonce_account { - let nonce_authority: &Keypair = nonce_authority.unwrap_or(&config.keypair); let nonce_account = rpc_client.get_account(nonce_account)?; - check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?; + check_nonce_account(&nonce_account, &nonce_authority_pubkey, &recent_blockhash)?; } check_account_for_fee( rpc_client, diff --git a/cli/tests/nonce.rs b/cli/tests/nonce.rs index bda64a59ab..ce7856bac8 100644 --- a/cli/tests/nonce.rs +++ b/cli/tests/nonce.rs @@ -1,5 +1,5 @@ use solana_cli::cli::{ - process_command, request_and_confirm_airdrop, CliCommand, CliConfig, KeypairEq, + process_command, request_and_confirm_airdrop, CliCommand, CliConfig, SigningAuthority, }; use solana_client::rpc_client::RpcClient; use solana_faucet::faucet::run_local_faucet; @@ -141,7 +141,7 @@ fn test_nonce_with_authority() { remove_dir_all(ledger_path).unwrap(); } -fn read_keypair_from_option(keypair_file: &Option<&str>) -> Option { +fn read_keypair_from_option(keypair_file: &Option<&str>) -> Option { keypair_file.map(|akf| read_keypair_file(&akf).unwrap().into()) } @@ -175,7 +175,7 @@ fn full_battery_tests( nonce_account: read_keypair_file(&nonce_keypair_file).unwrap().into(), seed, nonce_authority: read_keypair_from_option(&authority_keypair_file) - .map(|na: KeypairEq| na.pubkey()), + .map(|na: SigningAuthority| na.pubkey()), lamports: 1000, }; diff --git a/cli/tests/stake.rs b/cli/tests/stake.rs index 9d6e805b99..a573239a9e 100644 --- a/cli/tests/stake.rs +++ b/cli/tests/stake.rs @@ -574,8 +574,7 @@ fn test_stake_authorize() { stake_account_pubkey, new_authorized_pubkey: nonced_authority_pubkey, stake_authorize: StakeAuthorize::Staker, - // We need to be able to specify the authority by pubkey/sig pair here - authority: Some(read_keypair_file(&offline_authority_file).unwrap().into()), + authority: Some(offline_authority_pubkey.into()), sign_only: false, signers: Some(signers), blockhash: Some(blockhash), @@ -614,7 +613,7 @@ fn test_stake_authorize() { _ => panic!("Nonce is not initialized"), }; - // Nonced assignment of new nonced stake authority + // Nonced assignment of new online stake authority let online_authority = Keypair::new(); let online_authority_pubkey = online_authority.pubkey(); let (_online_authority_file, mut tmp_file) = make_tmp_file(); @@ -629,7 +628,6 @@ fn test_stake_authorize() { blockhash: Some(nonce_hash), nonce_account: Some(nonce_account.pubkey()), nonce_authority: None, - //nonce_authority: Some(read_keypair_file(&nonce_keypair_file).unwrap().into()), }; let sign_reply = process_command(&config).unwrap(); let (blockhash, signers) = parse_sign_only_reply_string(&sign_reply); @@ -638,8 +636,7 @@ fn test_stake_authorize() { stake_account_pubkey, new_authorized_pubkey: online_authority_pubkey, stake_authorize: StakeAuthorize::Staker, - // We need to be able to specify the authority by pubkey/sig pair here - authority: Some(read_keypair_file(&nonced_authority_file).unwrap().into()), + authority: Some(nonced_authority_pubkey.into()), sign_only: false, signers: Some(signers), blockhash: Some(blockhash),