Allow creating an vote program ix where the withdrawer is also the "to" account (#6992)
automerge
This commit is contained in:
@ -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),
|
|
||||||
metas_with_signer(
|
|
||||||
&[
|
|
||||||
AccountMeta::new(*stake_pubkey, false),
|
AccountMeta::new(*stake_pubkey, false),
|
||||||
AccountMeta::new(*split_stake_pubkey, false),
|
AccountMeta::new(*split_stake_pubkey, false),
|
||||||
],
|
]
|
||||||
authorized_pubkey,
|
.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
|
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),
|
||||||
],
|
]
|
||||||
authorized_pubkey,
|
.with_signer(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),
|
||||||
],
|
]
|
||||||
authorized_pubkey,
|
.with_signer(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),
|
||||||
],
|
]
|
||||||
authorized_pubkey,
|
.with_signer(authorized_pubkey);
|
||||||
);
|
|
||||||
Instruction::new(id(), &StakeInstruction::Deactivate, account_metas)
|
Instruction::new(id(), &StakeInstruction::Deactivate, account_metas)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
|
||||||
&[
|
|
||||||
// request slot_hashes sysvar account after vote_pubkey
|
|
||||||
AccountMeta::new_readonly(sysvar::slot_hashes::id(), false),
|
AccountMeta::new_readonly(sysvar::slot_hashes::id(), false),
|
||||||
// request clock sysvar account after that
|
|
||||||
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
||||||
],
|
]
|
||||||
);
|
.with_signer(authorized_voter_pubkey);
|
||||||
|
|
||||||
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;
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user