include vote account in deactivate (#5476)
This commit is contained in:
@ -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.
|
||||||
|
@ -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(),
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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());
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user