Add instructions and processor for stake deactivation (#4781)

automerge
This commit is contained in:
Pankaj Garg 2019-06-21 23:45:03 -07:00 committed by Grimes
parent c6316bb24b
commit 405ca1bcb2
3 changed files with 159 additions and 0 deletions

View File

@ -42,6 +42,13 @@ pub enum StakeInstruction {
/// The u64 is the portion of the Stake account balance to be withdrawn,
/// must be <= StakeAccount.lamports - staked lamports
Withdraw(u64),
/// Deactivates the stake in the account
///
/// Expects 2 Accounts:
/// 0 - Delegate StakeAccount
/// 1 - Syscall Account that carries epoch
Deactivate,
}
pub fn create_stake_account(
@ -97,6 +104,14 @@ pub fn withdraw(stake_pubkey: &Pubkey, to_pubkey: &Pubkey, lamports: u64) -> Ins
Instruction::new(id(), &StakeInstruction::Withdraw(lamports), account_metas)
}
pub fn deactivate_stake(stake_pubkey: &Pubkey) -> Instruction {
let account_metas = vec![
AccountMeta::new(*stake_pubkey, true),
AccountMeta::new(syscall::current::id(), false),
];
Instruction::new(id(), &StakeInstruction::Deactivate, account_metas)
}
pub fn process_instruction(
_program_id: &Pubkey,
keyed_accounts: &mut [KeyedAccount],
@ -156,6 +171,14 @@ pub fn process_instruction(
&syscall::current::from_keyed_account(&syscall[0])?,
)
}
StakeInstruction::Deactivate => {
if rest.len() != 1 {
Err(InstructionError::InvalidInstructionData)?;
}
let syscall = &rest[0];
me.deactivate_stake(&syscall::current::from_keyed_account(&syscall)?)
}
}
}
@ -205,6 +228,10 @@ mod tests {
process_instruction(&withdraw(&Pubkey::default(), &Pubkey::new_rand(), 100)),
Err(InstructionError::InvalidAccountData),
);
assert_eq!(
process_instruction(&deactivate_stake(&Pubkey::default())),
Err(InstructionError::InvalidAccountData),
);
}
#[test]
@ -322,6 +349,41 @@ mod tests {
),
Err(InstructionError::InvalidInstructionData),
);
// Tests 2nd keyed account is of correct type (Current instead of rewards) in deactivate
assert_eq!(
super::process_instruction(
&Pubkey::default(),
&mut [
KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()),
KeyedAccount::new(
&syscall::rewards::id(),
false,
&mut syscall::rewards::create_account(1, 0.0, 0.0)
),
],
&serialize(&StakeInstruction::Deactivate).unwrap(),
),
Err(InstructionError::InvalidArgument),
);
// Tests correct number of accounts are provided in deactivate
assert_eq!(
super::process_instruction(
&Pubkey::default(),
&mut [
KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()),
KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()),
KeyedAccount::new(
&syscall::current::id(),
false,
&mut syscall::rewards::create_account(1, 0.0, 0.0)
),
],
&serialize(&StakeInstruction::Deactivate).unwrap(),
),
Err(InstructionError::InvalidInstructionData),
);
}
}

View File

