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 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::<StakeState>() 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<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(
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)
}

View File

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

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
#[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);
}
}