diff --git a/programs/stake_api/src/stake_instruction.rs b/programs/stake_api/src/stake_instruction.rs index f4fe9f69ff..e0cab0f947 100644 --- a/programs/stake_api/src/stake_instruction.rs +++ b/programs/stake_api/src/stake_instruction.rs @@ -7,9 +7,9 @@ use log::*; use num_derive::{FromPrimitive, ToPrimitive}; use serde_derive::{Deserialize, Serialize}; use solana_sdk::{ - account::KeyedAccount, + account::{get_signers, KeyedAccount}, instruction::{AccountMeta, Instruction, InstructionError}, - instruction_processor_utils::DecodeError, + instruction_processor_utils::{next_keyed_account, DecodeError}, pubkey::Pubkey, system_instruction, sysvar, sysvar::rent, @@ -164,28 +164,24 @@ pub fn create_stake_account_and_delegate_stake( instructions } -// for instructions that whose authorized signer may differ from the account's pubkey -fn metas_for_authorized_signer( - account_pubkey: &Pubkey, - authorized_signer: &Pubkey, // currently authorized - other_params: &[AccountMeta], +// for instructions whose authorized signer may already be in account parameters +fn metas_with_signer( + metas: &[AccountMeta], // parameter metas, in order + signer: &Pubkey, // might already appear in parameters ) -> Vec { - let is_own_signer = authorized_signer == account_pubkey; + let mut metas = metas.to_vec(); - // vote account - let mut account_metas = vec![AccountMeta::new(*account_pubkey, is_own_signer)]; - - for meta in other_params { - account_metas.push(meta.clone()); + for meta in metas.iter_mut() { + if &meta.pubkey == signer { + meta.is_signer = true; // found it, we're done + return metas; + } } - // append signer at the end - if !is_own_signer { - account_metas.push(AccountMeta::new_credit_only(*authorized_signer, true)) - // signer - } + // signer wasn't in metas, append it after normal parameters + metas.push(AccountMeta::new_credit_only(*signer, true)); - account_metas + metas } pub fn authorize( @@ -194,7 +190,8 @@ pub fn authorize( new_authorized_pubkey: &Pubkey, stake_authorize: StakeAuthorize, ) -> Instruction { - let account_metas = metas_for_authorized_signer(stake_pubkey, authorized_pubkey, &[]); + let account_metas = + metas_with_signer(&[AccountMeta::new(*stake_pubkey, false)], authorized_pubkey); Instruction::new( id(), @@ -219,14 +216,14 @@ pub fn delegate_stake( authorized_pubkey: &Pubkey, vote_pubkey: &Pubkey, ) -> Instruction { - let account_metas = metas_for_authorized_signer( - stake_pubkey, - authorized_pubkey, + let account_metas = metas_with_signer( &[ + AccountMeta::new(*stake_pubkey, false), AccountMeta::new_credit_only(*vote_pubkey, false), AccountMeta::new_credit_only(sysvar::clock::id(), false), AccountMeta::new_credit_only(crate::config::id(), false), ], + authorized_pubkey, ); Instruction::new(id(), &StakeInstruction::DelegateStake, account_metas) } @@ -237,22 +234,25 @@ pub fn withdraw( to_pubkey: &Pubkey, lamports: u64, ) -> Instruction { - let mut accounts = vec![ - AccountMeta::new_credit_only(sysvar::clock::id(), false), - AccountMeta::new_credit_only(sysvar::stake_history::id(), false), - ]; - if to_pubkey != authorized_pubkey { - accounts.push(AccountMeta::new_credit_only(*to_pubkey, false)); - } - let account_metas = metas_for_authorized_signer(stake_pubkey, authorized_pubkey, &accounts); + let account_metas = metas_with_signer( + &[ + AccountMeta::new(*stake_pubkey, false), + AccountMeta::new_credit_only(*to_pubkey, false), + AccountMeta::new_credit_only(sysvar::clock::id(), false), + AccountMeta::new_credit_only(sysvar::stake_history::id(), false), + ], + authorized_pubkey, + ); Instruction::new(id(), &StakeInstruction::Withdraw(lamports), account_metas) } pub fn deactivate_stake(stake_pubkey: &Pubkey, authorized_pubkey: &Pubkey) -> Instruction { - let account_metas = metas_for_authorized_signer( - stake_pubkey, + let account_metas = metas_with_signer( + &[ + AccountMeta::new(*stake_pubkey, false), + AccountMeta::new_credit_only(sysvar::clock::id(), false), + ], authorized_pubkey, - &[AccountMeta::new_credit_only(sysvar::clock::id(), false)], ); Instruction::new(id(), &StakeInstruction::Deactivate, account_metas) } @@ -267,73 +267,55 @@ pub fn process_instruction( trace!("process_instruction: {:?}", data); trace!("keyed_accounts: {:?}", keyed_accounts); - if keyed_accounts.is_empty() { - return Err(InstructionError::InvalidInstructionData); - } + let signers = get_signers(keyed_accounts); - let (me, rest) = &mut keyed_accounts.split_at_mut(1); - let me = &mut me[0]; + let keyed_accounts = &mut keyed_accounts.iter_mut(); + let me = &mut next_keyed_account(keyed_accounts)?; // TODO: data-driven unpack and dispatch of KeyedAccounts match deserialize(data).map_err(|_| InstructionError::InvalidInstructionData)? { StakeInstruction::Initialize(authorized, lockup) => { - if rest.is_empty() { - return Err(InstructionError::InvalidInstructionData); - } - rent::verify_rent_exemption(me, &rest[0])?; + rent::verify_rent_exemption(me, next_keyed_account(keyed_accounts)?)?; me.initialize(&authorized, &lockup) } StakeInstruction::Authorize(authorized_pubkey, stake_authorize) => { - me.authorize(&authorized_pubkey, stake_authorize, &rest) + me.authorize(&authorized_pubkey, stake_authorize, &signers) } StakeInstruction::DelegateStake => { - if rest.len() < 3 { - return Err(InstructionError::InvalidInstructionData); - } - let vote = &rest[0]; + let vote = next_keyed_account(keyed_accounts)?; me.delegate_stake( - vote, - &sysvar::clock::from_keyed_account(&rest[1])?, - &config::from_keyed_account(&rest[2])?, - &rest[3..], + &vote, + &sysvar::clock::from_keyed_account(next_keyed_account(keyed_accounts)?)?, + &config::from_keyed_account(next_keyed_account(keyed_accounts)?)?, + &signers, ) } StakeInstruction::RedeemVoteCredits => { - if rest.len() != 4 { - return Err(InstructionError::InvalidInstructionData); - } - let (vote, rest) = rest.split_at_mut(1); - let vote = &mut vote[0]; - let (rewards_pool, rest) = rest.split_at_mut(1); - let rewards_pool = &mut rewards_pool[0]; + let vote = &mut next_keyed_account(keyed_accounts)?; + let rewards_pool = &mut next_keyed_account(keyed_accounts)?; me.redeem_vote_credits( vote, rewards_pool, - &sysvar::rewards::from_keyed_account(&rest[0])?, - &sysvar::stake_history::from_keyed_account(&rest[1])?, + &sysvar::rewards::from_keyed_account(next_keyed_account(keyed_accounts)?)?, + &sysvar::stake_history::from_keyed_account(next_keyed_account(keyed_accounts)?)?, ) } StakeInstruction::Withdraw(lamports) => { - if rest.len() < 3 { - return Err(InstructionError::InvalidInstructionData); - } - + let to = &mut next_keyed_account(keyed_accounts)?; me.withdraw( lamports, - &sysvar::clock::from_keyed_account(&rest[0])?, - &sysvar::stake_history::from_keyed_account(&rest[1])?, - &mut rest[2..], + to, + &sysvar::clock::from_keyed_account(next_keyed_account(keyed_accounts)?)?, + &sysvar::stake_history::from_keyed_account(next_keyed_account(keyed_accounts)?)?, + &signers, ) } - StakeInstruction::Deactivate => { - if rest.is_empty() { - return Err(InstructionError::InvalidInstructionData); - } - - me.deactivate_stake(&sysvar::clock::from_keyed_account(&rest[0])?, &rest[1..]) - } + StakeInstruction::Deactivate => me.deactivate_stake( + &sysvar::clock::from_keyed_account(next_keyed_account(keyed_accounts)?)?, + &signers, + ), } } @@ -421,7 +403,7 @@ mod tests { )) .unwrap(), ), - Err(InstructionError::InvalidInstructionData), + Err(InstructionError::NotEnoughAccountKeys), ); // gets the first check in delegate, wrong number of accounts @@ -435,7 +417,7 @@ mod tests { )], &serialize(&StakeInstruction::DelegateStake).unwrap(), ), - Err(InstructionError::InvalidInstructionData), + Err(InstructionError::NotEnoughAccountKeys), ); // gets the sub-check for number of args @@ -449,7 +431,7 @@ mod tests { ),], &serialize(&StakeInstruction::DelegateStake).unwrap(), ), - Err(InstructionError::InvalidInstructionData), + Err(InstructionError::NotEnoughAccountKeys), ); // catches the number of args check @@ -462,7 +444,7 @@ mod tests { ], &serialize(&StakeInstruction::RedeemVoteCredits).unwrap(), ), - Err(InstructionError::InvalidInstructionData), + Err(InstructionError::NotEnoughAccountKeys), ); // catches the type of args check @@ -555,22 +537,14 @@ mod tests { assert_eq!( super::process_instruction( &Pubkey::default(), - &mut [ - KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()), - KeyedAccount::new( - &sysvar::clock::id(), - false, - &mut sysvar::rewards::create_account(1, 0.0, 0.0) - ), - KeyedAccount::new( - &sysvar::stake_history::id(), - false, - &mut sysvar::stake_history::create_account(1, &StakeHistory::default()) - ), - ], + &mut [KeyedAccount::new( + &Pubkey::default(), + false, + &mut Account::default() + )], &serialize(&StakeInstruction::Withdraw(42)).unwrap(), ), - Err(InstructionError::InvalidInstructionData), + Err(InstructionError::NotEnoughAccountKeys), ); // Tests 2nd keyed account is of correct type (Clock instead of rewards) in deactivate @@ -594,14 +568,10 @@ mod tests { assert_eq!( super::process_instruction( &Pubkey::default(), - &mut [KeyedAccount::new( - &sysvar::clock::id(), - false, - &mut sysvar::rewards::create_account(1, 0.0, 0.0) - ),], + &mut [], &serialize(&StakeInstruction::Deactivate).unwrap(), ), - Err(InstructionError::InvalidInstructionData), + Err(InstructionError::NotEnoughAccountKeys), ); } diff --git a/programs/stake_api/src/stake_state.rs b/programs/stake_api/src/stake_state.rs index 045080733c..d0b240a18b 100644 --- a/programs/stake_api/src/stake_state.rs +++ b/programs/stake_api/src/stake_state.rs @@ -17,6 +17,7 @@ use solana_sdk::{ }, }; use solana_vote_api::vote_state::VoteState; +use std::collections::HashSet; #[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy)] #[allow(clippy::large_enum_variant)] @@ -134,32 +135,23 @@ impl Authorized { } pub fn check( &self, - stake_signer: Option<&Pubkey>, - other_signers: &[KeyedAccount], + signers: &HashSet, stake_authorize: StakeAuthorize, ) -> Result<(), InstructionError> { - let authorized = match stake_authorize { - StakeAuthorize::Staker => Some(&self.staker), - StakeAuthorize::Withdrawer => Some(&self.withdrawer), - }; - if stake_signer != authorized - && other_signers - .iter() - .all(|account| account.signer_key() != authorized) - { - Err(InstructionError::MissingRequiredSignature) - } else { - Ok(()) + match stake_authorize { + StakeAuthorize::Staker if signers.contains(&self.staker) => Ok(()), + StakeAuthorize::Withdrawer if signers.contains(&self.withdrawer) => Ok(()), + _ => Err(InstructionError::MissingRequiredSignature), } } + pub fn authorize( &mut self, - stake_signer: Option<&Pubkey>, - other_signers: &[KeyedAccount], + signers: &HashSet, new_authorized: &Pubkey, stake_authorize: StakeAuthorize, ) -> Result<(), InstructionError> { - self.check(stake_signer, other_signers, stake_authorize)?; + self.check(signers, stake_authorize)?; match stake_authorize { StakeAuthorize::Staker => self.staker = *new_authorized, StakeAuthorize::Withdrawer => self.withdrawer = *new_authorized, @@ -419,19 +411,19 @@ pub trait StakeAccount { &mut self, authority: &Pubkey, stake_authorize: StakeAuthorize, - other_signers: &[KeyedAccount], + signers: &HashSet, ) -> Result<(), InstructionError>; fn delegate_stake( &mut self, vote_account: &KeyedAccount, clock: &sysvar::clock::Clock, config: &Config, - other_signers: &[KeyedAccount], + signers: &HashSet, ) -> Result<(), InstructionError>; fn deactivate_stake( &mut self, clock: &sysvar::clock::Clock, - other_signers: &[KeyedAccount], + signers: &HashSet, ) -> Result<(), InstructionError>; fn redeem_vote_credits( &mut self, @@ -443,9 +435,10 @@ pub trait StakeAccount { fn withdraw( &mut self, lamports: u64, + to: &mut KeyedAccount, clock: &sysvar::clock::Clock, stake_history: &sysvar::stake_history::StakeHistory, - recipient_and_signer_accounts: &mut [KeyedAccount], + signers: &HashSet, ) -> Result<(), InstructionError>; } @@ -468,15 +461,15 @@ impl<'a> StakeAccount for KeyedAccount<'a> { &mut self, authority: &Pubkey, stake_authorize: StakeAuthorize, - other_signers: &[KeyedAccount], + signers: &HashSet, ) -> Result<(), InstructionError> { let stake_state = self.state()?; if let StakeState::Stake(mut authorized, lockup, stake) = stake_state { - authorized.authorize(self.signer_key(), other_signers, authority, stake_authorize)?; + authorized.authorize(signers, authority, stake_authorize)?; self.set_state(&StakeState::Stake(authorized, lockup, stake)) } else if let StakeState::Initialized(mut authorized, lockup) = stake_state { - authorized.authorize(self.signer_key(), other_signers, authority, stake_authorize)?; + authorized.authorize(signers, authority, stake_authorize)?; self.set_state(&StakeState::Initialized(authorized, lockup)) } else { Err(InstructionError::InvalidAccountData) @@ -487,10 +480,10 @@ impl<'a> StakeAccount for KeyedAccount<'a> { vote_account: &KeyedAccount, clock: &sysvar::clock::Clock, config: &Config, - other_signers: &[KeyedAccount], + signers: &HashSet, ) -> Result<(), InstructionError> { if let StakeState::Initialized(authorized, lockup) = self.state()? { - authorized.check(self.signer_key(), other_signers, StakeAuthorize::Staker)?; + authorized.check(signers, StakeAuthorize::Staker)?; let stake = Stake::new( self.account.lamports, vote_account.unsigned_key(), @@ -501,7 +494,7 @@ impl<'a> StakeAccount for KeyedAccount<'a> { self.set_state(&StakeState::Stake(authorized, lockup, stake)) } else if let StakeState::Stake(authorized, lockup, mut stake) = self.state()? { - authorized.check(self.signer_key(), other_signers, StakeAuthorize::Staker)?; + authorized.check(signers, StakeAuthorize::Staker)?; stake.redelegate( vote_account.unsigned_key(), &vote_account.state()?, @@ -515,10 +508,10 @@ impl<'a> StakeAccount for KeyedAccount<'a> { fn deactivate_stake( &mut self, clock: &sysvar::clock::Clock, - other_signers: &[KeyedAccount], + signers: &HashSet, ) -> Result<(), InstructionError> { if let StakeState::Stake(authorized, lockup, mut stake) = self.state()? { - authorized.check(self.signer_key(), other_signers, StakeAuthorize::Staker)?; + authorized.check(signers, StakeAuthorize::Staker)?; stake.deactivate(clock.epoch); self.set_state(&StakeState::Stake(authorized, lockup, stake)) @@ -574,17 +567,14 @@ impl<'a> StakeAccount for KeyedAccount<'a> { fn withdraw( &mut self, lamports: u64, + to: &mut KeyedAccount, clock: &sysvar::clock::Clock, stake_history: &sysvar::stake_history::StakeHistory, - recipient_and_signer_accounts: &mut [KeyedAccount], + signers: &HashSet, ) -> Result<(), InstructionError> { let lockup = match self.state()? { StakeState::Stake(authorized, lockup, stake) => { - authorized.check( - self.signer_key(), - recipient_and_signer_accounts, - StakeAuthorize::Withdrawer, - )?; + authorized.check(signers, StakeAuthorize::Withdrawer)?; // if we have a deactivation epoch and we're in cooldown let staked = if clock.epoch >= stake.deactivation_epoch { stake.stake(clock.epoch, Some(stake_history)) @@ -601,11 +591,7 @@ impl<'a> StakeAccount for KeyedAccount<'a> { lockup } StakeState::Initialized(authorized, lockup) => { - authorized.check( - self.signer_key(), - recipient_and_signer_accounts, - StakeAuthorize::Withdrawer, - )?; + authorized.check(signers, StakeAuthorize::Withdrawer)?; lockup } StakeState::Uninitialized => { @@ -616,7 +602,6 @@ impl<'a> StakeAccount for KeyedAccount<'a> { } _ => return Err(InstructionError::InvalidAccountData), }; - let mut to = &mut recipient_and_signer_accounts[0]; if lockup.slot > clock.slot && lockup.custodian != *to.unsigned_key() { return Err(StakeError::LockupInForce.into()); @@ -770,20 +755,22 @@ mod tests { ); } + let mut signers = HashSet::default(); assert_eq!( stake_keyed_account.delegate_stake( &vote_keyed_account, &clock, &Config::default(), - &[] + &signers, ), Err(InstructionError::MissingRequiredSignature) ); // signed keyed account let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account); + signers.insert(stake_pubkey); assert!(stake_keyed_account - .delegate_stake(&vote_keyed_account, &clock, &Config::default(), &[]) + .delegate_stake(&vote_keyed_account, &clock, &Config::default(), &signers,) .is_ok()); // verify that delegate_stake() looks right, compare against hand-rolled @@ -808,7 +795,7 @@ mod tests { // verify that delegate_stake can be called twice, 2nd is redelegate assert!(stake_keyed_account - .delegate_stake(&vote_keyed_account, &clock, &Config::default(), &[]) + .delegate_stake(&vote_keyed_account, &clock, &Config::default(), &signers) .is_ok()); // verify that non-stakes fail delegate_stake() @@ -816,7 +803,7 @@ mod tests { stake_keyed_account.set_state(&stake_state).unwrap(); assert!(stake_keyed_account - .delegate_stake(&vote_keyed_account, &clock, &Config::default(), &[]) + .delegate_stake(&vote_keyed_account, &clock, &Config::default(), &signers) .is_err()); } @@ -1163,8 +1150,9 @@ mod tests { // signed keyed account but not staked yet 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.deactivate_stake(&clock, &[]), + stake_keyed_account.deactivate_stake(&clock, &signers), Err(InstructionError::InvalidAccountData) ); @@ -1179,21 +1167,24 @@ mod tests { &vote_keyed_account, &clock, &Config::default(), - &[] + &signers ), Ok(()) ); - // unsigned keyed account + // no signers fails let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &mut stake_account); assert_eq!( - stake_keyed_account.deactivate_stake(&clock, &[]), + stake_keyed_account.deactivate_stake(&clock, &HashSet::default()), 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(&clock, &[]), Ok(())); + assert_eq!( + stake_keyed_account.deactivate_stake(&clock, &signers), + Ok(()) + ); } #[test] @@ -1212,29 +1203,32 @@ mod tests { let to = Pubkey::new_rand(); let mut to_account = Account::new(1, 0, &system_program::id()); - let to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); + let mut to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); - // unsigned keyed account should fail + // no signers, should fail let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &mut stake_account); assert_eq!( stake_keyed_account.withdraw( stake_lamports, + &mut to_keyed_account, &clock, &StakeHistory::default(), - &mut [to_keyed_account], + &HashSet::default(), ), Err(InstructionError::MissingRequiredSignature) ); // signed keyed account and uninitialized should work let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account); - let to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); + let mut to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); + let signers = vec![stake_pubkey].into_iter().collect(); assert_eq!( stake_keyed_account.withdraw( stake_lamports, + &mut to_keyed_account, &clock, &StakeHistory::default(), - &mut [to_keyed_account], + &signers, ), Ok(()) ); @@ -1255,13 +1249,14 @@ mod tests { // 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 to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); + let mut to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); assert_eq!( stake_keyed_account.withdraw( stake_lamports + 1, + &mut to_keyed_account, &clock, &StakeHistory::default(), - &mut [to_keyed_account], + &signers, ), Err(InstructionError::InsufficientFunds) ); @@ -1277,7 +1272,7 @@ mod tests { &vote_keyed_account, &clock, &Config::default(), - &[] + &signers, ), Ok(()) ); @@ -1286,13 +1281,14 @@ mod tests { stake_account.lamports += 10; // withdrawal before deactivate works for rewards amount let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account); - let to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); + let mut to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); assert_eq!( stake_keyed_account.withdraw( 10, + &mut to_keyed_account, &clock, &StakeHistory::default(), - &mut [to_keyed_account], + &signers, ), Ok(()) ); @@ -1301,42 +1297,48 @@ mod tests { stake_account.lamports += 10; // withdrawal of rewards fails if not in excess of stake let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account); - let to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); + let mut to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); assert_eq!( stake_keyed_account.withdraw( 10 + 1, + &mut to_keyed_account, &clock, &StakeHistory::default(), - &mut [to_keyed_account], + &signers ), Err(InstructionError::InsufficientFunds) ); // deactivate the stake before withdrawal - assert_eq!(stake_keyed_account.deactivate_stake(&clock, &[]), Ok(())); + assert_eq!( + stake_keyed_account.deactivate_stake(&clock, &signers), + Ok(()) + ); // simulate time passing clock.epoch += 100; // Try to withdraw more than what's available - let to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); + let mut to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); assert_eq!( stake_keyed_account.withdraw( stake_lamports + 10 + 1, + &mut to_keyed_account, &clock, &StakeHistory::default(), - &mut [to_keyed_account], + &signers ), Err(InstructionError::InsufficientFunds) ); // Try to withdraw all lamports - let to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); + let mut to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); assert_eq!( stake_keyed_account.withdraw( stake_lamports + 10, + &mut to_keyed_account, &clock, &StakeHistory::default(), - &mut [to_keyed_account], + &signers ), Ok(()) ); @@ -1362,7 +1364,7 @@ mod tests { let to = Pubkey::new_rand(); let mut to_account = Account::new(1, 0, &system_program::id()); - let to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); + let mut to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account); @@ -1372,12 +1374,13 @@ mod tests { vote_state::create_account(&vote_pubkey, &Pubkey::new_rand(), 0, 100); let mut vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &mut vote_account); vote_keyed_account.set_state(&VoteState::default()).unwrap(); + let signers = vec![stake_pubkey].into_iter().collect(); assert_eq!( stake_keyed_account.delegate_stake( &vote_keyed_account, &future, &Config::default(), - &[] + &signers, ), Ok(()) ); @@ -1392,9 +1395,10 @@ mod tests { assert_eq!( stake_keyed_account.withdraw( total_lamports - stake_lamports + 1, + &mut to_keyed_account, &clock, &stake_history, - &mut [to_keyed_account], + &signers, ), Err(InstructionError::InsufficientFunds) ); @@ -1414,15 +1418,16 @@ mod tests { let to = Pubkey::new_rand(); let mut to_account = Account::new(1, 0, &system_program::id()); - let to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); + let mut to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); 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.withdraw( total_lamports, + &mut to_keyed_account, &sysvar::clock::Clock::default(), &StakeHistory::default(), - &mut [to_keyed_account], + &signers, ), Err(InstructionError::InvalidAccountData) ); @@ -1446,31 +1451,37 @@ mod tests { let to = Pubkey::new_rand(); let mut to_account = Account::new(1, 0, &system_program::id()); - let to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); + let mut to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account); let mut clock = sysvar::clock::Clock::default(); + + let signers = vec![stake_pubkey].into_iter().collect(); + // lockup is still in force, can't withdraw assert_eq!( stake_keyed_account.withdraw( total_lamports, + &mut to_keyed_account, &clock, &StakeHistory::default(), - &mut [to_keyed_account], + &signers, ), Err(StakeError::LockupInForce.into()) ); // but we *can* send to the custodian let mut custodian_account = Account::new(1, 0, &system_program::id()); - let custodian_keyed_account = KeyedAccount::new(&custodian, false, &mut custodian_account); + let mut custodian_keyed_account = + KeyedAccount::new(&custodian, false, &mut custodian_account); assert_eq!( stake_keyed_account.withdraw( total_lamports, + &mut custodian_keyed_account, &clock, &StakeHistory::default(), - &mut [custodian_keyed_account], + &signers, ), Ok(()) ); @@ -1478,14 +1489,15 @@ mod tests { stake_keyed_account.account.lamports = total_lamports; // lockup has expired - let to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); + let mut to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); clock.slot += 1; assert_eq!( stake_keyed_account.withdraw( total_lamports, + &mut to_keyed_account, &clock, &StakeHistory::default(), - &mut [to_keyed_account], + &signers, ), Ok(()) ); @@ -1609,10 +1621,10 @@ mod tests { ), Err(InstructionError::InvalidAccountData) ); - + let signers = vec![stake_pubkey].into_iter().collect(); // delegate the stake assert!(stake_keyed_account - .delegate_stake(&vote_keyed_account, &clock, &Config::default(), &[]) + .delegate_stake(&vote_keyed_account, &clock, &Config::default(), &signers) .is_ok()); let stake_history = create_stake_history_from_stakes( @@ -1725,18 +1737,19 @@ mod tests { let to = Pubkey::new_rand(); let mut to_account = Account::new(1, 0, &system_program::id()); - let to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); + 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(); + let signers = vec![stake_pubkey].into_iter().collect(); assert_eq!( - stake_keyed_account.authorize(&stake_pubkey0, StakeAuthorize::Staker, &[]), + stake_keyed_account.authorize(&stake_pubkey0, StakeAuthorize::Staker, &signers), Ok(()) ); assert_eq!( - stake_keyed_account.authorize(&stake_pubkey0, StakeAuthorize::Withdrawer, &[]), + stake_keyed_account.authorize(&stake_pubkey0, StakeAuthorize::Withdrawer, &signers), Ok(()) ); if let StakeState::Initialized(authorized, _lockup) = @@ -1751,21 +1764,16 @@ 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, &[]), + stake_keyed_account.authorize(&stake_pubkey1, StakeAuthorize::Staker, &signers), 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); + let signers0 = vec![stake_pubkey0].into_iter().collect(); // 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, - &[staker_keyed_account0] - ), + stake_keyed_account.authorize(&stake_pubkey2, StakeAuthorize::Staker, &signers0), Ok(()) ); if let StakeState::Initialized(authorized, _lockup) = @@ -1774,13 +1782,8 @@ mod tests { assert_eq!(authorized.staker, stake_pubkey2); } - let staker_keyed_account0 = KeyedAccount::new(&stake_pubkey0, true, &mut staker_account0); assert_eq!( - stake_keyed_account.authorize( - &stake_pubkey2, - StakeAuthorize::Withdrawer, - &[staker_keyed_account0] - ), + stake_keyed_account.authorize(&stake_pubkey2, StakeAuthorize::Withdrawer, &signers0,), Ok(()) ); if let StakeState::Initialized(authorized, _lockup) = @@ -1789,28 +1792,29 @@ mod tests { assert_eq!(authorized.staker, 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); + let signers2 = vec![stake_pubkey2].into_iter().collect(); // Test that withdrawal to account fails without authorized withdrawer assert_eq!( stake_keyed_account.withdraw( stake_lamports, + &mut to_keyed_account, &clock, &StakeHistory::default(), - &mut [to_keyed_account], + &signers, // old signer ), Err(InstructionError::MissingRequiredSignature) ); // Test a successful action by the currently authorized withdrawer - let to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); + let mut to_keyed_account = KeyedAccount::new(&to, false, &mut to_account); assert_eq!( stake_keyed_account.withdraw( stake_lamports, + &mut to_keyed_account, &clock, &StakeHistory::default(), - &mut [to_keyed_account, staker_keyed_account2], + &signers2, ), Ok(()) ); @@ -1836,21 +1840,21 @@ mod tests { 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); + let signers = vec![stake_pubkey].into_iter().collect(); stake_keyed_account - .delegate_stake(&vote_keyed_account, &clock, &Config::default(), &[]) + .delegate_stake(&vote_keyed_account, &clock, &Config::default(), &signers) .unwrap(); let new_staker_pubkey = Pubkey::new_rand(); assert_eq!( - stake_keyed_account.authorize(&new_staker_pubkey, StakeAuthorize::Staker, &[]), + stake_keyed_account.authorize(&new_staker_pubkey, StakeAuthorize::Staker, &signers), Ok(()) ); let authorized = StakeState::authorized_from(&stake_keyed_account.account).unwrap(); assert_eq!(authorized.staker, 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); + let other_signers = vec![other_pubkey].into_iter().collect(); // Use unsigned stake_keyed_account to test other signers let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &mut stake_account); @@ -1869,22 +1873,19 @@ mod tests { &new_vote_keyed_account, &clock, &Config::default(), - &[other_keyed_account] + &other_signers, ), 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); - + let new_signers = vec![new_staker_pubkey].into_iter().collect(); // Authorized staker should succeed assert_eq!( stake_keyed_account.delegate_stake( &new_vote_keyed_account, &clock, &Config::default(), - &[new_staker_keyed_account] + &new_signers ), Ok(()) ); @@ -1892,10 +1893,8 @@ mod tests { 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(&clock, &[new_staker_keyed_account]), + stake_keyed_account.deactivate_stake(&clock, &new_signers), Ok(()) ); } diff --git a/sdk/src/account.rs b/sdk/src/account.rs index b7413af78a..009d17c8e1 100644 --- a/sdk/src/account.rs +++ b/sdk/src/account.rs @@ -1,6 +1,6 @@ use crate::hash::Hash; use crate::{clock::Epoch, pubkey::Pubkey}; -use std::{cmp, fmt}; +use std::{cmp, fmt, iter::FromIterator}; /// An Account with data that is stored on chain #[repr(C)] @@ -196,3 +196,15 @@ pub fn create_keyed_credit_only_accounts(accounts: &mut [(Pubkey, Account)]) -> }) .collect() } + +/// Return all the signers from a set of KeyedAccounts +pub fn get_signers(keyed_accounts: &[KeyedAccount]) -> A +where + A: FromIterator, +{ + keyed_accounts + .iter() + .filter_map(|keyed_account| keyed_account.signer_key()) + .cloned() + .collect::() +} diff --git a/sdk/src/instruction_processor_utils.rs b/sdk/src/instruction_processor_utils.rs index 8da5eafbc2..5474a1f19d 100644 --- a/sdk/src/instruction_processor_utils.rs +++ b/sdk/src/instruction_processor_utils.rs @@ -1,6 +1,4 @@ -use crate::account::KeyedAccount; -use crate::instruction::InstructionError; -use crate::pubkey::Pubkey; +use crate::{account::KeyedAccount, instruction::InstructionError, pubkey::Pubkey}; use num_traits::{FromPrimitive, ToPrimitive}; // All native programs export a symbol named process()