From 259289495852720544ca4622949d3587b1570f4e Mon Sep 17 00:00:00 2001 From: Trent Nelson Date: Sat, 21 Mar 2020 19:56:17 -0600 Subject: [PATCH] CLI: Support setting both stake authorities at once (#8976) automerge --- cli/src/cli.rs | 27 +- cli/src/stake.rs | 481 ++++++++++++++++++++--------- cli/tests/stake.rs | 67 ++-- docs/src/offline-signing/README.md | 3 +- 4 files changed, 378 insertions(+), 200 deletions(-) diff --git a/cli/src/cli.rs b/cli/src/cli.rs index 2ed5bc85ba..313927661e 100644 --- a/cli/src/cli.rs +++ b/cli/src/cli.rs @@ -304,9 +304,7 @@ pub enum CliCommand { }, StakeAuthorize { stake_account_pubkey: Pubkey, - new_authorized_pubkey: Pubkey, - stake_authorize: StakeAuthorize, - authority: SignerIndex, + new_authorizations: Vec<(StakeAuthorize, Pubkey, SignerIndex)>, sign_only: bool, blockhash_query: BlockhashQuery, nonce_account: Option, @@ -648,18 +646,9 @@ pub fn parse_command( ("split-stake", Some(matches)) => { parse_split_stake(matches, default_signer_path, wallet_manager) } - ("stake-authorize-staker", Some(matches)) => parse_stake_authorize( - matches, - default_signer_path, - wallet_manager, - StakeAuthorize::Staker, - ), - ("stake-authorize-withdrawer", Some(matches)) => parse_stake_authorize( - matches, - default_signer_path, - wallet_manager, - StakeAuthorize::Withdrawer, - ), + ("stake-authorize", Some(matches)) => { + parse_stake_authorize(matches, default_signer_path, wallet_manager) + } ("stake-set-lockup", Some(matches)) => { parse_stake_set_lockup(matches, default_signer_path, wallet_manager) } @@ -1832,9 +1821,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult { } CliCommand::StakeAuthorize { stake_account_pubkey, - new_authorized_pubkey, - stake_authorize, - authority, + ref new_authorizations, sign_only, blockhash_query, nonce_account, @@ -1844,9 +1831,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult { &rpc_client, config, &stake_account_pubkey, - &new_authorized_pubkey, - *stake_authorize, - *authority, + new_authorizations, *sign_only, blockhash_query, *nonce_account, diff --git a/cli/src/stake.rs b/cli/src/stake.rs index 7357f14fd5..786b1caa91 100644 --- a/cli/src/stake.rs +++ b/cli/src/stake.rs @@ -182,53 +182,36 @@ impl StakeSubCommands for App<'_, '_> { .arg(fee_payer_arg()) ) .subcommand( - SubCommand::with_name("stake-authorize-staker") - .about("Authorize a new stake signing keypair for the given stake account") + SubCommand::with_name("stake-authorize") + .about("Authorize a new signing keypair for the given stake account") .arg( Arg::with_name("stake_account_pubkey") - .index(1) - .value_name("STAKE_ACCOUNT_ADDRESS") - .takes_value(true) .required(true) + .index(1) + .takes_value(true) + .value_name("STAKE_ACCOUNT_ADDRESS") .validator(is_valid_pubkey) - .help("Stake account in which to set the authorized staker") + .help("Stake account in which to set a new authority") ) .arg( - Arg::with_name("authorized_pubkey") - .index(2) - .value_name("AUTHORIZED_PUBKEY") + Arg::with_name("new_stake_authority") + .long("new-stake-authority") + .required_unless("new_withdraw_authority") .takes_value(true) - .required(true) + .value_name("PUBKEY") .validator(is_valid_pubkey) .help("New authorized staker") ) - .arg(stake_authority_arg()) - .offline_args() - .arg(nonce_arg()) - .arg(nonce_authority_arg()) - .arg(fee_payer_arg()) - ) - .subcommand( - SubCommand::with_name("stake-authorize-withdrawer") - .about("Authorize a new withdraw signing keypair for the given stake account") .arg( - Arg::with_name("stake_account_pubkey") - .index(1) - .value_name("STAKE_ACCOUNT_ADDRESS") + Arg::with_name("new_withdraw_authority") + .long("new-withdraw-authority") + .required_unless("new_stake_authority") .takes_value(true) - .required(true) - .validator(is_valid_pubkey) - .help("Stake account in which to set the authorized withdrawer") - ) - .arg( - Arg::with_name("authorized_pubkey") - .index(2) - .value_name("AUTHORIZED_PUBKEY") - .takes_value(true) - .required(true) + .value_name("PUBKEY") .validator(is_valid_pubkey) .help("New authorized withdrawer") ) + .arg(stake_authority_arg()) .arg(withdraw_authority_arg()) .offline_args() .arg(nonce_arg()) @@ -515,37 +498,75 @@ pub fn parse_stake_authorize( matches: &ArgMatches<'_>, default_signer_path: &str, wallet_manager: Option<&Arc>, - stake_authorize: StakeAuthorize, ) -> Result { let stake_account_pubkey = pubkey_of_signer(matches, "stake_account_pubkey", wallet_manager)?.unwrap(); - let new_authorized_pubkey = - pubkey_of_signer(matches, "authorized_pubkey", wallet_manager)?.unwrap(); - let authority_flag = match stake_authorize { - StakeAuthorize::Staker => STAKE_AUTHORITY_ARG.name, - StakeAuthorize::Withdrawer => WITHDRAW_AUTHORITY_ARG.name, + + let mut new_authorizations = Vec::new(); + let mut bulk_signers = Vec::new(); + if let Some(new_authority_pubkey) = + pubkey_of_signer(matches, "new_stake_authority", wallet_manager)? + { + let (authority, authority_pubkey) = { + let (authority, authority_pubkey) = + signer_of(matches, STAKE_AUTHORITY_ARG.name, wallet_manager)?; + // Withdraw authority may also change the staker + if authority.is_none() { + signer_of(matches, WITHDRAW_AUTHORITY_ARG.name, wallet_manager)? + } else { + (authority, authority_pubkey) + } + }; + new_authorizations.push(( + StakeAuthorize::Staker, + new_authority_pubkey, + authority_pubkey, + )); + bulk_signers.push(authority); + }; + if let Some(new_authority_pubkey) = + pubkey_of_signer(matches, "new_withdraw_authority", wallet_manager)? + { + let (authority, authority_pubkey) = + signer_of(matches, WITHDRAW_AUTHORITY_ARG.name, wallet_manager)?; + new_authorizations.push(( + StakeAuthorize::Withdrawer, + new_authority_pubkey, + authority_pubkey, + )); + bulk_signers.push(authority); }; let sign_only = matches.is_present(SIGN_ONLY_ARG.name); - let (authority, authority_pubkey) = signer_of(matches, authority_flag, wallet_manager)?; let blockhash_query = BlockhashQuery::new_from_matches(matches); let nonce_account = pubkey_of(matches, NONCE_ARG.name); let (nonce_authority, nonce_authority_pubkey) = signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?; let (fee_payer, fee_payer_pubkey) = signer_of(matches, FEE_PAYER_ARG.name, wallet_manager)?; - let mut bulk_signers = vec![authority, fee_payer]; + bulk_signers.push(fee_payer); if nonce_account.is_some() { bulk_signers.push(nonce_authority); } let signer_info = generate_unique_signers(bulk_signers, matches, default_signer_path, wallet_manager)?; + let new_authorizations = new_authorizations + .into_iter() + .map( + |(stake_authorize, new_authority_pubkey, authority_pubkey)| { + ( + stake_authorize, + new_authority_pubkey, + signer_info.index_of(authority_pubkey).unwrap(), + ) + }, + ) + .collect(); + Ok(CliCommandInfo { command: CliCommand::StakeAuthorize { stake_account_pubkey, - new_authorized_pubkey, - stake_authorize, - authority: signer_info.index_of(authority_pubkey).unwrap(), + new_authorizations, sign_only, blockhash_query, nonce_account, @@ -871,28 +892,30 @@ pub fn process_stake_authorize( rpc_client: &RpcClient, config: &CliConfig, stake_account_pubkey: &Pubkey, - authorized_pubkey: &Pubkey, - stake_authorize: StakeAuthorize, - authority: SignerIndex, + new_authorizations: &[(StakeAuthorize, Pubkey, SignerIndex)], sign_only: bool, blockhash_query: &BlockhashQuery, nonce_account: Option, nonce_authority: SignerIndex, fee_payer: SignerIndex, ) -> ProcessResult { - check_unique_pubkeys( - (stake_account_pubkey, "stake_account_pubkey".to_string()), - (authorized_pubkey, "new_authorized_pubkey".to_string()), - )?; - let authority = config.signers[authority]; + let mut ixs = Vec::new(); + for (stake_authorize, authorized_pubkey, authority) in new_authorizations.iter() { + check_unique_pubkeys( + (stake_account_pubkey, "stake_account_pubkey".to_string()), + (authorized_pubkey, "new_authorized_pubkey".to_string()), + )?; + let authority = config.signers[*authority]; + ixs.push(stake_instruction::authorize( + stake_account_pubkey, // stake account to update + &authority.pubkey(), // currently authorized + authorized_pubkey, // new stake signer + *stake_authorize, // stake or withdraw + )); + } + let (recent_blockhash, fee_calculator) = blockhash_query.get_blockhash_and_fee_calculator(rpc_client)?; - let ixs = vec![stake_instruction::authorize( - stake_account_pubkey, // stake account to update - &authority.pubkey(), // currently authorized - authorized_pubkey, // new stake signer - stake_authorize, // stake or withdraw - )]; let nonce_authority = config.signers[nonce_authority]; let fee_payer = config.signers[fee_payer]; @@ -1486,83 +1509,279 @@ mod tests { (String::from(tmp_file.path().to_str().unwrap()), tmp_file) } - fn parse_authorize_tests( - test_commands: &App, - stake_account_pubkey: Pubkey, - authority_keypair_file: &str, - stake_authorize: StakeAuthorize, - ) { + #[test] + fn test_parse_command() { + let test_commands = app("test", "desc", "version"); let default_keypair = Keypair::new(); let (default_keypair_file, mut tmp_file) = make_tmp_file(); write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap(); + let (keypair_file, mut tmp_file) = make_tmp_file(); + let stake_account_keypair = Keypair::new(); + write_keypair(&stake_account_keypair, tmp_file.as_file_mut()).unwrap(); + let stake_account_pubkey = stake_account_keypair.pubkey(); + let (stake_authority_keypair_file, mut tmp_file) = make_tmp_file(); + let stake_authority_keypair = Keypair::new(); + write_keypair(&stake_authority_keypair, tmp_file.as_file_mut()).unwrap(); + // stake-authorize subcommand let stake_account_string = stake_account_pubkey.to_string(); - - let (subcommand, authority_flag) = match stake_authorize { - StakeAuthorize::Staker => ("stake-authorize-staker", "--stake-authority"), - StakeAuthorize::Withdrawer => ("stake-authorize-withdrawer", "--withdraw-authority"), - }; - - // Test Staker Subcommand - let test_authorize = test_commands.clone().get_matches_from(vec![ + let new_stake_authority = Pubkey::new(&[1u8; 32]); + let new_stake_string = new_stake_authority.to_string(); + let new_withdraw_authority = Pubkey::new(&[2u8; 32]); + let new_withdraw_string = new_withdraw_authority.to_string(); + let test_stake_authorize = test_commands.clone().get_matches_from(vec![ "test", - &subcommand, - &stake_account_string, + "stake-authorize", &stake_account_string, + "--new-stake-authority", + &new_stake_string, + "--new-withdraw-authority", + &new_withdraw_string, ]); assert_eq!( - parse_command(&test_authorize, &default_keypair_file, None).unwrap(), + parse_command(&test_stake_authorize, &default_keypair_file, None).unwrap(), CliCommandInfo { command: CliCommand::StakeAuthorize { stake_account_pubkey, - new_authorized_pubkey: stake_account_pubkey, - stake_authorize, - authority: 0, + new_authorizations: vec![ + (StakeAuthorize::Staker, new_stake_authority, 0,), + (StakeAuthorize::Withdrawer, new_withdraw_authority, 0,), + ], sign_only: false, - blockhash_query: BlockhashQuery::default(), + blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster), nonce_account: None, nonce_authority: 0, fee_payer: 0, }, - signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()], - } + signers: vec![read_keypair_file(&default_keypair_file).unwrap().into(),], + }, ); - // Test Staker Subcommand w/ authority - let test_authorize = test_commands.clone().get_matches_from(vec![ + let (withdraw_authority_keypair_file, mut tmp_file) = make_tmp_file(); + let withdraw_authority_keypair = Keypair::new(); + write_keypair(&withdraw_authority_keypair, tmp_file.as_file_mut()).unwrap(); + let test_stake_authorize = test_commands.clone().get_matches_from(vec![ "test", - &subcommand, + "stake-authorize", &stake_account_string, - &stake_account_string, - &authority_flag, - &authority_keypair_file, + "--new-stake-authority", + &new_stake_string, + "--new-withdraw-authority", + &new_withdraw_string, + "--stake-authority", + &stake_authority_keypair_file, + "--withdraw-authority", + &withdraw_authority_keypair_file, ]); assert_eq!( - parse_command(&test_authorize, &default_keypair_file, None).unwrap(), + parse_command(&test_stake_authorize, &default_keypair_file, None).unwrap(), CliCommandInfo { command: CliCommand::StakeAuthorize { stake_account_pubkey, - new_authorized_pubkey: stake_account_pubkey, - stake_authorize, - authority: 1, + new_authorizations: vec![ + (StakeAuthorize::Staker, new_stake_authority, 1,), + (StakeAuthorize::Withdrawer, new_withdraw_authority, 2,), + ], sign_only: false, - blockhash_query: BlockhashQuery::default(), + blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster), nonce_account: None, nonce_authority: 0, fee_payer: 0, }, signers: vec![ read_keypair_file(&default_keypair_file).unwrap().into(), - read_keypair_file(&authority_keypair_file).unwrap().into() + read_keypair_file(&stake_authority_keypair_file) + .unwrap() + .into(), + read_keypair_file(&withdraw_authority_keypair_file) + .unwrap() + .into(), ], - } + }, ); + // Withdraw authority may set both new authorities + let test_stake_authorize = test_commands.clone().get_matches_from(vec![ + "test", + "stake-authorize", + &stake_account_string, + "--new-stake-authority", + &new_stake_string, + "--new-withdraw-authority", + &new_withdraw_string, + "--withdraw-authority", + &withdraw_authority_keypair_file, + ]); + assert_eq!( + parse_command(&test_stake_authorize, &default_keypair_file, None).unwrap(), + CliCommandInfo { + command: CliCommand::StakeAuthorize { + stake_account_pubkey, + new_authorizations: vec![ + (StakeAuthorize::Staker, new_stake_authority, 1,), + (StakeAuthorize::Withdrawer, new_withdraw_authority, 1,), + ], + sign_only: false, + blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster), + nonce_account: None, + nonce_authority: 0, + fee_payer: 0, + }, + signers: vec![ + read_keypair_file(&default_keypair_file).unwrap().into(), + read_keypair_file(&withdraw_authority_keypair_file) + .unwrap() + .into(), + ], + }, + ); + let test_stake_authorize = test_commands.clone().get_matches_from(vec![ + "test", + "stake-authorize", + &stake_account_string, + "--new-stake-authority", + &new_stake_string, + ]); + assert_eq!( + parse_command(&test_stake_authorize, &default_keypair_file, None).unwrap(), + CliCommandInfo { + command: CliCommand::StakeAuthorize { + stake_account_pubkey, + new_authorizations: vec![(StakeAuthorize::Staker, new_stake_authority, 0,),], + sign_only: false, + blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster), + nonce_account: None, + nonce_authority: 0, + fee_payer: 0, + }, + signers: vec![read_keypair_file(&default_keypair_file).unwrap().into(),], + }, + ); + let test_stake_authorize = test_commands.clone().get_matches_from(vec![ + "test", + "stake-authorize", + &stake_account_string, + "--new-stake-authority", + &new_stake_string, + "--stake-authority", + &stake_authority_keypair_file, + ]); + assert_eq!( + parse_command(&test_stake_authorize, &default_keypair_file, None).unwrap(), + CliCommandInfo { + command: CliCommand::StakeAuthorize { + stake_account_pubkey, + new_authorizations: vec![(StakeAuthorize::Staker, new_stake_authority, 1,),], + sign_only: false, + blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster), + nonce_account: None, + nonce_authority: 0, + fee_payer: 0, + }, + signers: vec![ + read_keypair_file(&default_keypair_file).unwrap().into(), + read_keypair_file(&stake_authority_keypair_file) + .unwrap() + .into(), + ], + }, + ); + // Withdraw authority may set new stake authority + let test_stake_authorize = test_commands.clone().get_matches_from(vec![ + "test", + "stake-authorize", + &stake_account_string, + "--new-stake-authority", + &new_stake_string, + "--withdraw-authority", + &withdraw_authority_keypair_file, + ]); + assert_eq!( + parse_command(&test_stake_authorize, &default_keypair_file, None).unwrap(), + CliCommandInfo { + command: CliCommand::StakeAuthorize { + stake_account_pubkey, + new_authorizations: vec![(StakeAuthorize::Staker, new_stake_authority, 1,),], + sign_only: false, + blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster), + nonce_account: None, + nonce_authority: 0, + fee_payer: 0, + }, + signers: vec![ + read_keypair_file(&default_keypair_file).unwrap().into(), + read_keypair_file(&withdraw_authority_keypair_file) + .unwrap() + .into(), + ], + }, + ); + let test_stake_authorize = test_commands.clone().get_matches_from(vec![ + "test", + "stake-authorize", + &stake_account_string, + "--new-withdraw-authority", + &new_withdraw_string, + ]); + assert_eq!( + parse_command(&test_stake_authorize, &default_keypair_file, None).unwrap(), + CliCommandInfo { + command: CliCommand::StakeAuthorize { + stake_account_pubkey, + new_authorizations: vec![( + StakeAuthorize::Withdrawer, + new_withdraw_authority, + 0, + ),], + sign_only: false, + blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster), + nonce_account: None, + nonce_authority: 0, + fee_payer: 0, + }, + signers: vec![read_keypair_file(&default_keypair_file).unwrap().into(),], + }, + ); + let test_stake_authorize = test_commands.clone().get_matches_from(vec![ + "test", + "stake-authorize", + &stake_account_string, + "--new-withdraw-authority", + &new_withdraw_string, + "--withdraw-authority", + &withdraw_authority_keypair_file, + ]); + assert_eq!( + parse_command(&test_stake_authorize, &default_keypair_file, None).unwrap(), + CliCommandInfo { + command: CliCommand::StakeAuthorize { + stake_account_pubkey, + new_authorizations: vec![( + StakeAuthorize::Withdrawer, + new_withdraw_authority, + 1, + ),], + sign_only: false, + blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster), + nonce_account: None, + nonce_authority: 0, + fee_payer: 0, + }, + signers: vec![ + read_keypair_file(&default_keypair_file).unwrap().into(), + read_keypair_file(&withdraw_authority_keypair_file) + .unwrap() + .into(), + ], + }, + ); + // Test Authorize Subcommand w/ sign-only let blockhash = Hash::default(); let blockhash_string = format!("{}", blockhash); let test_authorize = test_commands.clone().get_matches_from(vec![ "test", - &subcommand, + "stake-authorize", &stake_account_string, + "--new-stake-authority", &stake_account_string, "--blockhash", &blockhash_string, @@ -1573,9 +1792,7 @@ mod tests { CliCommandInfo { command: CliCommand::StakeAuthorize { stake_account_pubkey, - new_authorized_pubkey: stake_account_pubkey, - stake_authorize, - authority: 0, + new_authorizations: vec![(StakeAuthorize::Staker, stake_account_pubkey, 0)], sign_only: true, blockhash_query: BlockhashQuery::None(blockhash), nonce_account: None, @@ -1592,8 +1809,9 @@ mod tests { let signer = format!("{}={}", keypair.pubkey(), sig); let test_authorize = test_commands.clone().get_matches_from(vec![ "test", - &subcommand, + "stake-authorize", &stake_account_string, + "--new-stake-authority", &stake_account_string, "--blockhash", &blockhash_string, @@ -1607,9 +1825,7 @@ mod tests { CliCommandInfo { command: CliCommand::StakeAuthorize { stake_account_pubkey, - new_authorized_pubkey: stake_account_pubkey, - stake_authorize, - authority: 0, + new_authorizations: vec![(StakeAuthorize::Staker, stake_account_pubkey, 0)], sign_only: false, blockhash_query: BlockhashQuery::FeeCalculator( blockhash_query::Source::Cluster, @@ -1633,8 +1849,9 @@ mod tests { let nonce_account = Pubkey::new(&[1u8; 32]); let test_authorize = test_commands.clone().get_matches_from(vec![ "test", - &subcommand, + "stake-authorize", &stake_account_string, + "--new-stake-authority", &stake_account_string, "--blockhash", &blockhash_string, @@ -1654,9 +1871,7 @@ mod tests { CliCommandInfo { command: CliCommand::StakeAuthorize { stake_account_pubkey, - new_authorized_pubkey: stake_account_pubkey, - stake_authorize, - authority: 0, + new_authorizations: vec![(StakeAuthorize::Staker, stake_account_pubkey, 0)], sign_only: false, blockhash_query: BlockhashQuery::FeeCalculator( blockhash_query::Source::NonceAccount(nonce_account), @@ -1676,8 +1891,9 @@ mod tests { // Test Authorize Subcommand w/ blockhash let test_authorize = test_commands.clone().get_matches_from(vec![ "test", - &subcommand, + "stake-authorize", &stake_account_string, + "--new-stake-authority", &stake_account_string, "--blockhash", &blockhash_string, @@ -1687,9 +1903,7 @@ mod tests { CliCommandInfo { command: CliCommand::StakeAuthorize { stake_account_pubkey, - new_authorized_pubkey: stake_account_pubkey, - stake_authorize, - authority: 0, + new_authorizations: vec![(StakeAuthorize::Staker, stake_account_pubkey, 0)], sign_only: false, blockhash_query: BlockhashQuery::FeeCalculator( blockhash_query::Source::Cluster, @@ -1710,8 +1924,9 @@ mod tests { let nonce_account_string = nonce_account_pubkey.to_string(); let test_authorize = test_commands.clone().get_matches_from(vec![ "test", - &subcommand, + "stake-authorize", &stake_account_string, + "--new-stake-authority", &stake_account_string, "--blockhash", &blockhash_string, @@ -1725,9 +1940,7 @@ mod tests { CliCommandInfo { command: CliCommand::StakeAuthorize { stake_account_pubkey, - new_authorized_pubkey: stake_account_pubkey, - stake_authorize, - authority: 0, + new_authorizations: vec![(StakeAuthorize::Staker, stake_account_pubkey, 0)], sign_only: false, blockhash_query: BlockhashQuery::FeeCalculator( blockhash_query::Source::NonceAccount(nonce_account_pubkey), @@ -1751,8 +1964,9 @@ mod tests { let fee_payer_string = fee_payer_pubkey.to_string(); let test_authorize = test_commands.clone().get_matches_from(vec![ "test", - &subcommand, + "stake-authorize", &stake_account_string, + "--new-stake-authority", &stake_account_string, "--fee-payer", &fee_payer_keypair_file, @@ -1762,9 +1976,7 @@ mod tests { CliCommandInfo { command: CliCommand::StakeAuthorize { stake_account_pubkey, - new_authorized_pubkey: stake_account_pubkey, - stake_authorize, - authority: 0, + new_authorizations: vec![(StakeAuthorize::Staker, stake_account_pubkey, 0)], sign_only: false, blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster), nonce_account: None, @@ -1782,8 +1994,9 @@ mod tests { let signer = format!("{}={}", fee_payer_string, sig); let test_authorize = test_commands.clone().get_matches_from(vec![ "test", - &subcommand, + "stake-authorize", &stake_account_string, + "--new-stake-authority", &stake_account_string, "--fee-payer", &fee_payer_string, @@ -1797,9 +2010,7 @@ mod tests { CliCommandInfo { command: CliCommand::StakeAuthorize { stake_account_pubkey, - new_authorized_pubkey: stake_account_pubkey, - stake_authorize, - authority: 0, + new_authorizations: vec![(StakeAuthorize::Staker, stake_account_pubkey, 0)], sign_only: false, blockhash_query: BlockhashQuery::FeeCalculator( blockhash_query::Source::Cluster, @@ -1815,34 +2026,6 @@ mod tests { ], } ); - } - - #[test] - fn test_parse_command() { - let test_commands = app("test", "desc", "version"); - let default_keypair = Keypair::new(); - let (default_keypair_file, mut tmp_file) = make_tmp_file(); - write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap(); - let (keypair_file, mut tmp_file) = make_tmp_file(); - let stake_account_keypair = Keypair::new(); - write_keypair(&stake_account_keypair, tmp_file.as_file_mut()).unwrap(); - let stake_account_pubkey = stake_account_keypair.pubkey(); - let (stake_authority_keypair_file, mut tmp_file) = make_tmp_file(); - let stake_authority_keypair = Keypair::new(); - write_keypair(&stake_authority_keypair, tmp_file.as_file_mut()).unwrap(); - - parse_authorize_tests( - &test_commands, - stake_account_pubkey, - &stake_authority_keypair_file, - StakeAuthorize::Staker, - ); - parse_authorize_tests( - &test_commands, - stake_account_pubkey, - &stake_authority_keypair_file, - StakeAuthorize::Withdrawer, - ); // Test CreateStakeAccount SubCommand let custodian = Pubkey::new_rand(); diff --git a/cli/tests/stake.rs b/cli/tests/stake.rs index e5c1a29a07..486327a83b 100644 --- a/cli/tests/stake.rs +++ b/cli/tests/stake.rs @@ -625,9 +625,7 @@ fn test_stake_authorize() { config.signers.pop(); config.command = CliCommand::StakeAuthorize { stake_account_pubkey, - new_authorized_pubkey: online_authority_pubkey, - stake_authorize: StakeAuthorize::Staker, - authority: 0, + new_authorizations: vec![(StakeAuthorize::Staker, online_authority_pubkey, 0)], sign_only: false, blockhash_query: BlockhashQuery::default(), nonce_account: None, @@ -643,13 +641,40 @@ fn test_stake_authorize() { }; assert_eq!(current_authority, online_authority_pubkey); - // Assign new offline stake authority + // Assign new online stake and withdraw authorities + let online_authority2 = Keypair::new(); + let online_authority2_pubkey = online_authority2.pubkey(); + let withdraw_authority = Keypair::new(); + let withdraw_authority_pubkey = withdraw_authority.pubkey(); config.signers.push(&online_authority); config.command = CliCommand::StakeAuthorize { stake_account_pubkey, - new_authorized_pubkey: offline_authority_pubkey, - stake_authorize: StakeAuthorize::Staker, - authority: 1, + new_authorizations: vec![ + (StakeAuthorize::Staker, online_authority2_pubkey, 1), + (StakeAuthorize::Withdrawer, withdraw_authority_pubkey, 0), + ], + sign_only: false, + blockhash_query: BlockhashQuery::default(), + nonce_account: None, + nonce_authority: 0, + fee_payer: 0, + }; + process_command(&config).unwrap(); + let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap(); + let stake_state: StakeState = stake_account.state().unwrap(); + let (current_staker, current_withdrawer) = match stake_state { + StakeState::Initialized(meta) => (meta.authorized.staker, meta.authorized.withdrawer), + _ => panic!("Unexpected stake state!"), + }; + assert_eq!(current_staker, online_authority2_pubkey); + assert_eq!(current_withdrawer, withdraw_authority_pubkey); + + // Assign new offline stake authority + config.signers.pop(); + config.signers.push(&online_authority2); + config.command = CliCommand::StakeAuthorize { + stake_account_pubkey, + new_authorizations: vec![(StakeAuthorize::Staker, offline_authority_pubkey, 1)], sign_only: false, blockhash_query: BlockhashQuery::default(), nonce_account: None, @@ -671,9 +696,7 @@ fn test_stake_authorize() { let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap(); config_offline.command = CliCommand::StakeAuthorize { stake_account_pubkey, - new_authorized_pubkey: nonced_authority_pubkey, - stake_authorize: StakeAuthorize::Staker, - authority: 0, + new_authorizations: vec![(StakeAuthorize::Staker, nonced_authority_pubkey, 0)], sign_only: true, blockhash_query: BlockhashQuery::None(blockhash), nonce_account: None, @@ -687,9 +710,7 @@ fn test_stake_authorize() { config.signers = vec![&offline_presigner]; config.command = CliCommand::StakeAuthorize { stake_account_pubkey, - new_authorized_pubkey: nonced_authority_pubkey, - stake_authorize: StakeAuthorize::Staker, - authority: 0, + new_authorizations: vec![(StakeAuthorize::Staker, nonced_authority_pubkey, 0)], sign_only: false, blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash), nonce_account: None, @@ -731,9 +752,7 @@ fn test_stake_authorize() { config_offline.signers.push(&nonced_authority); config_offline.command = CliCommand::StakeAuthorize { stake_account_pubkey, - new_authorized_pubkey: online_authority_pubkey, - stake_authorize: StakeAuthorize::Staker, - authority: 1, + new_authorizations: vec![(StakeAuthorize::Staker, online_authority_pubkey, 1)], sign_only: true, blockhash_query: BlockhashQuery::None(nonce_hash), nonce_account: Some(nonce_account.pubkey()), @@ -749,9 +768,7 @@ fn test_stake_authorize() { config.signers = vec![&offline_presigner, &nonced_authority_presigner]; config.command = CliCommand::StakeAuthorize { stake_account_pubkey, - new_authorized_pubkey: online_authority_pubkey, - stake_authorize: StakeAuthorize::Staker, - authority: 1, + new_authorizations: vec![(StakeAuthorize::Staker, online_authority_pubkey, 1)], sign_only: false, blockhash_query: BlockhashQuery::FeeCalculator( blockhash_query::Source::NonceAccount(nonce_account.pubkey()), @@ -858,9 +875,7 @@ fn test_stake_authorize_with_fee_payer() { config.signers = vec![&default_signer, &payer_keypair]; config.command = CliCommand::StakeAuthorize { stake_account_pubkey, - new_authorized_pubkey: offline_pubkey, - stake_authorize: StakeAuthorize::Staker, - authority: 0, + new_authorizations: vec![(StakeAuthorize::Staker, offline_pubkey, 0)], sign_only: false, blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster), nonce_account: None, @@ -878,9 +893,7 @@ fn test_stake_authorize_with_fee_payer() { let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap(); config_offline.command = CliCommand::StakeAuthorize { stake_account_pubkey, - new_authorized_pubkey: payer_pubkey, - stake_authorize: StakeAuthorize::Staker, - authority: 0, + new_authorizations: vec![(StakeAuthorize::Staker, payer_pubkey, 0)], sign_only: true, blockhash_query: BlockhashQuery::None(blockhash), nonce_account: None, @@ -894,9 +907,7 @@ fn test_stake_authorize_with_fee_payer() { config.signers = vec![&offline_presigner]; config.command = CliCommand::StakeAuthorize { stake_account_pubkey, - new_authorized_pubkey: payer_pubkey, - stake_authorize: StakeAuthorize::Staker, - authority: 0, + new_authorizations: vec![(StakeAuthorize::Staker, payer_pubkey, 0)], sign_only: false, blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash), nonce_account: None, diff --git a/docs/src/offline-signing/README.md b/docs/src/offline-signing/README.md index 4e90cd1768..e5a78f86fa 100644 --- a/docs/src/offline-signing/README.md +++ b/docs/src/offline-signing/README.md @@ -18,8 +18,7 @@ At present, the following commands support offline signing: * [`deactivate-stake`](../cli/usage.md#solana-deactivate-stake) * [`delegate-stake`](../cli/usage.md#solana-delegate-stake) * [`split-stake`](../cli/usage.md#solana-split-stake) -* [`stake-authorize-staker`](../cli/usage.md#solana-stake-authorize-staker) -* [`stake-authorize-withdrawer`](../cli/usage.md#solana-stake-authorize-withdrawer) +* [`stake-authorize`](../cli/usage.md#solana-stake-authorize) * [`stake-set-lockup`](../cli/usage.md#solana-stake-set-lockup) * [`transfer`](../cli/usage.md#solana-transfer) * [`withdraw-stake`](../cli/usage.md#solana-withdraw-stake)