include vote account in deactivate (#5476)

This commit is contained in:
Rob Walker
2019-08-09 12:55:21 -07:00
committed by GitHub
parent 7b77fbd525
commit 07a049aa59
5 changed files with 78 additions and 29 deletions

View File

@ -23,7 +23,7 @@ or more for the change to take effect.
Stake can be deactivate by running: Stake can be deactivate by running:
```bash ```bash
$ solana-wallet deactivate-stake ~/validator-config/stake-keypair.json $ solana-wallet deactivate-stake ~/validator-config/stake-keypair.json [VOTE PUBKEY]
``` ```
Note that a stake account may only be used once, so after deactivation, use the Note that a stake account may only be used once, so after deactivation, use the
wallet's `withdraw-stake` command to recover the previously staked lamports. wallet's `withdraw-stake` command to recover the previously staked lamports.

View File

@ -47,7 +47,8 @@ pub enum StakeInstruction {
/// ///
/// Expects 2 Accounts: /// Expects 2 Accounts:
/// 0 - Delegate StakeAccount /// 0 - Delegate StakeAccount
/// 1 - Syscall Account that carries epoch /// 1 - VoteAccount to which the Stake is delegated
/// 2 - Syscall Account that carries epoch
Deactivate, Deactivate,
} }
@ -79,7 +80,7 @@ pub fn create_stake_account_and_delegate_stake(
pub fn redeem_vote_credits(stake_pubkey: &Pubkey, vote_pubkey: &Pubkey) -> Instruction { pub fn redeem_vote_credits(stake_pubkey: &Pubkey, vote_pubkey: &Pubkey) -> Instruction {
let account_metas = vec![ let account_metas = vec![
AccountMeta::new(*stake_pubkey, false), AccountMeta::new(*stake_pubkey, false),
AccountMeta::new(*vote_pubkey, false), AccountMeta::new_credit_only(*vote_pubkey, false),
AccountMeta::new(crate::rewards_pools::random_id(), false), AccountMeta::new(crate::rewards_pools::random_id(), false),
AccountMeta::new_credit_only(sysvar::rewards::id(), false), AccountMeta::new_credit_only(sysvar::rewards::id(), false),
]; ];
@ -104,9 +105,10 @@ pub fn withdraw(stake_pubkey: &Pubkey, to_pubkey: &Pubkey, lamports: u64) -> Ins
Instruction::new(id(), &StakeInstruction::Withdraw(lamports), account_metas) Instruction::new(id(), &StakeInstruction::Withdraw(lamports), account_metas)
} }
pub fn deactivate_stake(stake_pubkey: &Pubkey) -> Instruction { pub fn deactivate_stake(stake_pubkey: &Pubkey, vote_pubkey: &Pubkey) -> Instruction {
let account_metas = vec![ let account_metas = vec![
AccountMeta::new(*stake_pubkey, true), AccountMeta::new(*stake_pubkey, true),
AccountMeta::new_credit_only(*vote_pubkey, false),
AccountMeta::new_credit_only(sysvar::clock::id(), false), AccountMeta::new_credit_only(sysvar::clock::id(), false),
]; ];
Instruction::new(id(), &StakeInstruction::Deactivate, account_metas) Instruction::new(id(), &StakeInstruction::Deactivate, account_metas)
@ -168,12 +170,14 @@ pub fn process_instruction(
) )
} }
StakeInstruction::Deactivate => { StakeInstruction::Deactivate => {
if rest.len() != 1 { if rest.len() != 2 {
Err(InstructionError::InvalidInstructionData)?; Err(InstructionError::InvalidInstructionData)?;
} }
let sysvar = &rest[0]; let (vote, rest) = rest.split_at_mut(1);
let vote = &mut vote[0];
let clock = &rest[0];
me.deactivate_stake(&sysvar::clock::from_keyed_account(&sysvar)?) me.deactivate_stake(vote, &sysvar::clock::from_keyed_account(&clock)?)
} }
} }
} }
@ -225,7 +229,7 @@ mod tests {
Err(InstructionError::InvalidAccountData), Err(InstructionError::InvalidAccountData),
); );
assert_eq!( assert_eq!(
process_instruction(&deactivate_stake(&Pubkey::default())), process_instruction(&deactivate_stake(&Pubkey::default(), &Pubkey::default())),
Err(InstructionError::InvalidAccountData), Err(InstructionError::InvalidAccountData),
); );
} }
@ -351,6 +355,7 @@ mod tests {
super::process_instruction( super::process_instruction(
&Pubkey::default(), &Pubkey::default(),
&mut [ &mut [
KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()),
KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()), KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()),
KeyedAccount::new( KeyedAccount::new(
&sysvar::rewards::id(), &sysvar::rewards::id(),
@ -368,7 +373,6 @@ mod tests {
super::process_instruction( super::process_instruction(
&Pubkey::default(), &Pubkey::default(),
&mut [ &mut [
KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()),
KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()), KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()),
KeyedAccount::new( KeyedAccount::new(
&sysvar::clock::id(), &sysvar::clock::id(),

View File

@ -180,7 +180,11 @@ pub trait StakeAccount {
stake: u64, stake: u64,
clock: &sysvar::clock::Clock, clock: &sysvar::clock::Clock,
) -> Result<(), InstructionError>; ) -> Result<(), InstructionError>;
fn deactivate_stake(&mut self, clock: &sysvar::clock::Clock) -> Result<(), InstructionError>; fn deactivate_stake(
&mut self,
vote_account: &KeyedAccount,
clock: &sysvar::clock::Clock,
) -> Result<(), InstructionError>;
fn redeem_vote_credits( fn redeem_vote_credits(
&mut self, &mut self,
vote_account: &mut KeyedAccount, vote_account: &mut KeyedAccount,
@ -225,7 +229,11 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
Err(InstructionError::InvalidAccountData) Err(InstructionError::InvalidAccountData)
} }
} }
fn deactivate_stake(&mut self, clock: &sysvar::clock::Clock) -> Result<(), InstructionError> { fn deactivate_stake(
&mut self,
_vote_account: &KeyedAccount, // TODO: used in slashing
clock: &sysvar::clock::Clock,
) -> Result<(), InstructionError> {
if self.signer_key().is_none() { if self.signer_key().is_none() {
return Err(InstructionError::MissingRequiredSignature); return Err(InstructionError::MissingRequiredSignature);
} }
@ -466,17 +474,22 @@ mod tests {
..sysvar::clock::Clock::default() ..sysvar::clock::Clock::default()
}; };
let vote_pubkey = Pubkey::new_rand();
let mut vote_account =
vote_state::create_account(&vote_pubkey, &Pubkey::new_rand(), 0, 100);
let vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &mut vote_account);
// unsigned keyed account // unsigned keyed account
let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &mut stake_account); let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &mut stake_account);
assert_eq!( assert_eq!(
stake_keyed_account.deactivate_stake(&clock), stake_keyed_account.deactivate_stake(&vote_keyed_account, &clock),
Err(InstructionError::MissingRequiredSignature) Err(InstructionError::MissingRequiredSignature)
); );
// signed keyed account but not staked yet // signed keyed account but not staked yet
let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account); let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account);
assert_eq!( assert_eq!(
stake_keyed_account.deactivate_stake(&clock), stake_keyed_account.deactivate_stake(&vote_keyed_account, &clock),
Err(InstructionError::InvalidAccountData) Err(InstructionError::InvalidAccountData)
); );
@ -492,7 +505,10 @@ mod tests {
); );
// Deactivate after staking // Deactivate after staking
assert_eq!(stake_keyed_account.deactivate_stake(&clock), Ok(())); assert_eq!(
stake_keyed_account.deactivate_stake(&vote_keyed_account, &clock),
Ok(())
);
} }
#[test] #[test]
@ -570,7 +586,10 @@ mod tests {
); );
// deactivate the stake before withdrawal // deactivate the stake before withdrawal
assert_eq!(stake_keyed_account.deactivate_stake(&clock), Ok(())); assert_eq!(
stake_keyed_account.deactivate_stake(&vote_keyed_account, &clock),
Ok(())
);
// simulate time passing // simulate time passing
clock.epoch += STAKE_WARMUP_EPOCHS * 2; clock.epoch += STAKE_WARMUP_EPOCHS * 2;

