Add new CreateVoteAccount instruction-set builders (#31330)

* Add failing test

* Add config struct for vote-account creation

* Add create-vote-account ix builders that use CreateVoteAccountConfig

* Test now passes; add case to demonstrate failure after activation

* Deprecate existing ix builders

* Use new builders in solana-cli

* Query feature status to use correct VoteState size in solana-cli

* Fix tests and clippy warnings

* Improve ugly conditional block
This commit is contained in:
Tyera 2023-04-27 11:14:39 -06:00 committed by GitHub
parent 54f1595dfa
commit 9af7009bb4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 236 additions and 53 deletions

View File

@ -31,14 +31,14 @@ use {
solana_rpc_client_api::config::RpcGetVoteAccountsConfig,
solana_rpc_client_nonce_utils::blockhash_query::BlockhashQuery,
solana_sdk::{
account::Account, commitment_config::CommitmentConfig, message::Message,
account::Account, commitment_config::CommitmentConfig, feature, message::Message,
native_token::lamports_to_sol, pubkey::Pubkey, system_instruction::SystemError,
transaction::Transaction,
},
solana_vote_program::{
vote_error::VoteError,
vote_instruction::{self, withdraw},
vote_state::{VoteAuthorize, VoteInit, VoteState},
vote_instruction::{self, withdraw, CreateVoteAccountConfig},
vote_state::{VoteAuthorize, VoteInit, VoteState, VoteStateVersions},
},
std::sync::Arc,
};
@ -800,6 +800,13 @@ pub fn process_create_vote_account(
let fee_payer = config.signers[fee_payer];
let nonce_authority = config.signers[nonce_authority];
let is_feature_active = (!sign_only)
.then(solana_sdk::feature_set::vote_state_add_vote_latency::id)
.and_then(|feature_address| rpc_client.get_account(&feature_address).ok())
.and_then(|account| feature::from_account(&account))
.map_or(false, |feature| feature.activated_at.is_some());
let space = VoteStateVersions::vote_state_size_of(is_feature_active) as u64;
let build_message = |lamports| {
let vote_init = VoteInit {
node_pubkey: identity_pubkey,
@ -807,28 +814,27 @@ pub fn process_create_vote_account(
authorized_withdrawer,
commission,
};
let ixs = if let Some(seed) = seed {
vote_instruction::create_account_with_seed(
&config.signers[0].pubkey(), // from
&vote_account_address, // to
&vote_account_pubkey, // base
seed, // seed
&vote_init,
lamports,
)
.with_memo(memo)
.with_compute_unit_price(compute_unit_price)
} else {
vote_instruction::create_account(
&config.signers[0].pubkey(),
&vote_account_pubkey,
&vote_init,
lamports,
)
.with_memo(memo)
.with_compute_unit_price(compute_unit_price)
let mut create_vote_account_config = CreateVoteAccountConfig {
space,
..CreateVoteAccountConfig::default()
};
let to = if let Some(seed) = seed {
create_vote_account_config.with_seed = Some((&vote_account_pubkey, seed));
&vote_account_address
} else {
&vote_account_pubkey
};
let ixs = vote_instruction::create_account_with_config(
&config.signers[0].pubkey(),
to,
&vote_init,
lamports,
create_vote_account_config,
)
.with_memo(memo)
.with_compute_unit_price(compute_unit_price);
if let Some(nonce_account) = &nonce_account {
Message::new_with_nonce(
ixs,

View File

@ -46,7 +46,7 @@ pub(crate) mod tests {
process_instructions(
bank,
&[from_account, vote_account, validator_identity_account],
&vote_instruction::create_account(
&vote_instruction::create_account_with_config(
&from_account.pubkey(),
&vote_pubkey,
&VoteInit {
@ -56,6 +56,10 @@ pub(crate) mod tests {
commission: 0,
},
amount,
vote_instruction::CreateVoteAccountConfig {
space: VoteStateVersions::vote_state_size_of(true) as u64,
..vote_instruction::CreateVoteAccountConfig::default()
},
),
);

View File

@ -653,7 +653,7 @@ impl LocalCluster {
{
// 1) Create vote account
let instructions = vote_instruction::create_account(
let instructions = vote_instruction::create_account_with_config(
&from_account.pubkey(),
&vote_account_pubkey,
&VoteInit {
@ -663,6 +663,10 @@ impl LocalCluster {
commission: 0,
},
amount,
vote_instruction::CreateVoteAccountConfig {
space: vote_state::VoteStateVersions::vote_state_size_of(true) as u64,
..vote_instruction::CreateVoteAccountConfig::default()
},
);
let message = Message::new(&instructions, Some(&from_account.pubkey()));
let mut transaction = Transaction::new(

View File

@ -82,7 +82,7 @@ async fn setup_vote(context: &mut ProgramTestContext) -> Pubkey {
let vote_lamports = Rent::default().minimum_balance(VoteState::size_of());
let vote_keypair = Keypair::new();
let user_keypair = Keypair::new();
instructions.append(&mut vote_instruction::create_account(
instructions.append(&mut vote_instruction::create_account_with_config(
&context.payer.pubkey(),
&vote_keypair.pubkey(),
&VoteInit {
@ -91,6 +91,10 @@ async fn setup_vote(context: &mut ProgramTestContext) -> Pubkey {
..VoteInit::default()
},
vote_lamports,
vote_instruction::CreateVoteAccountConfig {
space: vote_state::VoteStateVersions::vote_state_size_of(true) as u64,
..vote_instruction::CreateVoteAccountConfig::default()
},
));
let transaction = Transaction::new_signed_with_payer(

View File

@ -283,9 +283,9 @@ mod tests {
crate::{
vote_error::VoteError,
vote_instruction::{
authorize, authorize_checked, create_account, update_commission,
authorize, authorize_checked, create_account_with_config, update_commission,
update_validator_identity, update_vote_state, update_vote_state_switch, vote,
vote_switch, withdraw, VoteInstruction,
vote_switch, withdraw, CreateVoteAccountConfig, VoteInstruction,
},
vote_state::{
self, Lockout, Vote, VoteAuthorize, VoteAuthorizeCheckedWithSeedArgs,
@ -1795,15 +1795,113 @@ mod tests {
);
}
#[test]
fn test_create_account_vote_state_1_14_11() {
let node_pubkey = Pubkey::new_unique();
let vote_pubkey = Pubkey::new_unique();
let instructions = create_account_with_config(
&node_pubkey,
&vote_pubkey,
&VoteInit {
node_pubkey,
authorized_voter: vote_pubkey,
authorized_withdrawer: vote_pubkey,
commission: 0,
},
101,
CreateVoteAccountConfig::default(),
);
// grab the `space` value from SystemInstruction::CreateAccount by directly indexing, for
// expediency
let space = usize::from_le_bytes(instructions[0].data[12..20].try_into().unwrap());
assert_eq!(space, vote_state::VoteState1_14_11::size_of());
let empty_vote_account = AccountSharedData::new(101, space, &id());
let transaction_accounts = vec![
(vote_pubkey, empty_vote_account),
(node_pubkey, AccountSharedData::default()),
(sysvar::clock::id(), create_default_clock_account()),
(sysvar::rent::id(), create_default_rent_account()),
];
// should succeed when vote_state_add_vote_latency is disabled
process_instruction_disabled_features(
&instructions[1].data,
transaction_accounts.clone(),
instructions[1].accounts.clone(),
Ok(()),
);
// should fail, if vote_state_add_vote_latency is enabled
process_instruction(
&instructions[1].data,
transaction_accounts,
instructions[1].accounts.clone(),
Err(InstructionError::InvalidAccountData),
);
}
#[test]
fn test_create_account_vote_state_current() {
let node_pubkey = Pubkey::new_unique();
let vote_pubkey = Pubkey::new_unique();
let instructions = create_account_with_config(
&node_pubkey,
&vote_pubkey,
&VoteInit {
node_pubkey,
authorized_voter: vote_pubkey,
authorized_withdrawer: vote_pubkey,
commission: 0,
},
101,
CreateVoteAccountConfig {
space: vote_state::VoteState::size_of() as u64,
..CreateVoteAccountConfig::default()
},
);
// grab the `space` value from SystemInstruction::CreateAccount by directly indexing, for
// expediency
let space = usize::from_le_bytes(instructions[0].data[12..20].try_into().unwrap());
assert_eq!(space, vote_state::VoteState::size_of());
let empty_vote_account = AccountSharedData::new(101, space, &id());
let transaction_accounts = vec![
(vote_pubkey, empty_vote_account),
(node_pubkey, AccountSharedData::default()),
(sysvar::clock::id(), create_default_clock_account()),
(sysvar::rent::id(), create_default_rent_account()),
];
// should fail, if vote_state_add_vote_latency is disabled
process_instruction_disabled_features(
&instructions[1].data,
transaction_accounts.clone(),
instructions[1].accounts.clone(),
Err(InstructionError::InvalidAccountData),
);
// succeeds, since vote_state_add_vote_latency is enabled
process_instruction(
&instructions[1].data,
transaction_accounts,
instructions[1].accounts.clone(),
Ok(()),
);
}
#[test]
fn test_vote_process_instruction() {
solana_logger::setup();
let instructions = create_account(
let instructions = create_account_with_config(
&Pubkey::new_unique(),
&Pubkey::new_unique(),
&VoteInit::default(),
101,
CreateVoteAccountConfig::default(),
);
// this case fails regardless of CreateVoteAccountConfig::space, because
// process_instruction_as_one_arg passes a default (empty) account
process_instruction_as_one_arg(&instructions[1], Err(InstructionError::InvalidAccountData));
process_instruction_as_one_arg(
&vote(

View File

@ -4439,7 +4439,7 @@ fn test_bank_vote_accounts() {
// to have a vote account
let vote_keypair = Keypair::new();
let instructions = vote_instruction::create_account(
let instructions = vote_instruction::create_account_with_config(
&mint_keypair.pubkey(),
&vote_keypair.pubkey(),
&VoteInit {
@ -4449,6 +4449,10 @@ fn test_bank_vote_accounts() {
commission: 0,
},
10,
vote_instruction::CreateVoteAccountConfig {
space: VoteStateVersions::vote_state_size_of(true) as u64,
..vote_instruction::CreateVoteAccountConfig::default()
},
);
let message = Message::new(&instructions, Some(&mint_keypair.pubkey()));
@ -4503,7 +4507,7 @@ fn test_bank_cloned_stake_delegations() {
};
let vote_keypair = Keypair::new();
let mut instructions = vote_instruction::create_account(
let mut instructions = vote_instruction::create_account_with_config(
&mint_keypair.pubkey(),
&vote_keypair.pubkey(),
&VoteInit {
@ -4513,6 +4517,10 @@ fn test_bank_cloned_stake_delegations() {
commission: 0,
},
vote_balance,
vote_instruction::CreateVoteAccountConfig {
space: VoteStateVersions::vote_state_size_of(true) as u64,
..vote_instruction::CreateVoteAccountConfig::default()
},
);
let stake_keypair = Keypair::new();
@ -4817,7 +4825,7 @@ fn test_add_builtin() {
let mock_account = Keypair::new();
let mock_validator_identity = Keypair::new();
let mut instructions = vote_instruction::create_account(
let mut instructions = vote_instruction::create_account_with_config(
&mint_keypair.pubkey(),
&mock_account.pubkey(),
&VoteInit {
@ -4825,6 +4833,10 @@ fn test_add_builtin() {
..VoteInit::default()
},
1,
vote_instruction::CreateVoteAccountConfig {
space: VoteStateVersions::vote_state_size_of(true) as u64,
..vote_instruction::CreateVoteAccountConfig::default()
},
);
instructions[1].program_id = mock_vote_program_id();
@ -4859,7 +4871,7 @@ fn test_add_duplicate_static_program() {
let mock_account = Keypair::new();
let mock_validator_identity = Keypair::new();
let instructions = vote_instruction::create_account(
let instructions = vote_instruction::create_account_with_config(
&mint_keypair.pubkey(),
&mock_account.pubkey(),
&VoteInit {
@ -4867,6 +4879,10 @@ fn test_add_duplicate_static_program() {
..VoteInit::default()
},
1,
vote_instruction::CreateVoteAccountConfig {
space: VoteStateVersions::vote_state_size_of(true) as u64,
..vote_instruction::CreateVoteAccountConfig::default()
},
);
let message = Message::new(&instructions, Some(&mint_keypair.pubkey()));
@ -9504,7 +9520,7 @@ fn test_vote_epoch_panic() {
let mut setup_ixs = Vec::new();
setup_ixs.extend(
vote_instruction::create_account(
vote_instruction::create_account_with_config(
&mint_keypair.pubkey(),
&vote_keypair.pubkey(),
&VoteInit {
@ -9514,6 +9530,10 @@ fn test_vote_epoch_panic() {
commission: 0,
},
1_000_000_000,
vote_instruction::CreateVoteAccountConfig {
space: VoteStateVersions::vote_state_size_of(true) as u64,
..vote_instruction::CreateVoteAccountConfig::default()
},
)
.into_iter(),
);

View File

@ -304,7 +304,7 @@ fn test_stake_account_lifetime() {
// Create Vote Account
let message = Message::new(
&vote_instruction::create_account(
&vote_instruction::create_account_with_config(
&mint_pubkey,
&vote_pubkey,
&VoteInit {
@ -314,6 +314,10 @@ fn test_stake_account_lifetime() {
commission: 50,
},
vote_balance,
vote_instruction::CreateVoteAccountConfig {
space: VoteStateVersions::vote_state_size_of(true) as u64,
..vote_instruction::CreateVoteAccountConfig::default()
},
),
Some(&mint_pubkey),
);
@ -569,7 +573,7 @@ fn test_create_stake_account_from_seed() {
// Create Vote Account
let message = Message::new(
&vote_instruction::create_account(
&vote_instruction::create_account_with_config(
&mint_pubkey,
&vote_pubkey,
&VoteInit {
@ -579,6 +583,10 @@ fn test_create_stake_account_from_seed() {
commission: 50,
},
10,
vote_instruction::CreateVoteAccountConfig {
space: VoteStateVersions::vote_state_size_of(true) as u64,
..vote_instruction::CreateVoteAccountConfig::default()
},
),
Some(&mint_pubkey),
);

View File

@ -11,8 +11,8 @@ use {
program::id,
state::{
serde_compact_vote_state_update, Vote, VoteAuthorize,
VoteAuthorizeCheckedWithSeedArgs, VoteAuthorizeWithSeedArgs, VoteInit, VoteState,
VoteStateUpdate,
VoteAuthorizeCheckedWithSeedArgs, VoteAuthorizeWithSeedArgs, VoteInit,
VoteStateUpdate, VoteStateVersions,
},
},
},
@ -202,19 +202,43 @@ fn initialize_account(vote_pubkey: &Pubkey, vote_init: &VoteInit) -> Instruction
)
}
pub struct CreateVoteAccountConfig<'a> {
pub space: u64,
pub with_seed: Option<(&'a Pubkey, &'a str)>,
}
impl<'a> Default for CreateVoteAccountConfig<'a> {
fn default() -> Self {
Self {
space: VoteStateVersions::vote_state_size_of(false) as u64,
with_seed: None,
}
}
}
#[deprecated(
since = "1.16.0",
note = "Please use `create_account_with_config()` instead."
)]
pub fn create_account(
from_pubkey: &Pubkey,
vote_pubkey: &Pubkey,
vote_init: &VoteInit,
lamports: u64,
) -> Vec<Instruction> {
let space = VoteState::size_of() as u64;
let create_ix =
system_instruction::create_account(from_pubkey, vote_pubkey, lamports, space, &id());
let init_ix = initialize_account(vote_pubkey, vote_init);
vec![create_ix, init_ix]
create_account_with_config(
from_pubkey,
vote_pubkey,
vote_init,
lamports,
CreateVoteAccountConfig::default(),
)
}
#[deprecated(
since = "1.16.0",
note = "Please use `create_account_with_config()` instead."
)]
pub fn create_account_with_seed(
from_pubkey: &Pubkey,
vote_pubkey: &Pubkey,
@ -223,16 +247,27 @@ pub fn create_account_with_seed(
vote_init: &VoteInit,
lamports: u64,
) -> Vec<Instruction> {
let space = VoteState::size_of() as u64;
let create_ix = system_instruction::create_account_with_seed(
create_account_with_config(
from_pubkey,
vote_pubkey,
base,
seed,
vote_init,
lamports,
space,
&id(),
);
CreateVoteAccountConfig {
with_seed: Some((base, seed)),
..CreateVoteAccountConfig::default()
},
)
}
pub fn create_account_with_config(
from_pubkey: &Pubkey,
vote_pubkey: &Pubkey,
vote_init: &VoteInit,
lamports: u64,
config: CreateVoteAccountConfig,
) -> Vec<Instruction> {
let create_ix =
system_instruction::create_account(from_pubkey, vote_pubkey, lamports, config.space, &id());
let init_ix = initialize_account(vote_pubkey, vote_init);
vec![create_ix, init_ix]
}

View File

@ -255,7 +255,7 @@ mod test {
sysvar,
vote::{
instruction as vote_instruction,
state::{Vote, VoteAuthorize, VoteInit, VoteStateUpdate},
state::{Vote, VoteAuthorize, VoteInit, VoteStateUpdate, VoteStateVersions},
},
},
};
@ -276,11 +276,15 @@ mod test {
commission,
};
let instructions = vote_instruction::create_account(
let instructions = vote_instruction::create_account_with_config(
&Pubkey::new_unique(),
&vote_pubkey,
&vote_init,
lamports,
vote_instruction::CreateVoteAccountConfig {
space: VoteStateVersions::vote_state_size_of(true) as u64,
..vote_instruction::CreateVoteAccountConfig::default()
},
);
let mut message = Message::new(&instructions, None);
assert_eq!(