Allow stake lockup fields to be updated independently (#8568)

* Make Lockup fields optional for SetLockup instruction

* Use LockupArgs in cli

* Include lockup timestamp in stake-account print
This commit is contained in:
Tyera Eulberg
2020-03-02 12:28:43 -08:00
committed by GitHub
parent 13551885c2
commit 8dc4724340
5 changed files with 217 additions and 57 deletions

View File

@ -7,6 +7,7 @@ use num_derive::{FromPrimitive, ToPrimitive};
use serde_derive::{Deserialize, Serialize};
use solana_sdk::{
account::{get_signers, KeyedAccount},
clock::{Epoch, UnixTimestamp},
instruction::{AccountMeta, Instruction, InstructionError, WithSigner},
program_utils::{limited_deserialize, next_keyed_account, DecodeError},
pubkey::Pubkey,
@ -121,7 +122,14 @@ pub enum StakeInstruction {
/// Expects 1 Account:
/// 0 - initialized StakeAccount
///
SetLockup(Lockup),
SetLockup(LockupArgs),
}
#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
pub struct LockupArgs {
pub unix_timestamp: Option<UnixTimestamp>,
pub epoch: Option<Epoch>,
pub custodian: Option<Pubkey>,
}
fn initialize(stake_pubkey: &Pubkey, authorized: &Authorized, lockup: &Lockup) -> Instruction {
@ -361,7 +369,7 @@ pub fn deactivate_stake(stake_pubkey: &Pubkey, authorized_pubkey: &Pubkey) -> In
pub fn set_lockup(
stake_pubkey: &Pubkey,
lockup: &Lockup,
lockup: &LockupArgs,
custodian_pubkey: &Pubkey,
) -> Instruction {
let account_metas = vec![AccountMeta::new(*stake_pubkey, false)].with_signer(custodian_pubkey);
@ -540,7 +548,7 @@ mod tests {
assert_eq!(
process_instruction(&set_lockup(
&Pubkey::default(),
&Lockup::default(),
&LockupArgs::default(),
&Pubkey::default()
)),
Err(InstructionError::InvalidAccountData),

View File

@ -3,7 +3,11 @@
//! * keep track of rewards
//! * own mining pools
use crate::{config::Config, id, stake_instruction::StakeError};
use crate::{
config::Config,
id,
stake_instruction::{LockupArgs, StakeError},
};
use serde_derive::{Deserialize, Serialize};
use solana_sdk::{
account::{Account, KeyedAccount},
@ -120,13 +124,21 @@ pub struct Meta {
impl Meta {
pub fn set_lockup(
&mut self,
lockup: &Lockup,
lockup: &LockupArgs,
signers: &HashSet<Pubkey>,
) -> Result<(), InstructionError> {
if !signers.contains(&self.lockup.custodian) {
return Err(InstructionError::MissingRequiredSignature);
}
self.lockup = *lockup;
if let Some(unix_timestamp) = lockup.unix_timestamp {
self.lockup.unix_timestamp = unix_timestamp;
}
if let Some(epoch) = lockup.epoch {
self.lockup.epoch = epoch;
}
if let Some(custodian) = lockup.custodian {
self.lockup.custodian = custodian;
}
Ok(())
}
@ -524,7 +536,7 @@ pub trait StakeAccount {
fn deactivate(&self, clock: &Clock, signers: &HashSet<Pubkey>) -> Result<(), InstructionError>;
fn set_lockup(
&self,
lockup: &Lockup,
lockup: &LockupArgs,
signers: &HashSet<Pubkey>,
) -> Result<(), InstructionError>;
fn split(
@ -635,7 +647,7 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
}
fn set_lockup(
&self,
lockup: &Lockup,
lockup: &LockupArgs,
signers: &HashSet<Pubkey>,
) -> Result<(), InstructionError> {
match self.state()? {
@ -1594,7 +1606,7 @@ mod tests {
// wrong state, should fail
let stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &stake_account);
assert_eq!(
stake_keyed_account.set_lockup(&Lockup::default(), &HashSet::default(),),
stake_keyed_account.set_lockup(&LockupArgs::default(), &HashSet::default(),),
Err(InstructionError::InvalidAccountData)
);
@ -1613,16 +1625,16 @@ mod tests {
.unwrap();
assert_eq!(
stake_keyed_account.set_lockup(&Lockup::default(), &HashSet::default(),),
stake_keyed_account.set_lockup(&LockupArgs::default(), &HashSet::default(),),
Err(InstructionError::MissingRequiredSignature)
);
assert_eq!(
stake_keyed_account.set_lockup(
&Lockup {
unix_timestamp: 1,
epoch: 1,
custodian,
&LockupArgs {
unix_timestamp: Some(1),
epoch: Some(1),
custodian: Some(custodian),
},
&vec![custodian].into_iter().collect()
),
@ -1654,10 +1666,10 @@ mod tests {
assert_eq!(
stake_keyed_account.set_lockup(
&Lockup {
unix_timestamp: 1,
epoch: 1,
custodian,
&LockupArgs {
unix_timestamp: Some(1),
epoch: Some(1),
custodian: Some(custodian),
},
&HashSet::default(),
),
@ -1665,15 +1677,129 @@ mod tests {
);
assert_eq!(
stake_keyed_account.set_lockup(
&LockupArgs {
unix_timestamp: Some(1),
epoch: Some(1),
custodian: Some(custodian),
},
&vec![custodian].into_iter().collect()
),
Ok(())
);
}
#[test]
fn test_optional_lockup() {
let stake_pubkey = Pubkey::new_rand();
let stake_lamports = 42;
let stake_account = Account::new_ref_data_with_space(
stake_lamports,
&StakeState::Uninitialized,
std::mem::size_of::<StakeState>(),
&id(),
)
.expect("stake_account");
let stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &stake_account);
let custodian = Pubkey::new_rand();
stake_keyed_account
.initialize(
&Authorized::auto(&stake_pubkey),
&Lockup {
unix_timestamp: 1,
epoch: 1,
custodian,
},
&Rent::free(),
)
.unwrap();
assert_eq!(
stake_keyed_account.set_lockup(
&LockupArgs {
unix_timestamp: None,
epoch: None,
custodian: None,
},
&vec![custodian].into_iter().collect()
),
Ok(())
);
assert_eq!(
stake_keyed_account.set_lockup(
&LockupArgs {
unix_timestamp: Some(2),
epoch: None,
custodian: None,
},
&vec![custodian].into_iter().collect()
),
Ok(())
);
if let StakeState::Initialized(Meta { lockup, .. }) =
StakeState::from(&stake_keyed_account.account.borrow()).unwrap()
{
assert_eq!(lockup.unix_timestamp, 2);
assert_eq!(lockup.epoch, 1);
assert_eq!(lockup.custodian, custodian);
} else {
assert!(false);
}
assert_eq!(
stake_keyed_account.set_lockup(
&LockupArgs {
unix_timestamp: None,
epoch: Some(3),
custodian: None,
},
&vec![custodian].into_iter().collect()
),
Ok(())
);
if let StakeState::Initialized(Meta { lockup, .. }) =
StakeState::from(&stake_keyed_account.account.borrow()).unwrap()
{
assert_eq!(lockup.unix_timestamp, 2);
assert_eq!(lockup.epoch, 3);
assert_eq!(lockup.custodian, custodian);
} else {
assert!(false);
}
let new_custodian = Pubkey::new_rand();
assert_eq!(
stake_keyed_account.set_lockup(
&LockupArgs {
unix_timestamp: None,
epoch: None,
custodian: Some(new_custodian),
},
&vec![custodian].into_iter().collect()
),
Ok(())
);
if let StakeState::Initialized(Meta { lockup, .. }) =
StakeState::from(&stake_keyed_account.account.borrow()).unwrap()
{
assert_eq!(lockup.unix_timestamp, 2);
assert_eq!(lockup.epoch, 3);
assert_eq!(lockup.custodian, new_custodian);
} else {
assert!(false);
}
assert_eq!(
stake_keyed_account.set_lockup(
&LockupArgs::default(),
&vec![custodian].into_iter().collect()
),
Err(InstructionError::MissingRequiredSignature)
);
}
#[test]