reestablish parameter semantics for withdraw (#6330)

This commit is contained in:
Rob Walker 2019-10-14 15:02:24 -07:00 committed by GitHub
parent 3313b2ff58
commit c2ebf466fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 198 additions and 219 deletions

View File

@ -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<AccountMeta> {
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),
);
}

View File

@ -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<Pubkey>,
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<Pubkey>,
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<Pubkey>,
) -> Result<(), InstructionError>;
fn delegate_stake(
&mut self,
vote_account: &KeyedAccount,
clock: &sysvar::clock::Clock,
config: &Config,
other_signers: &[KeyedAccount],
signers: &HashSet<Pubkey>,
) -> Result<(), InstructionError>;
fn deactivate_stake(
&mut self,
clock: &sysvar::clock::Clock,
other_signers: &[KeyedAccount],
signers: &HashSet<Pubkey>,
) -> 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<Pubkey>,
) -> Result<(), InstructionError>;
}
@ -468,15 +461,15 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
&mut self,
authority: &Pubkey,
stake_authorize: StakeAuthorize,
other_signers: &[KeyedAccount],
signers: &HashSet<Pubkey>,
) -> 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<Pubkey>,
) -> 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<Pubkey>,
) -> 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<Pubkey>,
) -> 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(())
);
}

View File

@ -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<A>(keyed_accounts: &[KeyedAccount]) -> A
where
A: FromIterator<Pubkey>,
{
keyed_accounts
.iter()
.filter_map(|keyed_account| keyed_account.signer_key())
.cloned()
.collect::<A>()
}

View File

@ -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()