Allow creating an vote program ix where the withdrawer is also the "to" account (#6992)

automerge
This commit is contained in:
Justin Starry
2019-11-18 15:43:47 -05:00
committed by Grimes
parent c902fd0303
commit 3acd84d9c0
3 changed files with 99 additions and 114 deletions

View File

@ -7,7 +7,7 @@ use num_derive::{FromPrimitive, ToPrimitive};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use solana_sdk::{ use solana_sdk::{
account::{get_signers, KeyedAccount}, account::{get_signers, KeyedAccount},
instruction::{AccountMeta, Instruction, InstructionError}, instruction::{AccountMeta, Instruction, InstructionError, WithSigner},
instruction_processor_utils::{limited_deserialize, next_keyed_account, DecodeError}, instruction_processor_utils::{limited_deserialize, next_keyed_account, DecodeError},
pubkey::Pubkey, pubkey::Pubkey,
system_instruction, system_instruction,
@ -175,17 +175,14 @@ pub fn split(
std::mem::size_of::<StakeState>() as u64, std::mem::size_of::<StakeState>() as u64,
&id(), &id(),
), ),
Instruction::new( {
id(), let account_metas = vec![
&StakeInstruction::Split(lamports), AccountMeta::new(*stake_pubkey, false),
metas_with_signer( AccountMeta::new(*split_stake_pubkey, false),
&[ ]
AccountMeta::new(*stake_pubkey, false), .with_signer(authorized_pubkey);
AccountMeta::new(*split_stake_pubkey, false), Instruction::new(id(), &StakeInstruction::Split(lamports), account_metas)
], },
authorized_pubkey,
),
),
] ]
} }
@ -220,34 +217,13 @@ pub fn create_stake_account_and_delegate_stake(
instructions instructions
} }
// 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 mut metas = metas.to_vec();
for meta in metas.iter_mut() {
if &meta.pubkey == signer {
meta.is_signer = true; // found it, we're done
return metas;
}
}
// signer wasn't in metas, append it after normal parameters
metas.push(AccountMeta::new_readonly(*signer, true));
metas
}
pub fn authorize( pub fn authorize(
stake_pubkey: &Pubkey, stake_pubkey: &Pubkey,
authorized_pubkey: &Pubkey, authorized_pubkey: &Pubkey,
new_authorized_pubkey: &Pubkey, new_authorized_pubkey: &Pubkey,
stake_authorize: StakeAuthorize, stake_authorize: StakeAuthorize,
) -> Instruction { ) -> Instruction {
let account_metas = let account_metas = vec![AccountMeta::new(*stake_pubkey, false)].with_signer(authorized_pubkey);
metas_with_signer(&[AccountMeta::new(*stake_pubkey, false)], authorized_pubkey);
Instruction::new( Instruction::new(
id(), id(),
@ -272,15 +248,13 @@ pub fn delegate_stake(
authorized_pubkey: &Pubkey, authorized_pubkey: &Pubkey,
vote_pubkey: &Pubkey, vote_pubkey: &Pubkey,
) -> Instruction { ) -> Instruction {
let account_metas = metas_with_signer( let account_metas = vec![
&[ AccountMeta::new(*stake_pubkey, false),
AccountMeta::new(*stake_pubkey, false), AccountMeta::new_readonly(*vote_pubkey, false),
AccountMeta::new_readonly(*vote_pubkey, false), AccountMeta::new_readonly(sysvar::clock::id(), false),
AccountMeta::new_readonly(sysvar::clock::id(), false), AccountMeta::new_readonly(crate::config::id(), false),
AccountMeta::new_readonly(crate::config::id(), false), ]
], .with_signer(authorized_pubkey);
authorized_pubkey,
);
Instruction::new(id(), &StakeInstruction::DelegateStake, account_metas) Instruction::new(id(), &StakeInstruction::DelegateStake, account_metas)
} }
@ -290,26 +264,22 @@ pub fn withdraw(
to_pubkey: &Pubkey, to_pubkey: &Pubkey,
lamports: u64, lamports: u64,
) -> Instruction { ) -> Instruction {
let account_metas = metas_with_signer( let account_metas = vec![
&[ AccountMeta::new(*stake_pubkey, false),
AccountMeta::new(*stake_pubkey, false), AccountMeta::new(*to_pubkey, false),
AccountMeta::new(*to_pubkey, false), AccountMeta::new_readonly(sysvar::clock::id(), false),
AccountMeta::new_readonly(sysvar::clock::id(), false), AccountMeta::new_readonly(sysvar::stake_history::id(), false),
AccountMeta::new_readonly(sysvar::stake_history::id(), false), ]
], .with_signer(authorized_pubkey);
authorized_pubkey,
);
Instruction::new(id(), &StakeInstruction::Withdraw(lamports), account_metas) Instruction::new(id(), &StakeInstruction::Withdraw(lamports), account_metas)
} }
pub fn deactivate_stake(stake_pubkey: &Pubkey, authorized_pubkey: &Pubkey) -> Instruction { pub fn deactivate_stake(stake_pubkey: &Pubkey, authorized_pubkey: &Pubkey) -> Instruction {
let account_metas = metas_with_signer( let account_metas = vec![
&[ AccountMeta::new(*stake_pubkey, false),
AccountMeta::new(*stake_pubkey, false), AccountMeta::new_readonly(sysvar::clock::id(), false),
AccountMeta::new_readonly(sysvar::clock::id(), false), ]
], .with_signer(authorized_pubkey);
authorized_pubkey,
);
Instruction::new(id(), &StakeInstruction::Deactivate, account_metas) Instruction::new(id(), &StakeInstruction::Deactivate, account_metas)
} }

View File

@ -11,7 +11,7 @@ use serde_derive::{Deserialize, Serialize};
use solana_metrics::datapoint_debug; use solana_metrics::datapoint_debug;
use solana_sdk::{ use solana_sdk::{
account::{get_signers, KeyedAccount}, account::{get_signers, KeyedAccount},
instruction::{AccountMeta, Instruction, InstructionError}, instruction::{AccountMeta, Instruction, InstructionError, WithSigner},
instruction_processor_utils::{limited_deserialize, next_keyed_account, DecodeError}, instruction_processor_utils::{limited_deserialize, next_keyed_account, DecodeError},
pubkey::Pubkey, pubkey::Pubkey,
system_instruction, system_instruction,
@ -89,37 +89,13 @@ pub fn create_account(
vec![create_ix, init_ix] vec![create_ix, init_ix]
} }
// 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],
) -> Vec<AccountMeta> {
let is_own_signer = authorized_signer == account_pubkey;
// vote account
let mut account_metas = vec![AccountMeta::new(*account_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_readonly(*authorized_signer, true))
// signer
}
account_metas
}
pub fn authorize( pub fn authorize(
vote_pubkey: &Pubkey, vote_pubkey: &Pubkey,
authorized_pubkey: &Pubkey, // currently authorized authorized_pubkey: &Pubkey, // currently authorized
new_authorized_pubkey: &Pubkey, new_authorized_pubkey: &Pubkey,
vote_authorize: VoteAuthorize, vote_authorize: VoteAuthorize,
) -> Instruction { ) -> Instruction {
let account_metas = metas_for_authorized_signer(vote_pubkey, authorized_pubkey, &[]); let account_metas = vec![AccountMeta::new(*vote_pubkey, false)].with_signer(authorized_pubkey);
Instruction::new( Instruction::new(
id(), id(),
@ -129,16 +105,12 @@ pub fn authorize(
} }
pub fn vote(vote_pubkey: &Pubkey, authorized_voter_pubkey: &Pubkey, vote: Vote) -> Instruction { pub fn vote(vote_pubkey: &Pubkey, authorized_voter_pubkey: &Pubkey, vote: Vote) -> Instruction {
let account_metas = metas_for_authorized_signer( let account_metas = vec![
vote_pubkey, AccountMeta::new(*vote_pubkey, false),
authorized_voter_pubkey, AccountMeta::new_readonly(sysvar::slot_hashes::id(), false),
&[ AccountMeta::new_readonly(sysvar::clock::id(), false),
// request slot_hashes sysvar account after vote_pubkey ]
AccountMeta::new_readonly(sysvar::slot_hashes::id(), false), .with_signer(authorized_voter_pubkey);
// request clock sysvar account after that
AccountMeta::new_readonly(sysvar::clock::id(), false),
],
);
Instruction::new(id(), &VoteInstruction::Vote(vote), account_metas) Instruction::new(id(), &VoteInstruction::Vote(vote), account_metas)
} }
@ -149,11 +121,11 @@ pub fn withdraw(
lamports: u64, lamports: u64,
to_pubkey: &Pubkey, to_pubkey: &Pubkey,
) -> Instruction { ) -> Instruction {
let account_metas = metas_for_authorized_signer( let account_metas = vec![
vote_pubkey, AccountMeta::new(*vote_pubkey, false),
withdrawer_pubkey, AccountMeta::new(*to_pubkey, false),
&[AccountMeta::new(*to_pubkey, false)], ]
); .with_signer(withdrawer_pubkey);
Instruction::new(id(), &VoteInstruction::Withdraw(lamports), account_metas) Instruction::new(id(), &VoteInstruction::Withdraw(lamports), account_metas)
} }
@ -292,20 +264,6 @@ mod tests {
assert!(minimum_balance as f64 / 10f64.powf(9.0) < 0.02) assert!(minimum_balance as f64 / 10f64.powf(9.0) < 0.02)
} }
#[test]
fn test_metas_for_authorized_signer() {
let account_pubkey = Pubkey::new_rand();
let authorized_signer = Pubkey::new_rand();
assert_eq!(
metas_for_authorized_signer(&account_pubkey, &authorized_signer, &[]).len(),
2
);
assert_eq!(
metas_for_authorized_signer(&account_pubkey, &account_pubkey, &[]).len(),
1
);
}
#[test] #[test]
fn test_custom_error_decode() { fn test_custom_error_decode() {
use num_traits::FromPrimitive; use num_traits::FromPrimitive;

View File

@ -141,6 +141,28 @@ impl AccountMeta {
} }
} }
/// Trait for adding a signer Pubkey to an existing data structure
pub trait WithSigner {
/// Add a signer Pubkey
fn with_signer(self, signer: &Pubkey) -> Self;
}
impl WithSigner for Vec<AccountMeta> {
fn with_signer(mut self, signer: &Pubkey) -> Self {
for meta in self.iter_mut() {
// signer might already appear in parameters
if &meta.pubkey == signer {
meta.is_signer = true; // found it, we're done
return self;
}
}
// signer wasn't in metas, append it after normal parameters
self.push(AccountMeta::new_readonly(*signer, true));
self
}
}
/// An instruction to execute a program /// An instruction to execute a program
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct CompiledInstruction { pub struct CompiledInstruction {
@ -168,3 +190,38 @@ impl CompiledInstruction {
&program_ids[self.program_id_index as usize] &program_ids[self.program_id_index as usize]
} }
} }
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_account_meta_list_with_signer() {
let account_pubkey = Pubkey::new_rand();
let signer_pubkey = Pubkey::new_rand();
let account_meta = AccountMeta::new(account_pubkey, false);
let signer_account_meta = AccountMeta::new(signer_pubkey, false);
let metas = vec![].with_signer(&signer_pubkey);
assert_eq!(metas.len(), 1);
assert!(metas[0].is_signer);
let metas = vec![account_meta.clone()].with_signer(&signer_pubkey);
assert_eq!(metas.len(), 2);
assert!(!metas[0].is_signer);
assert!(metas[1].is_signer);
assert_eq!(metas[1].pubkey, signer_pubkey);
let metas = vec![signer_account_meta.clone()].with_signer(&signer_pubkey);
assert_eq!(metas.len(), 1);
assert!(metas[0].is_signer);
assert_eq!(metas[0].pubkey, signer_pubkey);
let metas = vec![account_meta, signer_account_meta].with_signer(&signer_pubkey);
assert_eq!(metas.len(), 2);
assert!(!metas[0].is_signer);
assert!(metas[1].is_signer);
assert_eq!(metas[1].pubkey, signer_pubkey);
}
}