View File

@ -175,7 +175,10 @@ fn test_stake_account_delegate() {
// Deactivate the stake // Deactivate the stake
let message = Message::new_with_payer( let message = Message::new_with_payer(
vec![stake_instruction::deactivate_stake(&staker_pubkey)], vec![stake_instruction::deactivate_stake(
&staker_pubkey,
&vote_pubkey,
)],
Some(&mint_pubkey), Some(&mint_pubkey),
); );
assert!(bank_client assert!(bank_client

View File

@ -55,7 +55,7 @@ pub enum WalletCommand {
ShowVoteAccount(Pubkey), ShowVoteAccount(Pubkey),
DelegateStake(Keypair, Pubkey, u64, bool), DelegateStake(Keypair, Pubkey, u64, bool),
WithdrawStake(Keypair, Pubkey, u64), WithdrawStake(Keypair, Pubkey, u64),
DeactivateStake(Keypair), DeactivateStake(Keypair, Pubkey),
RedeemVoteCredits(Pubkey, Pubkey), RedeemVoteCredits(Pubkey, Pubkey),
ShowStakeAccount(Pubkey), ShowStakeAccount(Pubkey),
CreateReplicatorStorageAccount(Pubkey, Pubkey), CreateReplicatorStorageAccount(Pubkey, Pubkey),
@ -271,7 +271,11 @@ pub fn parse_command(
} }
("deactivate-stake", Some(matches)) => { ("deactivate-stake", Some(matches)) => {
let stake_account_keypair = keypair_of(matches, "stake_account_keypair_file").unwrap(); let stake_account_keypair = keypair_of(matches, "stake_account_keypair_file").unwrap();
Ok(WalletCommand::DeactivateStake(stake_account_keypair)) let vote_account_pubkey = value_of(matches, "vote_account_pubkey").unwrap();
Ok(WalletCommand::DeactivateStake(
stake_account_keypair,
vote_account_pubkey,
))
} }
("redeem-vote-credits", Some(matches)) => { ("redeem-vote-credits", Some(matches)) => {
let stake_account_pubkey = pubkey_of(matches, "stake_account_pubkey").unwrap(); let stake_account_pubkey = pubkey_of(matches, "stake_account_pubkey").unwrap();
@ -587,9 +591,11 @@ fn process_deactivate_stake_account(
rpc_client: &RpcClient, rpc_client: &RpcClient,
config: &WalletConfig, config: &WalletConfig,
stake_account_keypair: &Keypair, stake_account_keypair: &Keypair,
vote_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 ixs = stake_instruction::deactivate_stake(&stake_account_keypair.pubkey()); let ixs =
stake_instruction::deactivate_stake(&stake_account_keypair.pubkey(), vote_account_pubkey);
let mut tx = Transaction::new_signed_with_payer( let mut tx = Transaction::new_signed_with_payer(
vec![ixs], vec![ixs],
Some(&config.keypair.pubkey()), Some(&config.keypair.pubkey()),
@ -1168,8 +1174,13 @@ pub fn process_command(config: &WalletConfig) -> ProcessResult {
), ),
// Deactivate stake account // Deactivate stake account
WalletCommand::DeactivateStake(stake_account_keypair) => { WalletCommand::DeactivateStake(stake_account_keypair, vote_account_pubkey) => {
process_deactivate_stake_account(&rpc_client, config, &stake_account_keypair) process_deactivate_stake_account(
&rpc_client,
config,
&stake_account_keypair,
&vote_account_pubkey,
)
} }
WalletCommand::RedeemVoteCredits(stake_account_pubkey, vote_account_pubkey) => { WalletCommand::RedeemVoteCredits(stake_account_pubkey, vote_account_pubkey) => {
@ -1549,6 +1560,15 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
.required(true) .required(true)
.help("Keypair file for the stake account, for signing the delegate transaction."), .help("Keypair file for the stake account, for signing the delegate transaction."),
) )
.arg(
Arg::with_name("vote_account_pubkey")
.index(2)
.value_name("PUBKEY")
.takes_value(true)
.required(true)
.validator(is_pubkey)
.help("The vote account to which the stake is currently delegated"),
)
) )
.subcommand( .subcommand(
SubCommand::with_name("withdraw-stake") SubCommand::with_name("withdraw-stake")
@ -2057,13 +2077,15 @@ mod tests {
let keypair_file = make_tmp_path("keypair_file"); let keypair_file = make_tmp_path("keypair_file");
gen_keypair_file(&keypair_file).unwrap(); gen_keypair_file(&keypair_file).unwrap();
let keypair = read_keypair(&keypair_file).unwrap(); let keypair = read_keypair(&keypair_file).unwrap();
let test_deactivate_stake = let test_deactivate_stake = test_commands.clone().get_matches_from(vec![
test_commands "test",
.clone() "deactivate-stake",
.get_matches_from(vec!["test", "deactivate-stake", &keypair_file]); &keypair_file,
&pubkey_string,
]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_deactivate_stake).unwrap(), parse_command(&pubkey, &test_deactivate_stake).unwrap(),
WalletCommand::DeactivateStake(keypair) WalletCommand::DeactivateStake(keypair, pubkey)
); );
// Test Deploy Subcommand // Test Deploy Subcommand
@ -2229,8 +2251,8 @@ mod tests {
// DeactivateStake test. // DeactivateStake test.
/* /*
let bob_keypair = Keypair::new(); let bob_keypair = Keypair::new();
let node_pubkey = Pubkey::new_rand(); let vote_pubkey = Pubkey::new_rand();
config.command = WalletCommand::DelegateStake(bob_keypair.into(), node_pubkey, 100, true); config.command = WalletCommand::DelegateStake(bob_keypair.into(), vote_pubkey, 100, true);
let signature = process_command(&config); let signature = process_command(&config);
assert_eq!(signature.unwrap(), SIGNATURE.to_string()); assert_eq!(signature.unwrap(), SIGNATURE.to_string());
*/ */
@ -2242,7 +2264,8 @@ mod tests {
assert_eq!(signature.unwrap(), SIGNATURE.to_string()); assert_eq!(signature.unwrap(), SIGNATURE.to_string());
let bob_keypair = Keypair::new(); let bob_keypair = Keypair::new();
config.command = WalletCommand::DeactivateStake(bob_keypair.into()); let vote_pubkey = Pubkey::new_rand();
config.command = WalletCommand::DeactivateStake(bob_keypair.into(), vote_pubkey);
let signature = process_command(&config); let signature = process_command(&config);
assert_eq!(signature.unwrap(), SIGNATURE.to_string()); assert_eq!(signature.unwrap(), SIGNATURE.to_string());