Add authorize_staker functionality (#5880)
* Add authorized_staker functionality * Generalize authorize names; implement for Lockup * Fix authorize() usage and improve tests
This commit is contained in:
		| @@ -45,7 +45,11 @@ pub enum StakeInstruction { | |||||||
|     ///    will allow withdrawal from the stake account. |     ///    will allow withdrawal from the stake account. | ||||||
|     /// |     /// | ||||||
|     Lockup(Slot), |     Lockup(Slot), | ||||||
|  |     /// Authorize a system account to manage stake | ||||||
|  |     /// | ||||||
|  |     /// Expects 1 Account: | ||||||
|  |     ///     0 - Locked-up or delegated StakeAccount to be updated with authorized staker | ||||||
|  |     Authorize(Pubkey), | ||||||
|     /// `Delegate` a stake to a particular vote account |     /// `Delegate` a stake to a particular vote account | ||||||
|     /// |     /// | ||||||
|     /// Expects 4 Accounts: |     /// Expects 4 Accounts: | ||||||
| @@ -133,6 +137,42 @@ pub fn create_stake_account_and_delegate_stake( | |||||||
|     instructions |     instructions | ||||||
| } | } | ||||||
|  |  | ||||||
|  | fn metas_for_authorized_staker( | ||||||
|  |     stake_pubkey: &Pubkey, | ||||||
|  |     authorized_pubkey: &Pubkey, // currently authorized | ||||||
|  |     other_params: &[AccountMeta], | ||||||
|  | ) -> Vec<AccountMeta> { | ||||||
|  |     let is_own_signer = authorized_pubkey == stake_pubkey; | ||||||
|  |  | ||||||
|  |     // stake account | ||||||
|  |     let mut account_metas = vec![AccountMeta::new(*stake_pubkey, is_own_signer)]; | ||||||
|  |  | ||||||
|  |     for meta in other_params { | ||||||
|  |         account_metas.push(meta.clone()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // append signer at the end | ||||||
|  |     if !is_own_signer { | ||||||
|  |         account_metas.push(AccountMeta::new_credit_only(*authorized_pubkey, true)) // signer | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     account_metas | ||||||
|  | } | ||||||
|  |  | ||||||
|  | pub fn authorize( | ||||||
|  |     stake_pubkey: &Pubkey, | ||||||
|  |     authorized_pubkey: &Pubkey, | ||||||
|  |     new_authorized_pubkey: &Pubkey, | ||||||
|  | ) -> Instruction { | ||||||
|  |     let account_metas = metas_for_authorized_staker(stake_pubkey, authorized_pubkey, &[]); | ||||||
|  |  | ||||||
|  |     Instruction::new( | ||||||
|  |         id(), | ||||||
|  |         &StakeInstruction::Authorize(*new_authorized_pubkey), | ||||||
|  |         account_metas, | ||||||
|  |     ) | ||||||
|  | } | ||||||
|  |  | ||||||
| pub fn redeem_vote_credits(stake_pubkey: &Pubkey, vote_pubkey: &Pubkey) -> Instruction { | pub fn redeem_vote_credits(stake_pubkey: &Pubkey, vote_pubkey: &Pubkey) -> Instruction { | ||||||
|     let account_metas = vec![ |     let account_metas = vec![ | ||||||
|         AccountMeta::new(*stake_pubkey, false), |         AccountMeta::new(*stake_pubkey, false), | ||||||
| @@ -193,8 +233,9 @@ pub fn process_instruction( | |||||||
|     // TODO: data-driven unpack and dispatch of KeyedAccounts |     // TODO: data-driven unpack and dispatch of KeyedAccounts | ||||||
|     match deserialize(data).map_err(|_| InstructionError::InvalidInstructionData)? { |     match deserialize(data).map_err(|_| InstructionError::InvalidInstructionData)? { | ||||||
|         StakeInstruction::Lockup(slot) => me.lockup(slot), |         StakeInstruction::Lockup(slot) => me.lockup(slot), | ||||||
|  |         StakeInstruction::Authorize(authorized_pubkey) => me.authorize(&authorized_pubkey, &rest), | ||||||
|         StakeInstruction::DelegateStake => { |         StakeInstruction::DelegateStake => { | ||||||
|             if rest.len() != 3 { |             if rest.len() < 3 { | ||||||
|                 Err(InstructionError::InvalidInstructionData)?; |                 Err(InstructionError::InvalidInstructionData)?; | ||||||
|             } |             } | ||||||
|             let vote = &rest[0]; |             let vote = &rest[0]; | ||||||
| @@ -203,6 +244,7 @@ pub fn process_instruction( | |||||||
|                 vote, |                 vote, | ||||||
|                 &sysvar::clock::from_keyed_account(&rest[1])?, |                 &sysvar::clock::from_keyed_account(&rest[1])?, | ||||||
|                 &config::from_keyed_account(&rest[2])?, |                 &config::from_keyed_account(&rest[2])?, | ||||||
|  |                 &rest[3..], | ||||||
|             ) |             ) | ||||||
|         } |         } | ||||||
|         StakeInstruction::RedeemVoteCredits => { |         StakeInstruction::RedeemVoteCredits => { | ||||||
| @@ -222,28 +264,32 @@ pub fn process_instruction( | |||||||
|             ) |             ) | ||||||
|         } |         } | ||||||
|         StakeInstruction::Withdraw(lamports) => { |         StakeInstruction::Withdraw(lamports) => { | ||||||
|             if rest.len() != 3 { |             if rest.len() < 3 { | ||||||
|                 Err(InstructionError::InvalidInstructionData)?; |                 Err(InstructionError::InvalidInstructionData)?; | ||||||
|             } |             } | ||||||
|             let (to, sysvar) = &mut rest.split_at_mut(1); |             let (to, rest) = &mut rest.split_at_mut(1); | ||||||
|             let mut to = &mut to[0]; |             let mut to = &mut to[0]; | ||||||
|  |  | ||||||
|             me.withdraw( |             me.withdraw( | ||||||
|                 lamports, |                 lamports, | ||||||
|                 &mut to, |                 &mut to, | ||||||
|                 &sysvar::clock::from_keyed_account(&sysvar[0])?, |                 &sysvar::clock::from_keyed_account(&rest[0])?, | ||||||
|                 &sysvar::stake_history::from_keyed_account(&sysvar[1])?, |                 &sysvar::stake_history::from_keyed_account(&rest[1])?, | ||||||
|  |                 &rest[2..], | ||||||
|             ) |             ) | ||||||
|         } |         } | ||||||
|         StakeInstruction::Deactivate => { |         StakeInstruction::Deactivate => { | ||||||
|             if rest.len() != 2 { |             if rest.len() < 2 { | ||||||
|                 Err(InstructionError::InvalidInstructionData)?; |                 Err(InstructionError::InvalidInstructionData)?; | ||||||
|             } |             } | ||||||
|             let (vote, rest) = rest.split_at_mut(1); |             let (vote, rest) = rest.split_at_mut(1); | ||||||
|             let vote = &mut vote[0]; |             let vote = &mut vote[0]; | ||||||
|             let clock = &rest[0]; |  | ||||||
|  |  | ||||||
|             me.deactivate_stake(vote, &sysvar::clock::from_keyed_account(&clock)?) |             me.deactivate_stake( | ||||||
|  |                 vote, | ||||||
|  |                 &sysvar::clock::from_keyed_account(&rest[0])?, | ||||||
|  |                 &rest[1..], | ||||||
|  |             ) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -22,7 +22,7 @@ use solana_vote_api::vote_state::VoteState; | |||||||
| #[allow(clippy::large_enum_variant)] | #[allow(clippy::large_enum_variant)] | ||||||
| pub enum StakeState { | pub enum StakeState { | ||||||
|     Uninitialized, |     Uninitialized, | ||||||
|     Lockup(Slot), |     Lockup(Lockup), | ||||||
|     Stake(Stake), |     Stake(Stake), | ||||||
|     RewardsPool, |     RewardsPool, | ||||||
| } | } | ||||||
| @@ -51,8 +51,18 @@ impl StakeState { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] | ||||||
|  | pub struct Lockup { | ||||||
|  |     /// slot height at which this stake will allow withdrawal | ||||||
|  |     pub slot: Slot, | ||||||
|  |     /// alternate signer that is enabled to act on the Stake account after lockup | ||||||
|  |     pub authorized_pubkey: Pubkey, | ||||||
|  | } | ||||||
|  |  | ||||||
| #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] | #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] | ||||||
| pub struct Stake { | pub struct Stake { | ||||||
|  |     /// alternate signer that is enabled to act on the Stake account | ||||||
|  |     pub authorized_pubkey: Pubkey, | ||||||
|     /// most recently delegated vote account pubkey |     /// most recently delegated vote account pubkey | ||||||
|     pub voter_pubkey: Pubkey, |     pub voter_pubkey: Pubkey, | ||||||
|     /// the epoch when voter_pubkey was most recently set |     /// the epoch when voter_pubkey was most recently set | ||||||
| @@ -81,6 +91,7 @@ const MAX_PRIOR_DELEGATES: usize = 32; // this is how many epochs a stake is exp | |||||||
| impl Default for Stake { | impl Default for Stake { | ||||||
|     fn default() -> Self { |     fn default() -> Self { | ||||||
|         Self { |         Self { | ||||||
|  |             authorized_pubkey: Pubkey::default(), | ||||||
|             voter_pubkey: Pubkey::default(), |             voter_pubkey: Pubkey::default(), | ||||||
|             voter_pubkey_epoch: 0, |             voter_pubkey_epoch: 0, | ||||||
|             credits_observed: 0, |             credits_observed: 0, | ||||||
| @@ -286,6 +297,7 @@ impl Stake { | |||||||
|     fn new_bootstrap(stake: u64, voter_pubkey: &Pubkey, vote_state: &VoteState) -> Self { |     fn new_bootstrap(stake: u64, voter_pubkey: &Pubkey, vote_state: &VoteState) -> Self { | ||||||
|         Self::new( |         Self::new( | ||||||
|             stake, |             stake, | ||||||
|  |             &Pubkey::default(), | ||||||
|             voter_pubkey, |             voter_pubkey, | ||||||
|             vote_state, |             vote_state, | ||||||
|             std::u64::MAX, |             std::u64::MAX, | ||||||
| @@ -316,6 +328,7 @@ impl Stake { | |||||||
|  |  | ||||||
|     fn new( |     fn new( | ||||||
|         stake: u64, |         stake: u64, | ||||||
|  |         stake_pubkey: &Pubkey, | ||||||
|         voter_pubkey: &Pubkey, |         voter_pubkey: &Pubkey, | ||||||
|         vote_state: &VoteState, |         vote_state: &VoteState, | ||||||
|         activation_epoch: Epoch, |         activation_epoch: Epoch, | ||||||
| @@ -325,6 +338,7 @@ impl Stake { | |||||||
|         Self { |         Self { | ||||||
|             stake, |             stake, | ||||||
|             activation_epoch, |             activation_epoch, | ||||||
|  |             authorized_pubkey: *stake_pubkey, | ||||||
|             voter_pubkey: *voter_pubkey, |             voter_pubkey: *voter_pubkey, | ||||||
|             voter_pubkey_epoch: activation_epoch, |             voter_pubkey_epoch: activation_epoch, | ||||||
|             credits_observed: vote_state.credits(), |             credits_observed: vote_state.credits(), | ||||||
| @@ -339,18 +353,69 @@ impl Stake { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | trait Authorized { | ||||||
|  |     fn check_authorized( | ||||||
|  |         &self, | ||||||
|  |         stake_pubkey_signer: Option<&Pubkey>, | ||||||
|  |         other_signers: &[KeyedAccount], | ||||||
|  |     ) -> Result<(), InstructionError>; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Authorized for Lockup { | ||||||
|  |     fn check_authorized( | ||||||
|  |         &self, | ||||||
|  |         stake_pubkey_signer: Option<&Pubkey>, | ||||||
|  |         other_signers: &[KeyedAccount], | ||||||
|  |     ) -> Result<(), InstructionError> { | ||||||
|  |         let authorized = Some(&self.authorized_pubkey); | ||||||
|  |         if stake_pubkey_signer != authorized | ||||||
|  |             && other_signers | ||||||
|  |                 .iter() | ||||||
|  |                 .all(|account| account.signer_key() != authorized) | ||||||
|  |         { | ||||||
|  |             return Err(InstructionError::MissingRequiredSignature); | ||||||
|  |         } | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Authorized for Stake { | ||||||
|  |     fn check_authorized( | ||||||
|  |         &self, | ||||||
|  |         stake_pubkey_signer: Option<&Pubkey>, | ||||||
|  |         other_signers: &[KeyedAccount], | ||||||
|  |     ) -> Result<(), InstructionError> { | ||||||
|  |         let authorized = Some(&self.authorized_pubkey); | ||||||
|  |         if stake_pubkey_signer != authorized | ||||||
|  |             && other_signers | ||||||
|  |                 .iter() | ||||||
|  |                 .all(|account| account.signer_key() != authorized) | ||||||
|  |         { | ||||||
|  |             return Err(InstructionError::MissingRequiredSignature); | ||||||
|  |         } | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| pub trait StakeAccount { | pub trait StakeAccount { | ||||||
|     fn lockup(&mut self, slot: Slot) -> Result<(), InstructionError>; |     fn lockup(&mut self, slot: Slot) -> Result<(), InstructionError>; | ||||||
|  |     fn authorize( | ||||||
|  |         &mut self, | ||||||
|  |         authorized_pubkey: &Pubkey, | ||||||
|  |         other_signers: &[KeyedAccount], | ||||||
|  |     ) -> Result<(), InstructionError>; | ||||||
|     fn delegate_stake( |     fn delegate_stake( | ||||||
|         &mut self, |         &mut self, | ||||||
|         vote_account: &KeyedAccount, |         vote_account: &KeyedAccount, | ||||||
|         clock: &sysvar::clock::Clock, |         clock: &sysvar::clock::Clock, | ||||||
|         config: &Config, |         config: &Config, | ||||||
|  |         other_signers: &[KeyedAccount], | ||||||
|     ) -> Result<(), InstructionError>; |     ) -> Result<(), InstructionError>; | ||||||
|     fn deactivate_stake( |     fn deactivate_stake( | ||||||
|         &mut self, |         &mut self, | ||||||
|         vote_account: &KeyedAccount, |         vote_account: &KeyedAccount, | ||||||
|         clock: &sysvar::clock::Clock, |         clock: &sysvar::clock::Clock, | ||||||
|  |         other_signers: &[KeyedAccount], | ||||||
|     ) -> Result<(), InstructionError>; |     ) -> Result<(), InstructionError>; | ||||||
|     fn redeem_vote_credits( |     fn redeem_vote_credits( | ||||||
|         &mut self, |         &mut self, | ||||||
| @@ -365,12 +430,37 @@ pub trait StakeAccount { | |||||||
|         to: &mut KeyedAccount, |         to: &mut KeyedAccount, | ||||||
|         clock: &sysvar::clock::Clock, |         clock: &sysvar::clock::Clock, | ||||||
|         stake_history: &sysvar::stake_history::StakeHistory, |         stake_history: &sysvar::stake_history::StakeHistory, | ||||||
|  |         other_signers: &[KeyedAccount], | ||||||
|     ) -> Result<(), InstructionError>; |     ) -> Result<(), InstructionError>; | ||||||
| } | } | ||||||
|  |  | ||||||
| impl<'a> StakeAccount for KeyedAccount<'a> { | impl<'a> StakeAccount for KeyedAccount<'a> { | ||||||
|     fn lockup(&mut self, lockup: Slot) -> Result<(), InstructionError> { |     fn lockup(&mut self, lockup: Slot) -> Result<(), InstructionError> { | ||||||
|         if let StakeState::Uninitialized = self.state()? { |         if let StakeState::Uninitialized = self.state()? { | ||||||
|  |             self.set_state(&StakeState::Lockup(Lockup { | ||||||
|  |                 slot: lockup, | ||||||
|  |                 authorized_pubkey: *self.unsigned_key(), | ||||||
|  |             })) | ||||||
|  |         } else { | ||||||
|  |             Err(InstructionError::InvalidAccountData) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     /// Authorize the given pubkey to manage stake (deactivate, withdraw). This may be called | ||||||
|  |     /// multiple times, but will implicitly withdraw authorization from the previously authorized | ||||||
|  |     /// staker. The default staker is the owner of the stake account's pubkey. | ||||||
|  |     fn authorize( | ||||||
|  |         &mut self, | ||||||
|  |         authorized_pubkey: &Pubkey, | ||||||
|  |         other_signers: &[KeyedAccount], | ||||||
|  |     ) -> Result<(), InstructionError> { | ||||||
|  |         let stake_state = self.state()?; | ||||||
|  |         if let StakeState::Stake(mut stake) = stake_state { | ||||||
|  |             stake.check_authorized(self.signer_key(), other_signers)?; | ||||||
|  |             stake.authorized_pubkey = *authorized_pubkey; | ||||||
|  |             self.set_state(&StakeState::Stake(stake)) | ||||||
|  |         } else if let StakeState::Lockup(mut lockup) = stake_state { | ||||||
|  |             lockup.check_authorized(self.signer_key(), other_signers)?; | ||||||
|  |             lockup.authorized_pubkey = *authorized_pubkey; | ||||||
|             self.set_state(&StakeState::Lockup(lockup)) |             self.set_state(&StakeState::Lockup(lockup)) | ||||||
|         } else { |         } else { | ||||||
|             Err(InstructionError::InvalidAccountData) |             Err(InstructionError::InvalidAccountData) | ||||||
| @@ -381,23 +471,23 @@ impl<'a> StakeAccount for KeyedAccount<'a> { | |||||||
|         vote_account: &KeyedAccount, |         vote_account: &KeyedAccount, | ||||||
|         clock: &sysvar::clock::Clock, |         clock: &sysvar::clock::Clock, | ||||||
|         config: &Config, |         config: &Config, | ||||||
|  |         other_signers: &[KeyedAccount], | ||||||
|     ) -> Result<(), InstructionError> { |     ) -> Result<(), InstructionError> { | ||||||
|         if self.signer_key().is_none() { |  | ||||||
|             return Err(InstructionError::MissingRequiredSignature); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if let StakeState::Lockup(lockup) = self.state()? { |         if let StakeState::Lockup(lockup) = self.state()? { | ||||||
|  |             lockup.check_authorized(self.signer_key(), other_signers)?; | ||||||
|             let stake = Stake::new( |             let stake = Stake::new( | ||||||
|                 self.account.lamports, |                 self.account.lamports, | ||||||
|  |                 &lockup.authorized_pubkey, | ||||||
|                 vote_account.unsigned_key(), |                 vote_account.unsigned_key(), | ||||||
|                 &vote_account.state()?, |                 &vote_account.state()?, | ||||||
|                 clock.epoch, |                 clock.epoch, | ||||||
|                 config, |                 config, | ||||||
|                 lockup, |                 lockup.slot, | ||||||
|             ); |             ); | ||||||
|  |  | ||||||
|             self.set_state(&StakeState::Stake(stake)) |             self.set_state(&StakeState::Stake(stake)) | ||||||
|         } else if let StakeState::Stake(mut stake) = self.state()? { |         } else if let StakeState::Stake(mut stake) = self.state()? { | ||||||
|  |             stake.check_authorized(self.signer_key(), other_signers)?; | ||||||
|             stake.redelegate( |             stake.redelegate( | ||||||
|                 vote_account.unsigned_key(), |                 vote_account.unsigned_key(), | ||||||
|                 &vote_account.state()?, |                 &vote_account.state()?, | ||||||
| @@ -412,12 +502,10 @@ impl<'a> StakeAccount for KeyedAccount<'a> { | |||||||
|         &mut self, |         &mut self, | ||||||
|         _vote_account: &KeyedAccount, // TODO: used in slashing |         _vote_account: &KeyedAccount, // TODO: used in slashing | ||||||
|         clock: &sysvar::clock::Clock, |         clock: &sysvar::clock::Clock, | ||||||
|  |         other_signers: &[KeyedAccount], | ||||||
|     ) -> Result<(), InstructionError> { |     ) -> Result<(), InstructionError> { | ||||||
|         if self.signer_key().is_none() { |  | ||||||
|             return Err(InstructionError::MissingRequiredSignature); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if let StakeState::Stake(mut stake) = self.state()? { |         if let StakeState::Stake(mut stake) = self.state()? { | ||||||
|  |             stake.check_authorized(self.signer_key(), other_signers)?; | ||||||
|             stake.deactivate(clock.epoch); |             stake.deactivate(clock.epoch); | ||||||
|  |  | ||||||
|             self.set_state(&StakeState::Stake(stake)) |             self.set_state(&StakeState::Stake(stake)) | ||||||
| @@ -475,11 +563,8 @@ impl<'a> StakeAccount for KeyedAccount<'a> { | |||||||
|         to: &mut KeyedAccount, |         to: &mut KeyedAccount, | ||||||
|         clock: &sysvar::clock::Clock, |         clock: &sysvar::clock::Clock, | ||||||
|         stake_history: &sysvar::stake_history::StakeHistory, |         stake_history: &sysvar::stake_history::StakeHistory, | ||||||
|  |         other_signers: &[KeyedAccount], | ||||||
|     ) -> Result<(), InstructionError> { |     ) -> Result<(), InstructionError> { | ||||||
|         if self.signer_key().is_none() { |  | ||||||
|             return Err(InstructionError::MissingRequiredSignature); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         fn transfer( |         fn transfer( | ||||||
|             from: &mut Account, |             from: &mut Account, | ||||||
|             to: &mut Account, |             to: &mut Account, | ||||||
| @@ -495,6 +580,7 @@ impl<'a> StakeAccount for KeyedAccount<'a> { | |||||||
|  |  | ||||||
|         match self.state()? { |         match self.state()? { | ||||||
|             StakeState::Stake(stake) => { |             StakeState::Stake(stake) => { | ||||||
|  |                 stake.check_authorized(self.signer_key(), other_signers)?; | ||||||
|                 // if we have a deactivation epoch and we're in cooldown |                 // if we have a deactivation epoch and we're in cooldown | ||||||
|                 let staked = if clock.epoch >= stake.deactivation_epoch { |                 let staked = if clock.epoch >= stake.deactivation_epoch { | ||||||
|                     stake.stake(clock.epoch, Some(stake_history)) |                     stake.stake(clock.epoch, Some(stake_history)) | ||||||
| @@ -510,11 +596,16 @@ impl<'a> StakeAccount for KeyedAccount<'a> { | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             StakeState::Lockup(lockup) => { |             StakeState::Lockup(lockup) => { | ||||||
|                 if lockup > clock.slot { |                 lockup.check_authorized(self.signer_key(), other_signers)?; | ||||||
|  |                 if lockup.slot > clock.slot { | ||||||
|                     return Err(InstructionError::InsufficientFunds); |                     return Err(InstructionError::InsufficientFunds); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             StakeState::Uninitialized => {} |             StakeState::Uninitialized => { | ||||||
|  |                 if self.signer_key().is_none() { | ||||||
|  |                     return Err(InstructionError::MissingRequiredSignature); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|             _ => return Err(InstructionError::InvalidAccountData), |             _ => return Err(InstructionError::InvalidAccountData), | ||||||
|         } |         } | ||||||
|         transfer(&mut self.account, &mut to.account, lamports) |         transfer(&mut self.account, &mut to.account, lamports) | ||||||
| @@ -623,7 +714,10 @@ mod tests { | |||||||
|         let stake_lamports = 42; |         let stake_lamports = 42; | ||||||
|         let mut stake_account = Account::new_data_with_space( |         let mut stake_account = Account::new_data_with_space( | ||||||
|             stake_lamports, |             stake_lamports, | ||||||
|             &StakeState::Lockup(0), |             &StakeState::Lockup(Lockup { | ||||||
|  |                 slot: 0, | ||||||
|  |                 authorized_pubkey: stake_pubkey, | ||||||
|  |             }), | ||||||
|             std::mem::size_of::<StakeState>(), |             std::mem::size_of::<StakeState>(), | ||||||
|             &id(), |             &id(), | ||||||
|         ) |         ) | ||||||
| @@ -634,18 +728,29 @@ mod tests { | |||||||
|  |  | ||||||
|         { |         { | ||||||
|             let stake_state: StakeState = stake_keyed_account.state().unwrap(); |             let stake_state: StakeState = stake_keyed_account.state().unwrap(); | ||||||
|             assert_eq!(stake_state, StakeState::Lockup(0)); |             assert_eq!( | ||||||
|  |                 stake_state, | ||||||
|  |                 StakeState::Lockup(Lockup { | ||||||
|  |                     slot: 0, | ||||||
|  |                     authorized_pubkey: stake_pubkey | ||||||
|  |                 }) | ||||||
|  |             ); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         assert_eq!( |         assert_eq!( | ||||||
|             stake_keyed_account.delegate_stake(&vote_keyed_account, &clock, &Config::default()), |             stake_keyed_account.delegate_stake( | ||||||
|  |                 &vote_keyed_account, | ||||||
|  |                 &clock, | ||||||
|  |                 &Config::default(), | ||||||
|  |                 &[] | ||||||
|  |             ), | ||||||
|             Err(InstructionError::MissingRequiredSignature) |             Err(InstructionError::MissingRequiredSignature) | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|         // signed keyed account |         // signed keyed account | ||||||
|         let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account); |         let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account); | ||||||
|         assert!(stake_keyed_account |         assert!(stake_keyed_account | ||||||
|             .delegate_stake(&vote_keyed_account, &clock, &Config::default()) |             .delegate_stake(&vote_keyed_account, &clock, &Config::default(), &[]) | ||||||
|             .is_ok()); |             .is_ok()); | ||||||
|  |  | ||||||
|         // verify that delegate_stake() looks right, compare against hand-rolled |         // verify that delegate_stake() looks right, compare against hand-rolled | ||||||
| @@ -653,6 +758,7 @@ mod tests { | |||||||
|         assert_eq!( |         assert_eq!( | ||||||
|             stake, |             stake, | ||||||
|             Stake { |             Stake { | ||||||
|  |                 authorized_pubkey: stake_pubkey, | ||||||
|                 voter_pubkey: vote_pubkey, |                 voter_pubkey: vote_pubkey, | ||||||
|                 voter_pubkey_epoch: clock.epoch, |                 voter_pubkey_epoch: clock.epoch, | ||||||
|                 credits_observed: vote_state.credits(), |                 credits_observed: vote_state.credits(), | ||||||
| @@ -670,7 +776,7 @@ mod tests { | |||||||
|  |  | ||||||
|         // verify that delegate_stake can be called twice, 2nd is redelegate |         // verify that delegate_stake can be called twice, 2nd is redelegate | ||||||
|         assert!(stake_keyed_account |         assert!(stake_keyed_account | ||||||
|             .delegate_stake(&vote_keyed_account, &clock, &Config::default()) |             .delegate_stake(&vote_keyed_account, &clock, &Config::default(), &[]) | ||||||
|             .is_ok()); |             .is_ok()); | ||||||
|  |  | ||||||
|         // verify that non-stakes fail delegate_stake() |         // verify that non-stakes fail delegate_stake() | ||||||
| @@ -678,7 +784,7 @@ mod tests { | |||||||
|  |  | ||||||
|         stake_keyed_account.set_state(&stake_state).unwrap(); |         stake_keyed_account.set_state(&stake_state).unwrap(); | ||||||
|         assert!(stake_keyed_account |         assert!(stake_keyed_account | ||||||
|             .delegate_stake(&vote_keyed_account, &clock, &Config::default()) |             .delegate_stake(&vote_keyed_account, &clock, &Config::default(), &[]) | ||||||
|             .is_err()); |             .is_err()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -980,7 +1086,10 @@ mod tests { | |||||||
|         // first time works, as is uninit |         // first time works, as is uninit | ||||||
|         assert_eq!( |         assert_eq!( | ||||||
|             StakeState::from(&stake_keyed_account.account).unwrap(), |             StakeState::from(&stake_keyed_account.account).unwrap(), | ||||||
|             StakeState::Lockup(1) |             StakeState::Lockup(Lockup { | ||||||
|  |                 slot: 1, | ||||||
|  |                 authorized_pubkey: stake_pubkey | ||||||
|  |             }) | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|         // 2nd time fails, can't move it from anything other than uninit->lockup |         // 2nd time fails, can't move it from anything other than uninit->lockup | ||||||
| @@ -996,7 +1105,10 @@ mod tests { | |||||||
|         let stake_lamports = 42; |         let stake_lamports = 42; | ||||||
|         let mut stake_account = Account::new_data_with_space( |         let mut stake_account = Account::new_data_with_space( | ||||||
|             stake_lamports, |             stake_lamports, | ||||||
|             &StakeState::Lockup(0), |             &StakeState::Lockup(Lockup { | ||||||
|  |                 slot: 0, | ||||||
|  |                 authorized_pubkey: stake_pubkey, | ||||||
|  |             }), | ||||||
|             std::mem::size_of::<StakeState>(), |             std::mem::size_of::<StakeState>(), | ||||||
|             &id(), |             &id(), | ||||||
|         ) |         ) | ||||||
| @@ -1012,17 +1124,10 @@ mod tests { | |||||||
|             vote_state::create_account(&vote_pubkey, &Pubkey::new_rand(), 0, 100); |             vote_state::create_account(&vote_pubkey, &Pubkey::new_rand(), 0, 100); | ||||||
|         let vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &mut vote_account); |         let vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &mut vote_account); | ||||||
|  |  | ||||||
|         // unsigned keyed account |  | ||||||
|         let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &mut stake_account); |  | ||||||
|         assert_eq!( |  | ||||||
|             stake_keyed_account.deactivate_stake(&vote_keyed_account, &clock), |  | ||||||
|             Err(InstructionError::MissingRequiredSignature) |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         // signed keyed account but not staked yet |         // signed keyed account but not staked yet | ||||||
|         let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account); |         let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account); | ||||||
|         assert_eq!( |         assert_eq!( | ||||||
|             stake_keyed_account.deactivate_stake(&vote_keyed_account, &clock), |             stake_keyed_account.deactivate_stake(&vote_keyed_account, &clock, &[]), | ||||||
|             Err(InstructionError::InvalidAccountData) |             Err(InstructionError::InvalidAccountData) | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
| @@ -1033,13 +1138,26 @@ mod tests { | |||||||
|         let mut vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &mut vote_account); |         let mut vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &mut vote_account); | ||||||
|         vote_keyed_account.set_state(&VoteState::default()).unwrap(); |         vote_keyed_account.set_state(&VoteState::default()).unwrap(); | ||||||
|         assert_eq!( |         assert_eq!( | ||||||
|             stake_keyed_account.delegate_stake(&vote_keyed_account, &clock, &Config::default()), |             stake_keyed_account.delegate_stake( | ||||||
|  |                 &vote_keyed_account, | ||||||
|  |                 &clock, | ||||||
|  |                 &Config::default(), | ||||||
|  |                 &[] | ||||||
|  |             ), | ||||||
|             Ok(()) |             Ok(()) | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|         // Deactivate after staking |         // unsigned keyed account | ||||||
|  |         let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &mut stake_account); | ||||||
|         assert_eq!( |         assert_eq!( | ||||||
|             stake_keyed_account.deactivate_stake(&vote_keyed_account, &clock), |             stake_keyed_account.deactivate_stake(&vote_keyed_account, &clock, &[]), | ||||||
|  |             Err(InstructionError::MissingRequiredSignature) | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         // Deactivate after staking | ||||||
|  |         let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account); | ||||||
|  |         assert_eq!( | ||||||
|  |             stake_keyed_account.deactivate_stake(&vote_keyed_account, &clock, &[]), | ||||||
|             Ok(()) |             Ok(()) | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| @@ -1050,7 +1168,7 @@ mod tests { | |||||||
|         let stake_lamports = 42; |         let stake_lamports = 42; | ||||||
|         let mut stake_account = Account::new_data_with_space( |         let mut stake_account = Account::new_data_with_space( | ||||||
|             stake_lamports, |             stake_lamports, | ||||||
|             &StakeState::Lockup(0), |             &StakeState::Uninitialized, | ||||||
|             std::mem::size_of::<StakeState>(), |             std::mem::size_of::<StakeState>(), | ||||||
|             &id(), |             &id(), | ||||||
|         ) |         ) | ||||||
| @@ -1069,7 +1187,8 @@ mod tests { | |||||||
|                 stake_lamports, |                 stake_lamports, | ||||||
|                 &mut to_keyed_account, |                 &mut to_keyed_account, | ||||||
|                 &clock, |                 &clock, | ||||||
|                 &StakeHistory::default() |                 &StakeHistory::default(), | ||||||
|  |                 &[], | ||||||
|             ), |             ), | ||||||
|             Err(InstructionError::MissingRequiredSignature) |             Err(InstructionError::MissingRequiredSignature) | ||||||
|         ); |         ); | ||||||
| @@ -1081,7 +1200,8 @@ mod tests { | |||||||
|                 stake_lamports, |                 stake_lamports, | ||||||
|                 &mut to_keyed_account, |                 &mut to_keyed_account, | ||||||
|                 &clock, |                 &clock, | ||||||
|                 &StakeHistory::default() |                 &StakeHistory::default(), | ||||||
|  |                 &[], | ||||||
|             ), |             ), | ||||||
|             Ok(()) |             Ok(()) | ||||||
|         ); |         ); | ||||||
| @@ -1090,14 +1210,19 @@ mod tests { | |||||||
|         // reset balance |         // reset balance | ||||||
|         stake_account.lamports = stake_lamports; |         stake_account.lamports = stake_lamports; | ||||||
|  |  | ||||||
|         // signed keyed account and uninitialized, more than available should fail |         // lockup | ||||||
|  |         let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account); | ||||||
|  |         stake_keyed_account.lockup(0).unwrap(); | ||||||
|  |  | ||||||
|  |         // signed keyed account and locked up, more than available should fail | ||||||
|         let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account); |         let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account); | ||||||
|         assert_eq!( |         assert_eq!( | ||||||
|             stake_keyed_account.withdraw( |             stake_keyed_account.withdraw( | ||||||
|                 stake_lamports + 1, |                 stake_lamports + 1, | ||||||
|                 &mut to_keyed_account, |                 &mut to_keyed_account, | ||||||
|                 &clock, |                 &clock, | ||||||
|                 &StakeHistory::default() |                 &StakeHistory::default(), | ||||||
|  |                 &[], | ||||||
|             ), |             ), | ||||||
|             Err(InstructionError::InsufficientFunds) |             Err(InstructionError::InsufficientFunds) | ||||||
|         ); |         ); | ||||||
| @@ -1109,7 +1234,12 @@ mod tests { | |||||||
|         let mut vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &mut vote_account); |         let mut vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &mut vote_account); | ||||||
|         vote_keyed_account.set_state(&VoteState::default()).unwrap(); |         vote_keyed_account.set_state(&VoteState::default()).unwrap(); | ||||||
|         assert_eq!( |         assert_eq!( | ||||||
|             stake_keyed_account.delegate_stake(&vote_keyed_account, &clock, &Config::default()), |             stake_keyed_account.delegate_stake( | ||||||
|  |                 &vote_keyed_account, | ||||||
|  |                 &clock, | ||||||
|  |                 &Config::default(), | ||||||
|  |                 &[] | ||||||
|  |             ), | ||||||
|             Ok(()) |             Ok(()) | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
| @@ -1122,7 +1252,8 @@ mod tests { | |||||||
|                 10, |                 10, | ||||||
|                 &mut to_keyed_account, |                 &mut to_keyed_account, | ||||||
|                 &clock, |                 &clock, | ||||||
|                 &StakeHistory::default() |                 &StakeHistory::default(), | ||||||
|  |                 &[], | ||||||
|             ), |             ), | ||||||
|             Ok(()) |             Ok(()) | ||||||
|         ); |         ); | ||||||
| @@ -1136,14 +1267,15 @@ mod tests { | |||||||
|                 10 + 1, |                 10 + 1, | ||||||
|                 &mut to_keyed_account, |                 &mut to_keyed_account, | ||||||
|                 &clock, |                 &clock, | ||||||
|                 &StakeHistory::default() |                 &StakeHistory::default(), | ||||||
|  |                 &[], | ||||||
|             ), |             ), | ||||||
|             Err(InstructionError::InsufficientFunds) |             Err(InstructionError::InsufficientFunds) | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|         // deactivate the stake before withdrawal |         // deactivate the stake before withdrawal | ||||||
|         assert_eq!( |         assert_eq!( | ||||||
|             stake_keyed_account.deactivate_stake(&vote_keyed_account, &clock), |             stake_keyed_account.deactivate_stake(&vote_keyed_account, &clock, &[]), | ||||||
|             Ok(()) |             Ok(()) | ||||||
|         ); |         ); | ||||||
|         // simulate time passing |         // simulate time passing | ||||||
| @@ -1155,7 +1287,8 @@ mod tests { | |||||||
|                 stake_lamports + 10 + 1, |                 stake_lamports + 10 + 1, | ||||||
|                 &mut to_keyed_account, |                 &mut to_keyed_account, | ||||||
|                 &clock, |                 &clock, | ||||||
|                 &StakeHistory::default() |                 &StakeHistory::default(), | ||||||
|  |                 &[], | ||||||
|             ), |             ), | ||||||
|             Err(InstructionError::InsufficientFunds) |             Err(InstructionError::InsufficientFunds) | ||||||
|         ); |         ); | ||||||
| @@ -1166,7 +1299,8 @@ mod tests { | |||||||
|                 stake_lamports + 10, |                 stake_lamports + 10, | ||||||
|                 &mut to_keyed_account, |                 &mut to_keyed_account, | ||||||
|                 &clock, |                 &clock, | ||||||
|                 &StakeHistory::default() |                 &StakeHistory::default(), | ||||||
|  |                 &[], | ||||||
|             ), |             ), | ||||||
|             Ok(()) |             Ok(()) | ||||||
|         ); |         ); | ||||||
| @@ -1180,7 +1314,10 @@ mod tests { | |||||||
|         let stake_lamports = 42; |         let stake_lamports = 42; | ||||||
|         let mut stake_account = Account::new_data_with_space( |         let mut stake_account = Account::new_data_with_space( | ||||||
|             total_lamports, |             total_lamports, | ||||||
|             &StakeState::Lockup(0), |             &StakeState::Lockup(Lockup { | ||||||
|  |                 slot: 0, | ||||||
|  |                 authorized_pubkey: stake_pubkey, | ||||||
|  |             }), | ||||||
|             std::mem::size_of::<StakeState>(), |             std::mem::size_of::<StakeState>(), | ||||||
|             &id(), |             &id(), | ||||||
|         ) |         ) | ||||||
| @@ -1203,7 +1340,12 @@ mod tests { | |||||||
|         let mut vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &mut vote_account); |         let mut vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &mut vote_account); | ||||||
|         vote_keyed_account.set_state(&VoteState::default()).unwrap(); |         vote_keyed_account.set_state(&VoteState::default()).unwrap(); | ||||||
|         assert_eq!( |         assert_eq!( | ||||||
|             stake_keyed_account.delegate_stake(&vote_keyed_account, &future, &Config::default()), |             stake_keyed_account.delegate_stake( | ||||||
|  |                 &vote_keyed_account, | ||||||
|  |                 &future, | ||||||
|  |                 &Config::default(), | ||||||
|  |                 &[] | ||||||
|  |             ), | ||||||
|             Ok(()) |             Ok(()) | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
| @@ -1219,7 +1361,8 @@ mod tests { | |||||||
|                 total_lamports - stake_lamports + 1, |                 total_lamports - stake_lamports + 1, | ||||||
|                 &mut to_keyed_account, |                 &mut to_keyed_account, | ||||||
|                 &clock, |                 &clock, | ||||||
|                 &stake_history |                 &stake_history, | ||||||
|  |                 &[], | ||||||
|             ), |             ), | ||||||
|             Err(InstructionError::InsufficientFunds) |             Err(InstructionError::InsufficientFunds) | ||||||
|         ); |         ); | ||||||
| @@ -1247,7 +1390,8 @@ mod tests { | |||||||
|                 total_lamports, |                 total_lamports, | ||||||
|                 &mut to_keyed_account, |                 &mut to_keyed_account, | ||||||
|                 &sysvar::clock::Clock::default(), |                 &sysvar::clock::Clock::default(), | ||||||
|                 &StakeHistory::default() |                 &StakeHistory::default(), | ||||||
|  |                 &[], | ||||||
|             ), |             ), | ||||||
|             Err(InstructionError::InvalidAccountData) |             Err(InstructionError::InvalidAccountData) | ||||||
|         ); |         ); | ||||||
| @@ -1259,7 +1403,10 @@ mod tests { | |||||||
|         let total_lamports = 100; |         let total_lamports = 100; | ||||||
|         let mut stake_account = Account::new_data_with_space( |         let mut stake_account = Account::new_data_with_space( | ||||||
|             total_lamports, |             total_lamports, | ||||||
|             &StakeState::Lockup(1), |             &StakeState::Lockup(Lockup { | ||||||
|  |                 slot: 1, | ||||||
|  |                 authorized_pubkey: stake_pubkey, | ||||||
|  |             }), | ||||||
|             std::mem::size_of::<StakeState>(), |             std::mem::size_of::<StakeState>(), | ||||||
|             &id(), |             &id(), | ||||||
|         ) |         ) | ||||||
| @@ -1277,7 +1424,8 @@ mod tests { | |||||||
|                 total_lamports, |                 total_lamports, | ||||||
|                 &mut to_keyed_account, |                 &mut to_keyed_account, | ||||||
|                 &clock, |                 &clock, | ||||||
|                 &StakeHistory::default() |                 &StakeHistory::default(), | ||||||
|  |                 &[], | ||||||
|             ), |             ), | ||||||
|             Err(InstructionError::InsufficientFunds) |             Err(InstructionError::InsufficientFunds) | ||||||
|         ); |         ); | ||||||
| @@ -1288,7 +1436,8 @@ mod tests { | |||||||
|                 total_lamports, |                 total_lamports, | ||||||
|                 &mut to_keyed_account, |                 &mut to_keyed_account, | ||||||
|                 &clock, |                 &clock, | ||||||
|                 &StakeHistory::default() |                 &StakeHistory::default(), | ||||||
|  |                 &[], | ||||||
|             ), |             ), | ||||||
|             Ok(()) |             Ok(()) | ||||||
|         ); |         ); | ||||||
| @@ -1385,17 +1534,20 @@ mod tests { | |||||||
|         let mut rewards_pool_keyed_account = |         let mut rewards_pool_keyed_account = | ||||||
|             KeyedAccount::new(&rewards_pool_pubkey, false, &mut rewards_pool_account); |             KeyedAccount::new(&rewards_pool_pubkey, false, &mut rewards_pool_account); | ||||||
|  |  | ||||||
|         let pubkey = Pubkey::default(); |         let stake_pubkey = Pubkey::default(); | ||||||
|         let stake_lamports = 100; |         let stake_lamports = 100; | ||||||
|         let mut stake_account = Account::new_data_with_space( |         let mut stake_account = Account::new_data_with_space( | ||||||
|             stake_lamports, |             stake_lamports, | ||||||
|             &StakeState::Lockup(0), |             &StakeState::Lockup(Lockup { | ||||||
|  |                 slot: 0, | ||||||
|  |                 authorized_pubkey: stake_pubkey, | ||||||
|  |             }), | ||||||
|             std::mem::size_of::<StakeState>(), |             std::mem::size_of::<StakeState>(), | ||||||
|             &id(), |             &id(), | ||||||
|         ) |         ) | ||||||
|         .expect("stake_account"); |         .expect("stake_account"); | ||||||
|  |  | ||||||
|         let mut stake_keyed_account = KeyedAccount::new(&pubkey, true, &mut stake_account); |         let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account); | ||||||
|  |  | ||||||
|         let vote_pubkey = Pubkey::new_rand(); |         let vote_pubkey = Pubkey::new_rand(); | ||||||
|         let mut vote_account = |         let mut vote_account = | ||||||
| @@ -1415,7 +1567,7 @@ mod tests { | |||||||
|  |  | ||||||
|         // delegate the stake |         // delegate the stake | ||||||
|         assert!(stake_keyed_account |         assert!(stake_keyed_account | ||||||
|             .delegate_stake(&vote_keyed_account, &clock, &Config::default()) |             .delegate_stake(&vote_keyed_account, &clock, &Config::default(), &[]) | ||||||
|             .is_ok()); |             .is_ok()); | ||||||
|  |  | ||||||
|         let stake_history = create_stake_history_from_stakes( |         let stake_history = create_stake_history_from_stakes( | ||||||
| @@ -1499,4 +1651,160 @@ mod tests { | |||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_authorize_lockup() { | ||||||
|  |         let stake_pubkey = Pubkey::new_rand(); | ||||||
|  |         let stake_lamports = 42; | ||||||
|  |         let mut stake_account = Account::new_data_with_space( | ||||||
|  |             stake_lamports, | ||||||
|  |             &StakeState::Lockup(Lockup { | ||||||
|  |                 slot: 0, | ||||||
|  |                 authorized_pubkey: stake_pubkey, | ||||||
|  |             }), | ||||||
|  |             std::mem::size_of::<StakeState>(), | ||||||
|  |             &id(), | ||||||
|  |         ) | ||||||
|  |         .expect("stake_account"); | ||||||
|  |  | ||||||
|  |         let to = Pubkey::new_rand(); | ||||||
|  |         let mut to_account = Account::new(1, 0, &system_program::id()); | ||||||
|  |         let mut to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); | ||||||
|  |  | ||||||
|  |         let clock = sysvar::clock::Clock::default(); | ||||||
|  |         let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account); | ||||||
|  |  | ||||||
|  |         let stake_pubkey0 = Pubkey::new_rand(); | ||||||
|  |         assert_eq!(stake_keyed_account.authorize(&stake_pubkey0, &[]), Ok(())); | ||||||
|  |         if let StakeState::Lockup(lockup) = StakeState::from(&stake_keyed_account.account).unwrap() | ||||||
|  |         { | ||||||
|  |             assert_eq!(lockup.authorized_pubkey, stake_pubkey0); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // 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, &[]), | ||||||
|  |             Err(InstructionError::MissingRequiredSignature) | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         let mut staker_account0 = Account::new(1, 0, &system_program::id()); | ||||||
|  |         let staker_keyed_account0 = KeyedAccount::new(&stake_pubkey0, true, &mut staker_account0); | ||||||
|  |  | ||||||
|  |         // Test a second authorization by the newly authorized pubkey | ||||||
|  |         let stake_pubkey2 = Pubkey::new_rand(); | ||||||
|  |         assert_eq!( | ||||||
|  |             stake_keyed_account.authorize(&stake_pubkey2, &[staker_keyed_account0]), | ||||||
|  |             Ok(()) | ||||||
|  |         ); | ||||||
|  |         if let StakeState::Lockup(lockup) = StakeState::from(&stake_keyed_account.account).unwrap() | ||||||
|  |         { | ||||||
|  |             assert_eq!(lockup.authorized_pubkey, stake_pubkey2); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         let mut staker_account2 = Account::new(1, 0, &system_program::id()); | ||||||
|  |         let staker_keyed_account2 = KeyedAccount::new(&stake_pubkey2, true, &mut staker_account2); | ||||||
|  |  | ||||||
|  |         // Test an action by the currently authorized pubkey | ||||||
|  |         assert_eq!( | ||||||
|  |             stake_keyed_account.withdraw( | ||||||
|  |                 stake_lamports, | ||||||
|  |                 &mut to_keyed_account, | ||||||
|  |                 &clock, | ||||||
|  |                 &StakeHistory::default(), | ||||||
|  |                 &[staker_keyed_account2], | ||||||
|  |             ), | ||||||
|  |             Ok(()) | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_authorize_delegated_stake() { | ||||||
|  |         let stake_pubkey = Pubkey::new_rand(); | ||||||
|  |         let stake_lamports = 42; | ||||||
|  |         let mut stake_account = Account::new_data_with_space( | ||||||
|  |             stake_lamports, | ||||||
|  |             &StakeState::Lockup(Lockup { | ||||||
|  |                 slot: 0, | ||||||
|  |                 authorized_pubkey: stake_pubkey, | ||||||
|  |             }), | ||||||
|  |             std::mem::size_of::<StakeState>(), | ||||||
|  |             &id(), | ||||||
|  |         ) | ||||||
|  |         .expect("stake_account"); | ||||||
|  |  | ||||||
|  |         let clock = sysvar::clock::Clock::default(); | ||||||
|  |  | ||||||
|  |         let vote_pubkey = Pubkey::new_rand(); | ||||||
|  |         let mut vote_account = | ||||||
|  |             vote_state::create_account(&vote_pubkey, &Pubkey::new_rand(), 0, 100); | ||||||
|  |         let vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &mut vote_account); | ||||||
|  |  | ||||||
|  |         let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account); | ||||||
|  |         stake_keyed_account | ||||||
|  |             .delegate_stake(&vote_keyed_account, &clock, &Config::default(), &[]) | ||||||
|  |             .unwrap(); | ||||||
|  |  | ||||||
|  |         let new_staker_pubkey = Pubkey::new_rand(); | ||||||
|  |         assert_eq!( | ||||||
|  |             stake_keyed_account.authorize(&new_staker_pubkey, &[]), | ||||||
|  |             Ok(()) | ||||||
|  |         ); | ||||||
|  |         let stake = StakeState::stake_from(&stake_keyed_account.account).unwrap(); | ||||||
|  |         assert_eq!(stake.authorized_pubkey, new_staker_pubkey); | ||||||
|  |  | ||||||
|  |         let other_pubkey = Pubkey::new_rand(); | ||||||
|  |         let mut other_account = Account::new(1, 0, &system_program::id()); | ||||||
|  |         let other_keyed_account = KeyedAccount::new(&other_pubkey, true, &mut other_account); | ||||||
|  |  | ||||||
|  |         // Use unsigned stake_keyed_account to test other signers | ||||||
|  |         let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &mut stake_account); | ||||||
|  |  | ||||||
|  |         let new_voter_pubkey = Pubkey::new_rand(); | ||||||
|  |         let vote_state = VoteState::default(); | ||||||
|  |         let mut new_vote_account = | ||||||
|  |             vote_state::create_account(&new_voter_pubkey, &Pubkey::new_rand(), 0, 100); | ||||||
|  |         let mut new_vote_keyed_account = | ||||||
|  |             KeyedAccount::new(&new_voter_pubkey, false, &mut new_vote_account); | ||||||
|  |         new_vote_keyed_account.set_state(&vote_state).unwrap(); | ||||||
|  |  | ||||||
|  |         // Random other account should fail | ||||||
|  |         assert_eq!( | ||||||
|  |             stake_keyed_account.delegate_stake( | ||||||
|  |                 &new_vote_keyed_account, | ||||||
|  |                 &clock, | ||||||
|  |                 &Config::default(), | ||||||
|  |                 &[other_keyed_account] | ||||||
|  |             ), | ||||||
|  |             Err(InstructionError::MissingRequiredSignature) | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         let mut new_staker_account = Account::new(1, 0, &system_program::id()); | ||||||
|  |         let new_staker_keyed_account = | ||||||
|  |             KeyedAccount::new(&new_staker_pubkey, true, &mut new_staker_account); | ||||||
|  |  | ||||||
|  |         // Authorized staker should succeed | ||||||
|  |         assert_eq!( | ||||||
|  |             stake_keyed_account.delegate_stake( | ||||||
|  |                 &new_vote_keyed_account, | ||||||
|  |                 &clock, | ||||||
|  |                 &Config::default(), | ||||||
|  |                 &[new_staker_keyed_account] | ||||||
|  |             ), | ||||||
|  |             Ok(()) | ||||||
|  |         ); | ||||||
|  |         let stake = StakeState::stake_from(&stake_keyed_account.account).unwrap(); | ||||||
|  |         assert_eq!(stake.voter_pubkey(0), &new_voter_pubkey); | ||||||
|  |  | ||||||
|  |         // Test another staking action | ||||||
|  |         let new_staker_keyed_account = | ||||||
|  |             KeyedAccount::new(&new_staker_pubkey, true, &mut new_staker_account); | ||||||
|  |         assert_eq!( | ||||||
|  |             stake_keyed_account.deactivate_stake( | ||||||
|  |                 &vote_keyed_account, | ||||||
|  |                 &clock, | ||||||
|  |                 &[new_staker_keyed_account] | ||||||
|  |             ), | ||||||
|  |             Ok(()) | ||||||
|  |         ); | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user