Make lockups block stake transfers via rekeying (#7651)

This commit is contained in:
Rob Walker
2020-01-01 11:03:29 -08:00
committed by GitHub
parent 374c17a0d9
commit e97b0088f2
3 changed files with 125 additions and 18 deletions

View File

@ -256,7 +256,11 @@ pub fn authorize(
new_authorized_pubkey: &Pubkey,
stake_authorize: StakeAuthorize,
) -> Instruction {
let account_metas = vec![AccountMeta::new(*stake_pubkey, false)].with_signer(authorized_pubkey);
let account_metas = vec![
AccountMeta::new(*stake_pubkey, false),
AccountMeta::new_readonly(sysvar::clock::id(), false),
]
.with_signer(authorized_pubkey);
Instruction::new(
id(),
@ -357,9 +361,12 @@ pub fn process_instruction(
&lockup,
&Rent::from_keyed_account(next_keyed_account(keyed_accounts)?)?,
),
StakeInstruction::Authorize(authorized_pubkey, stake_authorize) => {
me.authorize(&authorized_pubkey, stake_authorize, &signers)
}
StakeInstruction::Authorize(authorized_pubkey, stake_authorize) => me.authorize(
&authorized_pubkey,
stake_authorize,
&signers,
&Clock::from_keyed_account(next_keyed_account(keyed_accounts)?)?,
),
StakeInstruction::DelegateStake => {
let vote = next_keyed_account(keyed_accounts)?;

View File

@ -118,6 +118,24 @@ pub struct Meta {
pub lockup: Lockup,
}
impl Meta {
pub fn authorize(
&mut self,
authority: &Pubkey,
stake_authorize: StakeAuthorize,
signers: &HashSet<Pubkey>,
clock: &Clock,
) -> Result<(), InstructionError> {
// verify that lockup has expired or that the authorization
// is *also* signed by the custodian
if self.lockup.is_in_force(clock, signers) {
return Err(StakeError::LockupInForce.into());
}
self.authorized
.authorize(signers, authority, stake_authorize)
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
pub struct Delegation {
/// to whom the stake is delegated
@ -488,6 +506,7 @@ pub trait StakeAccount {
authority: &Pubkey,
stake_authorize: StakeAuthorize,
signers: &HashSet<Pubkey>,
clock: &Clock,
) -> Result<(), InstructionError>;
fn delegate_stake(
&mut self,
@ -556,16 +575,15 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
authority: &Pubkey,
stake_authorize: StakeAuthorize,
signers: &HashSet<Pubkey>,
clock: &Clock,
) -> Result<(), InstructionError> {
match self.state()? {
StakeState::Stake(mut meta, stake) => {
meta.authorized
.authorize(signers, authority, stake_authorize)?;
meta.authorize(authority, stake_authorize, signers, clock)?;
self.set_state(&StakeState::Stake(meta, stake))
}
StakeState::Initialized(mut meta) => {
meta.authorized
.authorize(signers, authority, stake_authorize)?;
meta.authorize(authority, stake_authorize, signers, clock)?;
self.set_state(&StakeState::Initialized(meta))
}
_ => Err(InstructionError::InvalidAccountData),
@ -888,6 +906,53 @@ mod tests {
}
}
#[test]
fn test_meta_authorize() {
let staker = Pubkey::new_rand();
let custodian = Pubkey::new_rand();
let mut meta = Meta {
authorized: Authorized::auto(&staker),
lockup: Lockup {
epoch: 0,
unix_timestamp: 0,
custodian,
},
..Meta::default()
};
// verify sig check
let mut signers = HashSet::new();
let mut clock = Clock::default();
assert_eq!(
meta.authorize(&staker, StakeAuthorize::Staker, &signers, &clock),
Err(InstructionError::MissingRequiredSignature)
);
signers.insert(staker);
assert_eq!(
meta.authorize(&staker, StakeAuthorize::Staker, &signers, &clock),
Ok(())
);
// verify lockup check
meta.lockup.epoch = 1;
assert_eq!(
meta.authorize(&staker, StakeAuthorize::Staker, &signers, &clock),
Err(StakeError::LockupInForce.into())
);
// verify lockup check defeated by custodian
signers.insert(custodian);
assert_eq!(
meta.authorize(&staker, StakeAuthorize::Staker, &signers, &clock),
Ok(())
);
// verify lock expiry
signers.remove(&custodian);
clock.epoch = 1;
assert_eq!(
meta.authorize(&staker, StakeAuthorize::Staker, &signers, &clock),
Ok(())
);
}
#[test]
fn test_stake_state_stake_from_fail() {
let mut stake_account = Account::new(0, std::mem::size_of::<StakeState>(), &id());
@ -2085,7 +2150,12 @@ mod tests {
let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account);
let signers = vec![stake_pubkey].into_iter().collect();
assert_eq!(
stake_keyed_account.authorize(&stake_pubkey, StakeAuthorize::Staker, &signers),
stake_keyed_account.authorize(
&stake_pubkey,
StakeAuthorize::Staker,
&signers,
&Clock::default()
),
Err(InstructionError::InvalidAccountData)
);
}
@ -2112,11 +2182,21 @@ mod tests {
let stake_pubkey0 = Pubkey::new_rand();
let signers = vec![stake_pubkey].into_iter().collect();
assert_eq!(
stake_keyed_account.authorize(&stake_pubkey0, StakeAuthorize::Staker, &signers),
stake_keyed_account.authorize(
&stake_pubkey0,
StakeAuthorize::Staker,
&signers,
&Clock::default()
),
Ok(())
);
assert_eq!(
stake_keyed_account.authorize(&stake_pubkey0, StakeAuthorize::Withdrawer, &signers),
stake_keyed_account.authorize(
&stake_pubkey0,
StakeAuthorize::Withdrawer,
&signers,
&Clock::default()
),
Ok(())
);
if let StakeState::Initialized(Meta { authorized, .. }) =
@ -2131,7 +2211,12 @@ mod tests {
// A second authorization signed by the stake_keyed_account should fail
let stake_pubkey1 = Pubkey::new_rand();
assert_eq!(
stake_keyed_account.authorize(&stake_pubkey1, StakeAuthorize::Staker, &signers),
stake_keyed_account.authorize(
&stake_pubkey1,
StakeAuthorize::Staker,
&signers,
&Clock::default()
),
Err(InstructionError::MissingRequiredSignature)
);
@ -2140,7 +2225,12 @@ mod tests {
// Test a second authorization by the newly authorized pubkey
let stake_pubkey2 = Pubkey::new_rand();
assert_eq!(
stake_keyed_account.authorize(&stake_pubkey2, StakeAuthorize::Staker, &signers0),
stake_keyed_account.authorize(
&stake_pubkey2,
StakeAuthorize::Staker,
&signers0,
&Clock::default()
),
Ok(())
);
if let StakeState::Initialized(Meta { authorized, .. }) =
@ -2150,7 +2240,12 @@ mod tests {
}
assert_eq!(
stake_keyed_account.authorize(&stake_pubkey2, StakeAuthorize::Withdrawer, &signers0,),
stake_keyed_account.authorize(
&stake_pubkey2,
StakeAuthorize::Withdrawer,
&signers0,
&Clock::default()
),
Ok(())
);
if let StakeState::Initialized(Meta { authorized, .. }) =
@ -2689,7 +2784,7 @@ mod tests {
)
.expect("stake_account");
let mut clock = sysvar::clock::Clock::default();
let mut clock = Clock::default();
let vote_pubkey = Pubkey::new_rand();
let mut vote_account =
@ -2704,7 +2799,12 @@ mod tests {
let new_staker_pubkey = Pubkey::new_rand();
assert_eq!(
stake_keyed_account.authorize(&new_staker_pubkey, StakeAuthorize::Staker, &signers),
stake_keyed_account.authorize(
&new_staker_pubkey,
StakeAuthorize::Staker,
&signers,
&clock,
),
Ok(())
);
let authorized = StakeState::authorized_from(&stake_keyed_account.account).unwrap();