|solana withdraw-from-vote-account| now supports ALL, and refuses to deallocate the vote account (#10602)

(cherry picked from commit 296ac10b3a)
This commit is contained in:
Michael Vines
2020-06-15 14:36:47 -07:00
parent 76a7071dba
commit d396a5f45a
3 changed files with 80 additions and 15 deletions

View File

@ -396,7 +396,7 @@ pub enum CliCommand {
vote_account_pubkey: Pubkey, vote_account_pubkey: Pubkey,
destination_account_pubkey: Pubkey, destination_account_pubkey: Pubkey,
withdraw_authority: SignerIndex, withdraw_authority: SignerIndex,
lamports: u64, withdraw_amount: SpendAmount,
}, },
VoteAuthorize { VoteAuthorize {
vote_account_pubkey: Pubkey, vote_account_pubkey: Pubkey,
@ -2155,14 +2155,14 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
CliCommand::WithdrawFromVoteAccount { CliCommand::WithdrawFromVoteAccount {
vote_account_pubkey, vote_account_pubkey,
withdraw_authority, withdraw_authority,
lamports, withdraw_amount,
destination_account_pubkey, destination_account_pubkey,
} => process_withdraw_from_vote_account( } => process_withdraw_from_vote_account(
&rpc_client, &rpc_client,
config, config,
vote_account_pubkey, vote_account_pubkey,
*withdraw_authority, *withdraw_authority,
*lamports, *withdraw_amount,
destination_account_pubkey, destination_account_pubkey,
), ),
CliCommand::VoteAuthorize { CliCommand::VoteAuthorize {

View File

@ -16,8 +16,9 @@ use solana_clap_utils::{
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::{
account::Account, commitment_config::CommitmentConfig, message::Message, pubkey::Pubkey, account::Account, commitment_config::CommitmentConfig, message::Message,
system_instruction::SystemError, transaction::Transaction, native_token::lamports_to_sol, pubkey::Pubkey, system_instruction::SystemError,
transaction::Transaction,
}; };
use solana_vote_program::{ use solana_vote_program::{
vote_instruction::{self, withdraw, VoteError}, vote_instruction::{self, withdraw, VoteError},
@ -232,8 +233,8 @@ impl VoteSubCommands for App<'_, '_> {
.value_name("AMOUNT") .value_name("AMOUNT")
.takes_value(true) .takes_value(true)
.required(true) .required(true)
.validator(is_amount) .validator(is_amount_or_all)
.help("The amount to withdraw, in SOL"), .help("The amount to withdraw, in SOL; accepts keyword ALL"),
) )
.arg( .arg(
Arg::with_name("authorized_withdrawer") Arg::with_name("authorized_withdrawer")
@ -392,7 +393,8 @@ pub fn parse_withdraw_from_vote_account(
pubkey_of_signer(matches, "vote_account_pubkey", wallet_manager)?.unwrap(); pubkey_of_signer(matches, "vote_account_pubkey", wallet_manager)?.unwrap();
let destination_account_pubkey = let destination_account_pubkey =
pubkey_of_signer(matches, "destination_account_pubkey", wallet_manager)?.unwrap(); pubkey_of_signer(matches, "destination_account_pubkey", wallet_manager)?.unwrap();
let lamports = lamports_of_sol(matches, "amount").unwrap(); let withdraw_amount = SpendAmount::new_from_matches(matches, "amount");
let (withdraw_authority, withdraw_authority_pubkey) = let (withdraw_authority, withdraw_authority_pubkey) =
signer_of(matches, "authorized_withdrawer", wallet_manager)?; signer_of(matches, "authorized_withdrawer", wallet_manager)?;
@ -409,7 +411,7 @@ pub fn parse_withdraw_from_vote_account(
vote_account_pubkey, vote_account_pubkey,
destination_account_pubkey, destination_account_pubkey,
withdraw_authority: signer_info.index_of(withdraw_authority_pubkey).unwrap(), withdraw_authority: signer_info.index_of(withdraw_authority_pubkey).unwrap(),
lamports, withdraw_amount,
}, },
signers: signer_info.signers, signers: signer_info.signers,
}) })
@ -683,12 +685,28 @@ pub fn process_withdraw_from_vote_account(
config: &CliConfig, config: &CliConfig,
vote_account_pubkey: &Pubkey, vote_account_pubkey: &Pubkey,
withdraw_authority: SignerIndex, withdraw_authority: SignerIndex,
lamports: u64, withdraw_amount: SpendAmount,
destination_account_pubkey: &Pubkey, destination_account_pubkey: &Pubkey,
) -> ProcessResult { ) -> ProcessResult {
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let withdraw_authority = config.signers[withdraw_authority]; let withdraw_authority = config.signers[withdraw_authority];
let current_balance = rpc_client.get_balance(&vote_account_pubkey)?;
let minimum_balance = rpc_client.get_minimum_balance_for_rent_exemption(VoteState::size_of())?;
let lamports = match withdraw_amount {
SpendAmount::All => current_balance.saturating_sub(minimum_balance),
SpendAmount::Some(withdraw_amount) => {
if current_balance.saturating_sub(withdraw_amount) < minimum_balance {
return Err(CliError::BadParameter(format!(
"Withdraw amount too large. The vote account balance must be at least {} SOL to remain rent exempt", lamports_to_sol(minimum_balance)
))
.into());
}
withdraw_amount
}
};
let ix = withdraw( let ix = withdraw(
vote_account_pubkey, vote_account_pubkey,
&withdraw_authority.pubkey(), &withdraw_authority.pubkey(),
@ -966,7 +984,33 @@ mod tests {
vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(), vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(),
destination_account_pubkey: pubkey, destination_account_pubkey: pubkey,
withdraw_authority: 0, withdraw_authority: 0,
lamports: 42_000_000_000 withdraw_amount: SpendAmount::Some(42_000_000_000),
},
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
}
);
// Test WithdrawFromVoteAccount subcommand
let test_withdraw_from_vote_account = test_commands.clone().get_matches_from(vec![
"test",
"withdraw-from-vote-account",
&keypair_file,
&pubkey_string,
"ALL",
]);
assert_eq!(
parse_command(
&test_withdraw_from_vote_account,
&default_keypair_file,
&mut None
)
.unwrap(),
CliCommandInfo {
command: CliCommand::WithdrawFromVoteAccount {
vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(),
destination_account_pubkey: pubkey,
withdraw_authority: 0,
withdraw_amount: SpendAmount::All,
}, },
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()], signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
} }
@ -997,7 +1041,7 @@ mod tests {
vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(), vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(),
destination_account_pubkey: pubkey, destination_account_pubkey: pubkey,
withdraw_authority: 1, withdraw_authority: 1,
lamports: 42_000_000_000 withdraw_amount: SpendAmount::Some(42_000_000_000),
}, },
signers: vec![ signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(), read_keypair_file(&default_keypair_file).unwrap().into(),

View File

@ -1,5 +1,9 @@
use solana_cli::cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig}; use solana_cli::{
use solana_cli::test_utils::check_balance; cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
offline::{blockhash_query::BlockhashQuery, *},
spend_utils::SpendAmount,
test_utils::check_balance,
};
use solana_client::rpc_client::RpcClient; use solana_client::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;
@ -64,6 +68,23 @@ fn test_vote_authorize_and_withdraw() {
.max(1); .max(1);
check_balance(expected_balance, &rpc_client, &vote_account_pubkey); check_balance(expected_balance, &rpc_client, &vote_account_pubkey);
// Transfer in some more SOL
config.signers = vec![&default_signer];
config.command = CliCommand::Transfer {
amount: SpendAmount::Some(1_000),
to: vote_account_pubkey,
from: 0,
sign_only: false,
no_wait: false,
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
nonce_account: None,
nonce_authority: 0,
fee_payer: 0,
};
process_command(&config).unwrap();
let expected_balance = expected_balance + 1_000;
check_balance(expected_balance, &rpc_client, &vote_account_pubkey);
// Authorize vote account withdrawal to another signer // Authorize vote account withdrawal to another signer
let withdraw_authority = Keypair::new(); let withdraw_authority = Keypair::new();
config.signers = vec![&default_signer]; config.signers = vec![&default_signer];
@ -86,7 +107,7 @@ fn test_vote_authorize_and_withdraw() {
config.command = CliCommand::WithdrawFromVoteAccount { config.command = CliCommand::WithdrawFromVoteAccount {
vote_account_pubkey, vote_account_pubkey,
withdraw_authority: 1, withdraw_authority: 1,
lamports: 100, withdraw_amount: SpendAmount::Some(100),
destination_account_pubkey: destination_account, destination_account_pubkey: destination_account,
}; };
process_command(&config).unwrap(); process_command(&config).unwrap();