From bed1b143a527c5a3303c94c8bc65af4d7492cb2e Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Mon, 13 Dec 2021 19:00:29 -0700 Subject: [PATCH] Restore ALL behavior; add enum variant, comments, and help text to make behavior clearer (#21854) --- cli/src/spend_utils.rs | 25 +++++++++++++++++++++++++ cli/src/vote.rs | 12 +++++++++--- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/cli/src/spend_utils.rs b/cli/src/spend_utils.rs index b678e6e350..a8c0230bd2 100644 --- a/cli/src/spend_utils.rs +++ b/cli/src/spend_utils.rs @@ -16,6 +16,7 @@ use { pub enum SpendAmount { All, Some(u64), + RentExempt, } impl Default for SpendAmount { @@ -90,6 +91,7 @@ where 0, from_pubkey, fee_pubkey, + 0, build_message, )?; Ok((message, spend)) @@ -97,6 +99,12 @@ where let from_balance = rpc_client .get_balance_with_commitment(from_pubkey, commitment)? .value; + let from_rent_exempt_minimum = if amount == SpendAmount::RentExempt { + let data = rpc_client.get_account_data(from_pubkey)?; + rpc_client.get_minimum_balance_for_rent_exemption(data.len())? + } else { + 0 + }; let (message, SpendAndFee { spend, fee }) = resolve_spend_message( rpc_client, amount, @@ -104,6 +112,7 @@ where from_balance, from_pubkey, fee_pubkey, + from_rent_exempt_minimum, build_message, )?; if from_pubkey == fee_pubkey { @@ -140,6 +149,7 @@ fn resolve_spend_message( from_balance: u64, from_pubkey: &Pubkey, fee_pubkey: &Pubkey, + from_rent_exempt_minimum: u64, build_message: F, ) -> Result<(Message, SpendAndFee), CliError> where @@ -176,5 +186,20 @@ where }, )) } + SpendAmount::RentExempt => { + let mut lamports = if from_pubkey == fee_pubkey { + from_balance.saturating_sub(fee) + } else { + from_balance + }; + lamports = lamports.saturating_sub(from_rent_exempt_minimum); + Ok(( + build_message(lamports), + SpendAndFee { + spend: lamports, + fee, + }, + )) + } } } diff --git a/cli/src/vote.rs b/cli/src/vote.rs index 1bc752e173..1e4607c0fe 100644 --- a/cli/src/vote.rs +++ b/cli/src/vote.rs @@ -359,7 +359,7 @@ impl VoteSubCommands for App<'_, '_> { .takes_value(true) .required(true) .validator(is_amount_or_all) - .help("The amount to withdraw, in SOL; accepts keyword ALL"), + .help("The amount to withdraw, in SOL; accepts keyword ALL, which for this command means account balance minus rent-exempt minimum"), ) .arg( Arg::with_name("authorized_withdrawer") @@ -653,7 +653,13 @@ pub fn parse_withdraw_from_vote_account( pubkey_of_signer(matches, "vote_account_pubkey", wallet_manager)?.unwrap(); let destination_account_pubkey = pubkey_of_signer(matches, "destination_account_pubkey", wallet_manager)?.unwrap(); - let withdraw_amount = SpendAmount::new_from_matches(matches, "amount"); + let mut withdraw_amount = SpendAmount::new_from_matches(matches, "amount"); + // As a safeguard for vote accounts for running validators, `ALL` withdraws only the amount in + // excess of the rent-exempt minimum. In order to close the account with this subcommand, a + // validator must specify the withdrawal amount precisely. + if withdraw_amount == SpendAmount::All { + withdraw_amount = SpendAmount::RentExempt; + } let (withdraw_authority, withdraw_authority_pubkey) = signer_of(matches, "authorized_withdrawer", wallet_manager)?; @@ -1990,7 +1996,7 @@ mod tests { vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(), destination_account_pubkey: pubkey, withdraw_authority: 0, - withdraw_amount: SpendAmount::All, + withdraw_amount: SpendAmount::RentExempt, sign_only: false, dump_transaction_message: false, blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),