* Merge stake::withdraw instructions (#9617) * Add custodian option to withdraw-stake command (#9662) automerge Co-authored-by: Greg Fitzgerald <greg@solana.com>
This commit is contained in:
@ -339,6 +339,7 @@ pub enum CliCommand {
|
|||||||
destination_account_pubkey: Pubkey,
|
destination_account_pubkey: Pubkey,
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
withdraw_authority: SignerIndex,
|
withdraw_authority: SignerIndex,
|
||||||
|
custodian: Option<SignerIndex>,
|
||||||
sign_only: bool,
|
sign_only: bool,
|
||||||
blockhash_query: BlockhashQuery,
|
blockhash_query: BlockhashQuery,
|
||||||
nonce_account: Option<Pubkey>,
|
nonce_account: Option<Pubkey>,
|
||||||
@ -1968,6 +1969,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
|||||||
destination_account_pubkey,
|
destination_account_pubkey,
|
||||||
lamports,
|
lamports,
|
||||||
withdraw_authority,
|
withdraw_authority,
|
||||||
|
custodian,
|
||||||
sign_only,
|
sign_only,
|
||||||
blockhash_query,
|
blockhash_query,
|
||||||
ref nonce_account,
|
ref nonce_account,
|
||||||
@ -1980,6 +1982,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
|||||||
&destination_account_pubkey,
|
&destination_account_pubkey,
|
||||||
*lamports,
|
*lamports,
|
||||||
*withdraw_authority,
|
*withdraw_authority,
|
||||||
|
*custodian,
|
||||||
*sign_only,
|
*sign_only,
|
||||||
blockhash_query,
|
blockhash_query,
|
||||||
nonce_account.as_ref(),
|
nonce_account.as_ref(),
|
||||||
@ -3378,6 +3381,7 @@ mod tests {
|
|||||||
destination_account_pubkey: to_pubkey,
|
destination_account_pubkey: to_pubkey,
|
||||||
lamports: 100,
|
lamports: 100,
|
||||||
withdraw_authority: 0,
|
withdraw_authority: 0,
|
||||||
|
custodian: None,
|
||||||
sign_only: false,
|
sign_only: false,
|
||||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||||
nonce_account: None,
|
nonce_account: None,
|
||||||
|
@ -313,6 +313,14 @@ impl StakeSubCommands for App<'_, '_> {
|
|||||||
.arg(nonce_arg())
|
.arg(nonce_arg())
|
||||||
.arg(nonce_authority_arg())
|
.arg(nonce_authority_arg())
|
||||||
.arg(fee_payer_arg())
|
.arg(fee_payer_arg())
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("custodian")
|
||||||
|
.long("custodian")
|
||||||
|
.takes_value(true)
|
||||||
|
.value_name("KEYPAIR")
|
||||||
|
.validator(is_valid_signer)
|
||||||
|
.help("Authority to override account lockup")
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("stake-set-lockup")
|
SubCommand::with_name("stake-set-lockup")
|
||||||
@ -676,11 +684,15 @@ pub fn parse_stake_withdraw_stake(
|
|||||||
let (nonce_authority, nonce_authority_pubkey) =
|
let (nonce_authority, nonce_authority_pubkey) =
|
||||||
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
|
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 (fee_payer, fee_payer_pubkey) = signer_of(matches, FEE_PAYER_ARG.name, wallet_manager)?;
|
||||||
|
let (custodian, custodian_pubkey) = signer_of(matches, "custodian", wallet_manager)?;
|
||||||
|
|
||||||
let mut bulk_signers = vec![withdraw_authority, fee_payer];
|
let mut bulk_signers = vec![withdraw_authority, fee_payer];
|
||||||
if nonce_account.is_some() {
|
if nonce_account.is_some() {
|
||||||
bulk_signers.push(nonce_authority);
|
bulk_signers.push(nonce_authority);
|
||||||
}
|
}
|
||||||
|
if custodian.is_some() {
|
||||||
|
bulk_signers.push(custodian);
|
||||||
|
}
|
||||||
let signer_info =
|
let signer_info =
|
||||||
generate_unique_signers(bulk_signers, matches, default_signer_path, wallet_manager)?;
|
generate_unique_signers(bulk_signers, matches, default_signer_path, wallet_manager)?;
|
||||||
|
|
||||||
@ -695,6 +707,7 @@ pub fn parse_stake_withdraw_stake(
|
|||||||
nonce_account,
|
nonce_account,
|
||||||
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
|
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
|
||||||
fee_payer: signer_info.index_of(fee_payer_pubkey).unwrap(),
|
fee_payer: signer_info.index_of(fee_payer_pubkey).unwrap(),
|
||||||
|
custodian: custodian_pubkey.and_then(|_| signer_info.index_of(custodian_pubkey)),
|
||||||
},
|
},
|
||||||
signers: signer_info.signers,
|
signers: signer_info.signers,
|
||||||
})
|
})
|
||||||
@ -1013,6 +1026,7 @@ pub fn process_withdraw_stake(
|
|||||||
destination_account_pubkey: &Pubkey,
|
destination_account_pubkey: &Pubkey,
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
withdraw_authority: SignerIndex,
|
withdraw_authority: SignerIndex,
|
||||||
|
custodian: Option<SignerIndex>,
|
||||||
sign_only: bool,
|
sign_only: bool,
|
||||||
blockhash_query: &BlockhashQuery,
|
blockhash_query: &BlockhashQuery,
|
||||||
nonce_account: Option<&Pubkey>,
|
nonce_account: Option<&Pubkey>,
|
||||||
@ -1022,12 +1036,14 @@ pub fn process_withdraw_stake(
|
|||||||
let (recent_blockhash, fee_calculator) =
|
let (recent_blockhash, fee_calculator) =
|
||||||
blockhash_query.get_blockhash_and_fee_calculator(rpc_client)?;
|
blockhash_query.get_blockhash_and_fee_calculator(rpc_client)?;
|
||||||
let withdraw_authority = config.signers[withdraw_authority];
|
let withdraw_authority = config.signers[withdraw_authority];
|
||||||
|
let custodian = custodian.map(|index| config.signers[index]);
|
||||||
|
|
||||||
let ixs = vec![stake_instruction::withdraw(
|
let ixs = vec![stake_instruction::withdraw(
|
||||||
stake_account_pubkey,
|
stake_account_pubkey,
|
||||||
&withdraw_authority.pubkey(),
|
&withdraw_authority.pubkey(),
|
||||||
destination_account_pubkey,
|
destination_account_pubkey,
|
||||||
lamports,
|
lamports,
|
||||||
|
custodian.map(|signer| signer.pubkey()).as_ref(),
|
||||||
)];
|
)];
|
||||||
|
|
||||||
let fee_payer = config.signers[fee_payer];
|
let fee_payer = config.signers[fee_payer];
|
||||||
@ -1496,6 +1512,9 @@ mod tests {
|
|||||||
let (stake_authority_keypair_file, mut tmp_file) = make_tmp_file();
|
let (stake_authority_keypair_file, mut tmp_file) = make_tmp_file();
|
||||||
let stake_authority_keypair = Keypair::new();
|
let stake_authority_keypair = Keypair::new();
|
||||||
write_keypair(&stake_authority_keypair, tmp_file.as_file_mut()).unwrap();
|
write_keypair(&stake_authority_keypair, tmp_file.as_file_mut()).unwrap();
|
||||||
|
let (custodian_keypair_file, mut tmp_file) = make_tmp_file();
|
||||||
|
let custodian_keypair = Keypair::new();
|
||||||
|
write_keypair(&custodian_keypair, tmp_file.as_file_mut()).unwrap();
|
||||||
|
|
||||||
// stake-authorize subcommand
|
// stake-authorize subcommand
|
||||||
let stake_account_string = stake_account_pubkey.to_string();
|
let stake_account_string = stake_account_pubkey.to_string();
|
||||||
@ -2434,6 +2453,7 @@ mod tests {
|
|||||||
destination_account_pubkey: stake_account_pubkey,
|
destination_account_pubkey: stake_account_pubkey,
|
||||||
lamports: 42_000_000_000,
|
lamports: 42_000_000_000,
|
||||||
withdraw_authority: 0,
|
withdraw_authority: 0,
|
||||||
|
custodian: None,
|
||||||
sign_only: false,
|
sign_only: false,
|
||||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||||
nonce_account: None,
|
nonce_account: None,
|
||||||
@ -2463,6 +2483,7 @@ mod tests {
|
|||||||
destination_account_pubkey: stake_account_pubkey,
|
destination_account_pubkey: stake_account_pubkey,
|
||||||
lamports: 42_000_000_000,
|
lamports: 42_000_000_000,
|
||||||
withdraw_authority: 1,
|
withdraw_authority: 1,
|
||||||
|
custodian: None,
|
||||||
sign_only: false,
|
sign_only: false,
|
||||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||||
nonce_account: None,
|
nonce_account: None,
|
||||||
@ -2478,6 +2499,39 @@ mod tests {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Test WithdrawStake Subcommand w/ custodian
|
||||||
|
let test_withdraw_stake = test_commands.clone().get_matches_from(vec![
|
||||||
|
"test",
|
||||||
|
"withdraw-stake",
|
||||||
|
&stake_account_string,
|
||||||
|
&stake_account_string,
|
||||||
|
"42",
|
||||||
|
"--custodian",
|
||||||
|
&custodian_keypair_file,
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
parse_command(&test_withdraw_stake, &default_keypair_file, &mut None).unwrap(),
|
||||||
|
CliCommandInfo {
|
||||||
|
command: CliCommand::WithdrawStake {
|
||||||
|
stake_account_pubkey,
|
||||||
|
destination_account_pubkey: stake_account_pubkey,
|
||||||
|
lamports: 42_000_000_000,
|
||||||
|
withdraw_authority: 0,
|
||||||
|
custodian: Some(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(&custodian_keypair_file).unwrap().into()
|
||||||
|
],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// Test WithdrawStake Subcommand w/ authority and offline nonce
|
// Test WithdrawStake Subcommand w/ authority and offline nonce
|
||||||
let test_withdraw_stake = test_commands.clone().get_matches_from(vec![
|
let test_withdraw_stake = test_commands.clone().get_matches_from(vec![
|
||||||
"test",
|
"test",
|
||||||
@ -2507,6 +2561,7 @@ mod tests {
|
|||||||
destination_account_pubkey: stake_account_pubkey,
|
destination_account_pubkey: stake_account_pubkey,
|
||||||
lamports: 42_000_000_000,
|
lamports: 42_000_000_000,
|
||||||
withdraw_authority: 0,
|
withdraw_authority: 0,
|
||||||
|
custodian: None,
|
||||||
sign_only: false,
|
sign_only: false,
|
||||||
blockhash_query: BlockhashQuery::FeeCalculator(
|
blockhash_query: BlockhashQuery::FeeCalculator(
|
||||||
blockhash_query::Source::NonceAccount(nonce_account),
|
blockhash_query::Source::NonceAccount(nonce_account),
|
||||||
|
@ -1451,6 +1451,7 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
|||||||
destination_account_pubkey: recipient_pubkey,
|
destination_account_pubkey: recipient_pubkey,
|
||||||
lamports: 42,
|
lamports: 42,
|
||||||
withdraw_authority: 0,
|
withdraw_authority: 0,
|
||||||
|
custodian: None,
|
||||||
sign_only: true,
|
sign_only: true,
|
||||||
blockhash_query: BlockhashQuery::None(nonce_hash),
|
blockhash_query: BlockhashQuery::None(nonce_hash),
|
||||||
nonce_account: Some(nonce_pubkey),
|
nonce_account: Some(nonce_pubkey),
|
||||||
@ -1466,6 +1467,7 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
|||||||
destination_account_pubkey: recipient_pubkey,
|
destination_account_pubkey: recipient_pubkey,
|
||||||
lamports: 42,
|
lamports: 42,
|
||||||
withdraw_authority: 0,
|
withdraw_authority: 0,
|
||||||
|
custodian: None,
|
||||||
sign_only: false,
|
sign_only: false,
|
||||||
blockhash_query: BlockhashQuery::FeeCalculator(
|
blockhash_query: BlockhashQuery::FeeCalculator(
|
||||||
blockhash_query::Source::NonceAccount(nonce_pubkey),
|
blockhash_query::Source::NonceAccount(nonce_pubkey),
|
||||||
|
@ -328,8 +328,9 @@ pub fn withdraw(
|
|||||||
withdrawer_pubkey: &Pubkey,
|
withdrawer_pubkey: &Pubkey,
|
||||||
to_pubkey: &Pubkey,
|
to_pubkey: &Pubkey,
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
|
custodian_pubkey: Option<&Pubkey>,
|
||||||
) -> Instruction {
|
) -> Instruction {
|
||||||
let account_metas = vec![
|
let mut account_metas = vec![
|
||||||
AccountMeta::new(*stake_pubkey, false),
|
AccountMeta::new(*stake_pubkey, false),
|
||||||
AccountMeta::new(*to_pubkey, false),
|
AccountMeta::new(*to_pubkey, false),
|
||||||
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
||||||
@ -337,24 +338,9 @@ pub fn withdraw(
|
|||||||
]
|
]
|
||||||
.with_signer(withdrawer_pubkey);
|
.with_signer(withdrawer_pubkey);
|
||||||
|
|
||||||
Instruction::new(id(), &StakeInstruction::Withdraw(lamports), account_metas)
|
if let Some(custodian_pubkey) = custodian_pubkey {
|
||||||
}
|
account_metas = account_metas.with_signer(custodian_pubkey)
|
||||||
|
}
|
||||||
pub fn withdraw_early(
|
|
||||||
stake_pubkey: &Pubkey,
|
|
||||||
withdrawer_pubkey: &Pubkey,
|
|
||||||
to_pubkey: &Pubkey,
|
|
||||||
lamports: u64,
|
|
||||||
custodian_pubkey: &Pubkey,
|
|
||||||
) -> Instruction {
|
|
||||||
let account_metas = vec![
|
|
||||||
AccountMeta::new(*stake_pubkey, false),
|
|
||||||
AccountMeta::new(*to_pubkey, false),
|
|
||||||
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
|
||||||
AccountMeta::new_readonly(sysvar::stake_history::id(), false),
|
|
||||||
]
|
|
||||||
.with_signer(withdrawer_pubkey)
|
|
||||||
.with_signer(custodian_pubkey);
|
|
||||||
|
|
||||||
Instruction::new(id(), &StakeInstruction::Withdraw(lamports), account_metas)
|
Instruction::new(id(), &StakeInstruction::Withdraw(lamports), account_metas)
|
||||||
}
|
}
|
||||||
@ -535,7 +521,8 @@ mod tests {
|
|||||||
&Pubkey::default(),
|
&Pubkey::default(),
|
||||||
&Pubkey::default(),
|
&Pubkey::default(),
|
||||||
&Pubkey::new_rand(),
|
&Pubkey::new_rand(),
|
||||||
100
|
100,
|
||||||
|
None,
|
||||||
)),
|
)),
|
||||||
Err(InstructionError::InvalidAccountData),
|
Err(InstructionError::InvalidAccountData),
|
||||||
);
|
);
|
||||||
|
@ -224,6 +224,7 @@ fn test_stake_account_lifetime() {
|
|||||||
&stake_pubkey,
|
&stake_pubkey,
|
||||||
&Pubkey::new_rand(),
|
&Pubkey::new_rand(),
|
||||||
1,
|
1,
|
||||||
|
None,
|
||||||
)],
|
)],
|
||||||
Some(&mint_pubkey),
|
Some(&mint_pubkey),
|
||||||
);
|
);
|
||||||
@ -318,6 +319,7 @@ fn test_stake_account_lifetime() {
|
|||||||
&stake_pubkey,
|
&stake_pubkey,
|
||||||
&Pubkey::new_rand(),
|
&Pubkey::new_rand(),
|
||||||
lamports / 2 - split_staked + 1,
|
lamports / 2 - split_staked + 1,
|
||||||
|
None,
|
||||||
)],
|
)],
|
||||||
Some(&mint_pubkey),
|
Some(&mint_pubkey),
|
||||||
);
|
);
|
||||||
@ -339,6 +341,7 @@ fn test_stake_account_lifetime() {
|
|||||||
&stake_pubkey,
|
&stake_pubkey,
|
||||||
&Pubkey::new_rand(),
|
&Pubkey::new_rand(),
|
||||||
lamports / 2,
|
lamports / 2,
|
||||||
|
None,
|
||||||
)],
|
)],
|
||||||
Some(&mint_pubkey),
|
Some(&mint_pubkey),
|
||||||
);
|
);
|
||||||
@ -354,6 +357,7 @@ fn test_stake_account_lifetime() {
|
|||||||
&stake_pubkey,
|
&stake_pubkey,
|
||||||
&Pubkey::new_rand(),
|
&Pubkey::new_rand(),
|
||||||
lamports / 2 - split_staked,
|
lamports / 2 - split_staked,
|
||||||
|
None,
|
||||||
)],
|
)],
|
||||||
Some(&mint_pubkey),
|
Some(&mint_pubkey),
|
||||||
);
|
);
|
||||||
@ -378,6 +382,7 @@ fn test_stake_account_lifetime() {
|
|||||||
&stake_pubkey,
|
&stake_pubkey,
|
||||||
&Pubkey::new_rand(),
|
&Pubkey::new_rand(),
|
||||||
split_staked,
|
split_staked,
|
||||||
|
None,
|
||||||
)],
|
)],
|
||||||
Some(&mint_pubkey),
|
Some(&mint_pubkey),
|
||||||
);
|
);
|
||||||
|
@ -450,6 +450,7 @@ mod test {
|
|||||||
&stake3_keypair.pubkey(),
|
&stake3_keypair.pubkey(),
|
||||||
&payer.pubkey(),
|
&payer.pubkey(),
|
||||||
one_sol,
|
one_sol,
|
||||||
|
None,
|
||||||
)],
|
)],
|
||||||
Some(&payer.pubkey()),
|
Some(&payer.pubkey()),
|
||||||
),
|
),
|
||||||
|
Reference in New Issue
Block a user