parent
5b8d963ee2
commit
ba58589656
|
@ -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)?;
|
||||||
|
|
||||||
|
|
|
@ -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 =
|
||||||
|
|
Loading…
Reference in New Issue