Relax setting withdraw authority during lockup (#9644)

automerge
This commit is contained in:
Greg Fitzgerald 2020-04-21 22:05:49 -06:00 committed by GitHub
parent 5b8d963ee2
commit ba58589656
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 48 additions and 115 deletions

View File

@ -62,7 +62,8 @@ pub enum StakeInstruction {
/// Expects 2 Accounts: /// Expects 2 Accounts:
/// 0 - StakeAccount to be updated with the Pubkey for /// 0 - StakeAccount to be updated with the Pubkey for
/// authorization /// authorization
/// 1 - Clock sysvar Account that carries clock bank epoch /// 1 - (reserved for future use) Clock sysvar Account that carries
/// clock bank epoch
Authorize(Pubkey, StakeAuthorize), Authorize(Pubkey, StakeAuthorize),
/// `Delegate` a stake to a particular vote account /// `Delegate` a stake to a particular vote account
@ -383,12 +384,9 @@ pub fn process_instruction(
&lockup, &lockup,
&Rent::from_keyed_account(next_keyed_account(keyed_accounts)?)?, &Rent::from_keyed_account(next_keyed_account(keyed_accounts)?)?,
), ),
StakeInstruction::Authorize(authorized_pubkey, stake_authorize) => me.authorize( StakeInstruction::Authorize(authorized_pubkey, stake_authorize) => {
&authorized_pubkey, me.authorize(&authorized_pubkey, stake_authorize, &signers)
stake_authorize, }
&signers,
&Clock::from_keyed_account(next_keyed_account(keyed_accounts)?)?,
),
StakeInstruction::DelegateStake => { StakeInstruction::DelegateStake => {
let vote = next_keyed_account(keyed_accounts)?; let vote = next_keyed_account(keyed_accounts)?;

View File

@ -142,21 +142,19 @@ impl Meta {
Ok(()) Ok(())
} }
pub fn authorize( pub fn authorize_withdraw(
&mut self, &mut self,
authority: &Pubkey, authority: &Pubkey,
stake_authorize: StakeAuthorize,
signers: &HashSet<Pubkey>, signers: &HashSet<Pubkey>,
clock: &Clock, clock: &Clock,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
// verify that lockup has expired or that the authorization // verify that lockup has expired or that the authorization
// is *also* signed by the custodian // is *also* signed by the custodian
if stake_authorize == StakeAuthorize::Withdrawer && self.lockup.is_in_force(clock, signers) if self.lockup.is_in_force(clock, signers) {
{
return Err(StakeError::LockupInForce.into()); return Err(StakeError::LockupInForce.into());
} }
self.authorized self.authorized
.authorize(signers, authority, stake_authorize) .authorize(signers, authority, StakeAuthorize::Withdrawer)
} }
} }
@ -524,7 +522,6 @@ pub trait StakeAccount {
authority: &Pubkey, authority: &Pubkey,
stake_authorize: StakeAuthorize, stake_authorize: StakeAuthorize,
signers: &HashSet<Pubkey>, signers: &HashSet<Pubkey>,
clock: &Clock,
) -> Result<(), InstructionError>; ) -> Result<(), InstructionError>;
fn delegate( fn delegate(
&self, &self,
@ -588,15 +585,16 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
authority: &Pubkey, authority: &Pubkey,
stake_authorize: StakeAuthorize, stake_authorize: StakeAuthorize,
signers: &HashSet<Pubkey>, signers: &HashSet<Pubkey>,
clock: &Clock,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
match self.state()? { match self.state()? {
StakeState::Stake(mut meta, stake) => { StakeState::Stake(mut meta, stake) => {
meta.authorize(authority, stake_authorize, signers, clock)?; meta.authorized
.authorize(signers, authority, stake_authorize)?;
self.set_state(&StakeState::Stake(meta, stake)) self.set_state(&StakeState::Stake(meta, stake))
} }
StakeState::Initialized(mut meta) => { StakeState::Initialized(mut meta) => {
meta.authorize(authority, stake_authorize, signers, clock)?; meta.authorized
.authorize(signers, authority, stake_authorize)?;
self.set_state(&StakeState::Initialized(meta)) self.set_state(&StakeState::Initialized(meta))
} }
_ => Err(InstructionError::InvalidAccountData), _ => Err(InstructionError::InvalidAccountData),
@ -924,7 +922,23 @@ mod tests {
} }
#[test] #[test]
fn test_meta_authorize() { fn test_authorized_authorize() {
let staker = Pubkey::new_rand();
let mut authorized = Authorized::auto(&staker);
let mut signers = HashSet::new();
assert_eq!(
authorized.authorize(&signers, &staker, StakeAuthorize::Staker),
Err(InstructionError::MissingRequiredSignature)
);
signers.insert(staker);
assert_eq!(
authorized.authorize(&signers, &staker, StakeAuthorize::Staker),
Ok(())
);
}
#[test]
fn test_meta_authorize_withdraw() {
let staker = Pubkey::new_rand(); let staker = Pubkey::new_rand();
let custodian = Pubkey::new_rand(); let custodian = Pubkey::new_rand();
let mut meta = Meta { let mut meta = Meta {
@ -938,41 +952,22 @@ mod tests {
}; };
// verify sig check // verify sig check
let mut signers = HashSet::new(); let mut signers = HashSet::new();
signers.insert(staker);
let mut clock = Clock::default(); let mut clock = Clock::default();
assert_eq!( // verify lockup check
meta.authorize(&staker, StakeAuthorize::Staker, &signers, &clock),
Err(InstructionError::MissingRequiredSignature)
);
signers.insert(staker);
assert_eq!(
meta.authorize(&staker, StakeAuthorize::Staker, &signers, &clock),
Ok(())
);
// verify staker not subject to lockup, but withdrawer is
meta.lockup.epoch = 1; meta.lockup.epoch = 1;
assert_eq!( assert_eq!(
meta.authorize(&staker, StakeAuthorize::Staker, &signers, &clock), meta.authorize_withdraw(&staker, &signers, &clock),
Ok(())
);
// verify lockup check
assert_eq!(
meta.authorize(&staker, StakeAuthorize::Withdrawer, &signers, &clock),
Err(StakeError::LockupInForce.into()) Err(StakeError::LockupInForce.into())
); );
// verify lockup check defeated by custodian // verify lockup check defeated by custodian
signers.insert(custodian); signers.insert(custodian);
assert_eq!( assert_eq!(meta.authorize_withdraw(&staker, &signers, &clock), Ok(()));
meta.authorize(&staker, StakeAuthorize::Withdrawer, &signers, &clock),
Ok(())
);
// verify lock expiry // verify lock expiry
signers.remove(&custodian); signers.remove(&custodian);
clock.epoch = 1; clock.epoch = 1;
assert_eq!( assert_eq!(meta.authorize_withdraw(&staker, &signers, &clock), Ok(()));
meta.authorize(&staker, StakeAuthorize::Withdrawer, &signers, &clock),
Ok(())
);
} }
#[test] #[test]
@ -1612,7 +1607,7 @@ mod tests {
// wrong state, should fail // wrong state, should fail
let stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &stake_account); let stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &stake_account);
assert_eq!( assert_eq!(
stake_keyed_account.set_lockup(&LockupArgs::default(), &HashSet::default(),), stake_keyed_account.set_lockup(&LockupArgs::default(), &HashSet::default()),
Err(InstructionError::InvalidAccountData) Err(InstructionError::InvalidAccountData)
); );
@ -1631,7 +1626,7 @@ mod tests {
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
stake_keyed_account.set_lockup(&LockupArgs::default(), &HashSet::default(),), stake_keyed_account.set_lockup(&LockupArgs::default(), &HashSet::default()),
Err(InstructionError::MissingRequiredSignature) Err(InstructionError::MissingRequiredSignature)
); );
@ -2282,12 +2277,7 @@ mod tests {
let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account); let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account);
let signers = vec![stake_pubkey].into_iter().collect(); let signers = vec![stake_pubkey].into_iter().collect();
assert_eq!( assert_eq!(
stake_keyed_account.authorize( stake_keyed_account.authorize(&stake_pubkey, StakeAuthorize::Staker, &signers),
&stake_pubkey,
StakeAuthorize::Staker,
&signers,
&Clock::default()
),
Err(InstructionError::InvalidAccountData) Err(InstructionError::InvalidAccountData)
); );
} }
@ -2314,21 +2304,11 @@ mod tests {
let stake_pubkey0 = Pubkey::new_rand(); let stake_pubkey0 = Pubkey::new_rand();
let signers = vec![stake_pubkey].into_iter().collect(); let signers = vec![stake_pubkey].into_iter().collect();
assert_eq!( assert_eq!(
stake_keyed_account.authorize( stake_keyed_account.authorize(&stake_pubkey0, StakeAuthorize::Staker, &signers),
&stake_pubkey0,
StakeAuthorize::Staker,
&signers,
&Clock::default()
),
Ok(()) Ok(())
); );
assert_eq!( assert_eq!(
stake_keyed_account.authorize( stake_keyed_account.authorize(&stake_pubkey0, StakeAuthorize::Withdrawer, &signers),
&stake_pubkey0,
StakeAuthorize::Withdrawer,
&signers,
&Clock::default()
),
Ok(()) Ok(())
); );
if let StakeState::Initialized(Meta { authorized, .. }) = if let StakeState::Initialized(Meta { authorized, .. }) =
@ -2343,12 +2323,7 @@ mod tests {
// A second authorization signed by the stake_keyed_account should fail // A second authorization signed by the stake_keyed_account should fail
let stake_pubkey1 = Pubkey::new_rand(); let stake_pubkey1 = Pubkey::new_rand();
assert_eq!( assert_eq!(
stake_keyed_account.authorize( stake_keyed_account.authorize(&stake_pubkey1, StakeAuthorize::Staker, &signers),
&stake_pubkey1,
StakeAuthorize::Staker,
&signers,
&Clock::default()
),
Err(InstructionError::MissingRequiredSignature) Err(InstructionError::MissingRequiredSignature)
); );
@ -2357,12 +2332,7 @@ mod tests {
// Test a second authorization by the newly authorized pubkey // Test a second authorization by the newly authorized pubkey
let stake_pubkey2 = Pubkey::new_rand(); let stake_pubkey2 = Pubkey::new_rand();
assert_eq!( assert_eq!(
stake_keyed_account.authorize( stake_keyed_account.authorize(&stake_pubkey2, StakeAuthorize::Staker, &signers0),
&stake_pubkey2,
StakeAuthorize::Staker,
&signers0,
&Clock::default()
),
Ok(()) Ok(())
); );
if let StakeState::Initialized(Meta { authorized, .. }) = if let StakeState::Initialized(Meta { authorized, .. }) =
@ -2372,12 +2342,7 @@ mod tests {
} }
assert_eq!( assert_eq!(
stake_keyed_account.authorize( stake_keyed_account.authorize(&stake_pubkey2, StakeAuthorize::Withdrawer, &signers0),
&stake_pubkey2,
StakeAuthorize::Withdrawer,
&signers0,
&Clock::default()
),
Ok(()) Ok(())
); );
if let StakeState::Initialized(Meta { authorized, .. }) = if let StakeState::Initialized(Meta { authorized, .. }) =
@ -2432,12 +2397,7 @@ mod tests {
let stake_pubkey = Pubkey::new_rand(); let stake_pubkey = Pubkey::new_rand();
let signers = vec![withdrawer_pubkey].into_iter().collect(); let signers = vec![withdrawer_pubkey].into_iter().collect();
assert_eq!( assert_eq!(
stake_keyed_account.authorize( stake_keyed_account.authorize(&stake_pubkey, StakeAuthorize::Staker, &signers),
&stake_pubkey,
StakeAuthorize::Staker,
&signers,
&Clock::default()
),
Ok(()) Ok(())
); );
@ -2445,48 +2405,28 @@ mod tests {
let mallory_pubkey = Pubkey::new_rand(); let mallory_pubkey = Pubkey::new_rand();
let signers = vec![stake_pubkey].into_iter().collect(); let signers = vec![stake_pubkey].into_iter().collect();
assert_eq!( assert_eq!(
stake_keyed_account.authorize( stake_keyed_account.authorize(&mallory_pubkey, StakeAuthorize::Staker, &signers),
&mallory_pubkey,
StakeAuthorize::Staker,
&signers,
&Clock::default()
),
Ok(()) Ok(())
); );
// Verify the original staker no longer has access. // Verify the original staker no longer has access.
let new_stake_pubkey = Pubkey::new_rand(); let new_stake_pubkey = Pubkey::new_rand();
assert_eq!( assert_eq!(
stake_keyed_account.authorize( stake_keyed_account.authorize(&new_stake_pubkey, StakeAuthorize::Staker, &signers),
&new_stake_pubkey,
StakeAuthorize::Staker,
&signers,
&Clock::default()
),
Err(InstructionError::MissingRequiredSignature) Err(InstructionError::MissingRequiredSignature)
); );
// Verify the withdrawer (pulled from cold storage) can save the day. // Verify the withdrawer (pulled from cold storage) can save the day.
let signers = vec![withdrawer_pubkey].into_iter().collect(); let signers = vec![withdrawer_pubkey].into_iter().collect();
assert_eq!( assert_eq!(
stake_keyed_account.authorize( stake_keyed_account.authorize(&new_stake_pubkey, StakeAuthorize::Withdrawer, &signers),
&new_stake_pubkey,
StakeAuthorize::Withdrawer,
&signers,
&Clock::default()
),
Ok(()) Ok(())
); );
// Attack! Verify the staker cannot be used to authorize a withdraw. // Attack! Verify the staker cannot be used to authorize a withdraw.
let signers = vec![new_stake_pubkey].into_iter().collect(); let signers = vec![new_stake_pubkey].into_iter().collect();
assert_eq!( assert_eq!(
stake_keyed_account.authorize( stake_keyed_account.authorize(&mallory_pubkey, StakeAuthorize::Withdrawer, &signers),
&mallory_pubkey,
StakeAuthorize::Withdrawer,
&signers,
&Clock::default()
),
Ok(()) Ok(())
); );
} }
@ -3027,12 +2967,7 @@ mod tests {
let new_staker_pubkey = Pubkey::new_rand(); let new_staker_pubkey = Pubkey::new_rand();
assert_eq!( assert_eq!(
stake_keyed_account.authorize( stake_keyed_account.authorize(&new_staker_pubkey, StakeAuthorize::Staker, &signers),
&new_staker_pubkey,
StakeAuthorize::Staker,
&signers,
&clock,
),
Ok(()) Ok(())
); );
let authorized = let authorized =