From 3acd84d9c0be95176c24875c76bba56923a9a803 Mon Sep 17 00:00:00 2001 From: Justin Starry Date: Mon, 18 Nov 2019 15:43:47 -0500 Subject: [PATCH] Allow creating an vote program ix where the withdrawer is also the "to" account (#6992) automerge --- programs/stake_api/src/stake_instruction.rs | 88 +++++++-------------- programs/vote_api/src/vote_instruction.rs | 68 +++------------- sdk/src/instruction.rs | 57 +++++++++++++ 3 files changed, 99 insertions(+), 114 deletions(-) diff --git a/programs/stake_api/src/stake_instruction.rs b/programs/stake_api/src/stake_instruction.rs index c91008d07..af7904945 100644 --- a/programs/stake_api/src/stake_instruction.rs +++ b/programs/stake_api/src/stake_instruction.rs @@ -7,7 +7,7 @@ use num_derive::{FromPrimitive, ToPrimitive}; use serde_derive::{Deserialize, Serialize}; use solana_sdk::{ account::{get_signers, KeyedAccount}, - instruction::{AccountMeta, Instruction, InstructionError}, + instruction::{AccountMeta, Instruction, InstructionError, WithSigner}, instruction_processor_utils::{limited_deserialize, next_keyed_account, DecodeError}, pubkey::Pubkey, system_instruction, @@ -175,17 +175,14 @@ pub fn split( std::mem::size_of::() as u64, &id(), ), - Instruction::new( - id(), - &StakeInstruction::Split(lamports), - metas_with_signer( - &[ - AccountMeta::new(*stake_pubkey, false), - AccountMeta::new(*split_stake_pubkey, false), - ], - authorized_pubkey, - ), - ), + { + let account_metas = vec![ + AccountMeta::new(*stake_pubkey, false), + AccountMeta::new(*split_stake_pubkey, false), + ] + .with_signer(authorized_pubkey); + Instruction::new(id(), &StakeInstruction::Split(lamports), account_metas) + }, ] } @@ -220,34 +217,13 @@ pub fn create_stake_account_and_delegate_stake( 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 { - 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( stake_pubkey: &Pubkey, authorized_pubkey: &Pubkey, new_authorized_pubkey: &Pubkey, stake_authorize: StakeAuthorize, ) -> Instruction { - let account_metas = - metas_with_signer(&[AccountMeta::new(*stake_pubkey, false)], authorized_pubkey); + let account_metas = vec![AccountMeta::new(*stake_pubkey, false)].with_signer(authorized_pubkey); Instruction::new( id(), @@ -272,15 +248,13 @@ pub fn delegate_stake( authorized_pubkey: &Pubkey, vote_pubkey: &Pubkey, ) -> Instruction { - let account_metas = metas_with_signer( - &[ - AccountMeta::new(*stake_pubkey, false), - AccountMeta::new_readonly(*vote_pubkey, false), - AccountMeta::new_readonly(sysvar::clock::id(), false), - AccountMeta::new_readonly(crate::config::id(), false), - ], - authorized_pubkey, - ); + let account_metas = vec![ + AccountMeta::new(*stake_pubkey, false), + AccountMeta::new_readonly(*vote_pubkey, false), + AccountMeta::new_readonly(sysvar::clock::id(), false), + AccountMeta::new_readonly(crate::config::id(), false), + ] + .with_signer(authorized_pubkey); Instruction::new(id(), &StakeInstruction::DelegateStake, account_metas) } @@ -290,26 +264,22 @@ pub fn withdraw( to_pubkey: &Pubkey, lamports: u64, ) -> Instruction { - let account_metas = metas_with_signer( - &[ - AccountMeta::new(*stake_pubkey, false), - AccountMeta::new(*to_pubkey, false), - AccountMeta::new_readonly(sysvar::clock::id(), false), - AccountMeta::new_readonly(sysvar::stake_history::id(), false), - ], - authorized_pubkey, - ); + let account_metas = vec![ + AccountMeta::new(*stake_pubkey, false), + AccountMeta::new(*to_pubkey, false), + AccountMeta::new_readonly(sysvar::clock::id(), false), + AccountMeta::new_readonly(sysvar::stake_history::id(), false), + ] + .with_signer(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_with_signer( - &[ - AccountMeta::new(*stake_pubkey, false), - AccountMeta::new_readonly(sysvar::clock::id(), false), - ], - authorized_pubkey, - ); + let account_metas = vec![ + AccountMeta::new(*stake_pubkey, false), + AccountMeta::new_readonly(sysvar::clock::id(), false), + ] + .with_signer(authorized_pubkey); Instruction::new(id(), &StakeInstruction::Deactivate, account_metas) } diff --git a/programs/vote_api/src/vote_instruction.rs b/programs/vote_api/src/vote_instruction.rs index a24c1a246..adbc05e66 100644 --- a/programs/vote_api/src/vote_instruction.rs +++ b/programs/vote_api/src/vote_instruction.rs @@ -11,7 +11,7 @@ use serde_derive::{Deserialize, Serialize}; use solana_metrics::datapoint_debug; use solana_sdk::{ account::{get_signers, KeyedAccount}, - instruction::{AccountMeta, Instruction, InstructionError}, + instruction::{AccountMeta, Instruction, InstructionError, WithSigner}, instruction_processor_utils::{limited_deserialize, next_keyed_account, DecodeError}, pubkey::Pubkey, system_instruction, @@ -89,37 +89,13 @@ pub fn create_account( 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 { - 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( vote_pubkey: &Pubkey, authorized_pubkey: &Pubkey, // currently authorized new_authorized_pubkey: &Pubkey, vote_authorize: VoteAuthorize, ) -> 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( id(), @@ -129,16 +105,12 @@ pub fn authorize( } pub fn vote(vote_pubkey: &Pubkey, authorized_voter_pubkey: &Pubkey, vote: Vote) -> Instruction { - let account_metas = metas_for_authorized_signer( - vote_pubkey, - authorized_voter_pubkey, - &[ - // request slot_hashes sysvar account after vote_pubkey - AccountMeta::new_readonly(sysvar::slot_hashes::id(), false), - // request clock sysvar account after that - AccountMeta::new_readonly(sysvar::clock::id(), false), - ], - ); + let account_metas = vec![ + AccountMeta::new(*vote_pubkey, false), + AccountMeta::new_readonly(sysvar::slot_hashes::id(), false), + AccountMeta::new_readonly(sysvar::clock::id(), false), + ] + .with_signer(authorized_voter_pubkey); Instruction::new(id(), &VoteInstruction::Vote(vote), account_metas) } @@ -149,11 +121,11 @@ pub fn withdraw( lamports: u64, to_pubkey: &Pubkey, ) -> Instruction { - let account_metas = metas_for_authorized_signer( - vote_pubkey, - withdrawer_pubkey, - &[AccountMeta::new(*to_pubkey, false)], - ); + let account_metas = vec![ + AccountMeta::new(*vote_pubkey, false), + AccountMeta::new(*to_pubkey, false), + ] + .with_signer(withdrawer_pubkey); 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) } - #[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] fn test_custom_error_decode() { use num_traits::FromPrimitive; diff --git a/sdk/src/instruction.rs b/sdk/src/instruction.rs index c18ec5f8f..f7e6c8cab 100644 --- a/sdk/src/instruction.rs +++ b/sdk/src/instruction.rs @@ -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 { + 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 #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct CompiledInstruction { @@ -168,3 +190,38 @@ impl CompiledInstruction { &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); + } +}