Add unix_timestap to stake lockups (#7569)

This commit is contained in:
Rob Walker
2019-12-19 14:37:47 -08:00
committed by GitHub
parent 0245847ea8
commit 3f405d8908
12 changed files with 1038 additions and 373 deletions

View File

@@ -8,7 +8,7 @@ use serde_derive::{Deserialize, Serialize};
use solana_sdk::{
account::{Account, KeyedAccount},
account_utils::State,
clock::{Clock, Epoch, Slot},
clock::{Clock, Epoch, Slot, UnixTimestamp},
instruction::InstructionError,
pubkey::Pubkey,
rent::Rent,
@@ -86,7 +86,10 @@ pub enum StakeAuthorize {
#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
pub struct Lockup {
/// epoch at which this stake will allow withdrawal, unless
/// UnixTimestamp at which this stake will allow withdrawal, unless
/// to the custodian.
pub unix_timestamp: UnixTimestamp,
/// epoch height at which this stake will allow withdrawal, unless
/// to the custodian
pub epoch: Epoch,
/// custodian account, the only account to which this stake will honor a
@@ -95,6 +98,13 @@ pub struct Lockup {
pub custodian: Pubkey,
}
impl Lockup {
pub fn is_in_force(&self, clock: &Clock, signers: &HashSet<Pubkey>) -> bool {
(self.unix_timestamp > clock.unix_timestamp || self.epoch > clock.epoch)
&& !signers.contains(&self.custodian)
}
}
#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
pub struct Authorized {
pub staker: Pubkey,
@@ -754,8 +764,8 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
};
// verify that lockup has expired or that the withdrawal is signed by
// the custodian
if lockup.epoch > clock.epoch && !signers.contains(&lockup.custodian) {
// the custodian, both epoch and unix_timestamp must have passed
if lockup.is_in_force(&clock, signers) {
return Err(StakeError::LockupInForce.into());
}
@@ -1404,6 +1414,7 @@ mod tests {
&Authorized::auto(&stake_pubkey),
&Lockup {
epoch: 1,
unix_timestamp: 0,
custodian
},
&Rent::free(),
@@ -1415,6 +1426,7 @@ mod tests {
StakeState::from(&stake_keyed_account.account).unwrap(),
StakeState::Initialized(Meta {
lockup: Lockup {
unix_timestamp: 0,
epoch: 1,
custodian
},
@@ -1555,6 +1567,7 @@ mod tests {
.initialize(
&Authorized::auto(&stake_pubkey),
&Lockup {
unix_timestamp: 0,
epoch: 0,
custodian,
},
@@ -1759,6 +1772,7 @@ mod tests {
total_lamports,
&StakeState::Initialized(Meta {
lockup: Lockup {
unix_timestamp: 0,
epoch: 1,
custodian,
},
@@ -2591,6 +2605,77 @@ mod tests {
}
}
#[test]
fn test_lockup_is_expired() {
let custodian = Pubkey::new_rand();
let signers = [custodian].iter().cloned().collect::<HashSet<_>>();
let lockup = Lockup {
epoch: 1,
unix_timestamp: 1,
custodian,
};
// neither time
assert_eq!(
lockup.is_in_force(
&Clock {
epoch: 0,
unix_timestamp: 0,
..Clock::default()
},
&HashSet::new()
),
true
);
// not timestamp
assert_eq!(
lockup.is_in_force(
&Clock {
epoch: 2,
unix_timestamp: 0,
..Clock::default()
},
&HashSet::new()
),
true
);
// not epoch
assert_eq!(
lockup.is_in_force(
&Clock {
epoch: 0,
unix_timestamp: 2,
..Clock::default()
},
&HashSet::new()
),
true
);
// both, no custodian
assert_eq!(
lockup.is_in_force(
&Clock {
epoch: 1,
unix_timestamp: 1,
..Clock::default()
},
&HashSet::new()
),
false
);
// neither, but custodian
assert_eq!(
lockup.is_in_force(
&Clock {
epoch: 0,
unix_timestamp: 0,
..Clock::default()
},
&signers,
),
false,
);
}
#[test]
fn test_authorize_delegated_stake() {
let stake_pubkey = Pubkey::new_rand();