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:
@ -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),
|
||||
|
@ -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]
|
||||
|
Reference in New Issue
Block a user