@ -464,6 +464,44 @@ mod tests {
assert_eq!(stake.stake(STAKE_WARMUP_EPOCHS * 42), 0);
}
#[test]
fn test_deactivate_stake() {
let stake_pubkey = Pubkey::new_rand();
let stake_lamports = 42;
let mut stake_account =
Account::new(stake_lamports, std::mem::size_of::<StakeState>(), &id());
let current = syscall::current::Current::default();
// unsigned keyed account
let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &mut stake_account);
assert_eq!(
stake_keyed_account.deactivate_stake(&current),
Err(InstructionError::MissingRequiredSignature)
);
// signed keyed account but not staked yet
let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account);
assert_eq!(
stake_keyed_account.deactivate_stake(&current),
Err(InstructionError::InvalidAccountData)
);
// Staking
let vote_pubkey = Pubkey::new_rand();
let mut vote_account =
vote_state::create_account(&vote_pubkey, &Pubkey::new_rand(), 0, 100);
let mut vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &mut vote_account);
vote_keyed_account.set_state(&VoteState::default()).unwrap();
assert_eq!(
stake_keyed_account.delegate_stake(&vote_keyed_account, stake_lamports, &current),
Ok(())
);
// Deactivate after staking
assert_eq!(stake_keyed_account.deactivate_stake(&current), Ok(()));
}
#[test]
fn test_withdraw_stake() {
let stake_pubkey = Pubkey::new_rand();

View File

@ -53,6 +53,7 @@ pub enum WalletCommand {
CreateStakeAccount(Pubkey, u64),
DelegateStake(Keypair, Pubkey, u64),
WithdrawStake(Keypair, Pubkey, u64),
DeactivateStake(Keypair),
RedeemVoteCredits(Pubkey, Pubkey),
ShowStakeAccount(Pubkey),
CreateStorageMiningPoolAccount(Pubkey, u64),
@ -261,6 +262,11 @@ pub fn parse_command(
lamports,
))
}
("deactivate-stake", Some(matches)) => {
let staking_account_keypair =
keypair_of(matches, "staking_account_keypair_file").unwrap();
Ok(WalletCommand::DeactivateStake(staking_account_keypair))
}
("redeem-vote-credits", Some(matches)) => {
let staking_account_pubkey = value_of(matches, "staking_account_pubkey").unwrap();
let voting_account_pubkey = value_of(matches, "voting_account_pubkey").unwrap();
@ -571,6 +577,24 @@ fn process_create_stake_account(
Ok(signature_str.to_string())
}
fn process_deactivate_stake_account(
rpc_client: &RpcClient,
config: &WalletConfig,
staking_account_keypair: &Keypair,
) -> ProcessResult {
let (recent_blockhash, _fee_calculator) = rpc_client.get_recent_blockhash()?;
let ixs = stake_instruction::deactivate_stake(&staking_account_keypair.pubkey());
let mut tx = Transaction::new_signed_with_payer(
vec![ixs],
Some(&config.keypair.pubkey()),
&[&config.keypair, &staking_account_keypair],
recent_blockhash,
);
let signature_str = rpc_client
.send_and_confirm_transaction(&mut tx, &[&config.keypair, &staking_account_keypair])?;
Ok(signature_str.to_string())
}
fn process_delegate_stake(
rpc_client: &RpcClient,
config: &WalletConfig,
@ -1073,6 +1097,11 @@ pub fn process_command(config: &WalletConfig) -> ProcessResult {
*lamports,
),
// Deactivate stake account
WalletCommand::DeactivateStake(staking_account_keypair) => {
process_deactivate_stake_account(&rpc_client, config, &staking_account_keypair)
}
WalletCommand::RedeemVoteCredits(staking_account_pubkey, voting_account_pubkey) => {
process_redeem_vote_credits(
&rpc_client,
@ -1452,6 +1481,18 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
.help("The number of lamports to stake, must be less than the stake account's balance."),
),
)
.subcommand(
SubCommand::with_name("deactivate-stake")
.about("Deactivate the delegated stake from the staking account")
.arg(
Arg::with_name("staking_account_keypair_file")
.index(1)
.value_name("KEYPAIR_FILE")
.takes_value(true)
.required(true)
.help("Keypair file for the staking account, for signing the delegate transaction."),
)
)
.subcommand(
SubCommand::with_name("withdraw-stake")
.about("Withdraw the unstaked lamports from the stake account")
@ -1933,6 +1974,19 @@ mod tests {
WalletCommand::WithdrawStake(keypair, pubkey, 42)
);
// Test Deactivate Stake Subcommand
let keypair_file = make_tmp_path("keypair_file");
gen_keypair_file(&keypair_file).unwrap();
let keypair = read_keypair(&keypair_file).unwrap();
let test_deactivate_stake =
test_commands
.clone()
.get_matches_from(vec!["test", "deactivate-stake", &keypair_file]);
assert_eq!(
parse_command(&pubkey, &test_deactivate_stake).unwrap(),
WalletCommand::DeactivateStake(keypair)
);
// Test Deploy Subcommand
let test_deploy =
test_commands
@ -2108,6 +2162,11 @@ mod tests {
let signature = process_command(&config);
assert_eq!(signature.unwrap(), SIGNATURE.to_string());
let bob_keypair = Keypair::new();
config.command = WalletCommand::DeactivateStake(bob_keypair.into());
let signature = process_command(&config);
assert_eq!(signature.unwrap(), SIGNATURE.to_string());
config.command = WalletCommand::GetTransactionCount;
assert_eq!(process_command(&config).unwrap(), "1234");