diff --git a/programs/vote/benches/process_vote.rs b/programs/vote/benches/process_vote.rs index 4ab1e56f77..61e6f759d9 100644 --- a/programs/vote/benches/process_vote.rs +++ b/programs/vote/benches/process_vote.rs @@ -7,36 +7,30 @@ use { solana_sdk::{ account::{create_account_for_test, Account, AccountSharedData}, clock::{Clock, Slot}, - feature_set::FeatureSet, hash::Hash, - instruction::{AccountMeta, Instruction}, - keyed_account::KeyedAccount, pubkey::Pubkey, slot_hashes::{SlotHashes, MAX_ENTRIES}, sysvar, - transaction_context::{InstructionAccount, TransactionContext}, + transaction_context::{InstructionAccount, TransactionAccount, TransactionContext}, }, solana_vote_program::{ vote_instruction::VoteInstruction, vote_state::{ - self, Vote, VoteInit, VoteState, VoteStateUpdate, VoteStateVersions, - MAX_LOCKOUT_HISTORY, + Vote, VoteInit, VoteState, VoteStateUpdate, VoteStateVersions, MAX_LOCKOUT_HISTORY, }, }, - std::{cell::RefCell, collections::HashSet, sync::Arc}, test::Bencher, }; -struct VoteComponents { - slot_hashes: SlotHashes, - clock: Clock, - signers: HashSet, - authority_pubkey: Pubkey, - vote_pubkey: Pubkey, - vote_account: Account, -} +fn create_accounts() -> ( + Slot, + SlotHashes, + Vec, + Vec, +) { + // vote accounts are usually almost full of votes in normal operation + let num_initial_votes = MAX_LOCKOUT_HISTORY as Slot; -fn create_components(num_initial_votes: Slot) -> VoteComponents { let clock = Clock::default(); let mut slot_hashes = SlotHashes::new(&[]); for i in 0..MAX_ENTRIES { @@ -46,7 +40,6 @@ fn create_components(num_initial_votes: Slot) -> VoteComponents { let vote_pubkey = Pubkey::new_unique(); let authority_pubkey = Pubkey::new_unique(); - let signers: HashSet = vec![authority_pubkey].into_iter().collect(); let vote_account = { let mut vote_state = VoteState::new( &VoteInit { @@ -62,7 +55,7 @@ fn create_components(num_initial_votes: Slot) -> VoteComponents { vote_state.process_next_vote_slot(next_vote_slot, 0); } let mut vote_account_data: Vec = vec![0; VoteState::size_of()]; - let versioned = VoteStateVersions::new_current(vote_state.clone()); + let versioned = VoteStateVersions::new_current(vote_state); VoteState::serialize(&versioned, &mut vote_account_data).unwrap(); Account { @@ -74,131 +67,65 @@ fn create_components(num_initial_votes: Slot) -> VoteComponents { } }; - VoteComponents { + let transaction_accounts = vec![ + (solana_vote_program::id(), AccountSharedData::default()), + (vote_pubkey, AccountSharedData::from(vote_account)), + ( + sysvar::slot_hashes::id(), + AccountSharedData::from(create_account_for_test(&slot_hashes)), + ), + ( + sysvar::clock::id(), + AccountSharedData::from(create_account_for_test(&clock)), + ), + (authority_pubkey, AccountSharedData::default()), + ]; + let mut instruction_accounts = (0..4) + .map(|index| InstructionAccount { + index_in_transaction: 1usize.saturating_add(index), + index_in_caller: 1usize.saturating_add(index), + is_signer: false, + is_writable: false, + }) + .collect::>(); + instruction_accounts[0].is_writable = true; + instruction_accounts[3].is_signer = true; + + ( + num_initial_votes, slot_hashes, - clock, - signers, - authority_pubkey, - vote_pubkey, - vote_account, - } + transaction_accounts, + instruction_accounts, + ) } -/// `feature` can be used to change vote program behavior per bench run. -fn do_bench_process_vote_instruction(bencher: &mut Bencher, feature: Option) { - // vote accounts are usually almost full of votes in normal operation - let num_initial_votes = MAX_LOCKOUT_HISTORY as Slot; - - let VoteComponents { - slot_hashes, - clock, - authority_pubkey, - vote_pubkey, - vote_account, - .. - } = create_components(num_initial_votes); - - let slot_hashes_account = create_account_for_test(&slot_hashes); - let clock_account = create_account_for_test(&clock); - let authority_account = Account::default(); - - let mut feature_set = FeatureSet::all_enabled(); - if let Some(feature) = feature { - feature_set.activate(&feature, 0); - } - let feature_set = Arc::new(feature_set); - - let num_vote_slots = 4; - let last_vote_slot = num_initial_votes - .saturating_add(num_vote_slots) - .saturating_sub(1); - let last_vote_hash = slot_hashes - .iter() - .find(|(slot, _hash)| *slot == last_vote_slot) - .unwrap() - .1; - - let vote_ix_data = bincode::serialize(&VoteInstruction::Vote(Vote::new( - (num_initial_votes..=last_vote_slot).collect(), - last_vote_hash, - ))) - .unwrap(); - - let instruction = Instruction { - program_id: solana_vote_program::id(), - data: vote_ix_data, - accounts: vec![ - AccountMeta::new(vote_pubkey, false), - AccountMeta::new_readonly(sysvar::slot_hashes::id(), false), - AccountMeta::new_readonly(sysvar::clock::id(), false), - AccountMeta::new_readonly(authority_pubkey, true), - ], - }; - - let program_indices = vec![4]; - let instruction_accounts = instruction - .accounts - .iter() - .enumerate() - .map(|(index_in_transaction, account_meta)| InstructionAccount { - index_in_transaction, - index_in_caller: index_in_transaction, - is_signer: account_meta.is_signer, - is_writable: account_meta.is_writable, - }) - .collect::>(); - +fn bench_process_vote_instruction( + bencher: &mut Bencher, + transaction_accounts: Vec, + instruction_accounts: Vec, + instruction_data: Vec, +) { bencher.iter(|| { - let mut transaction_context = TransactionContext::new( - vec![ - (vote_pubkey, AccountSharedData::from(vote_account.clone())), - ( - sysvar::slot_hashes::id(), - AccountSharedData::from(slot_hashes_account.clone()), - ), - ( - sysvar::clock::id(), - AccountSharedData::from(clock_account.clone()), - ), - ( - authority_pubkey, - AccountSharedData::from(authority_account.clone()), - ), - (solana_vote_program::id(), AccountSharedData::default()), - ], - 1, - 1, - ); - + let mut transaction_context = TransactionContext::new(transaction_accounts.clone(), 1, 1); let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); - invoke_context.feature_set = feature_set.clone(); invoke_context - .push(&instruction_accounts, &program_indices, &[]) + .push(&instruction_accounts, &[0], &instruction_data) .unwrap(); - - let first_instruction_account = 1; assert!(solana_vote_program::vote_processor::process_instruction( - first_instruction_account, - &instruction.data, + 1, + &instruction_data, &mut invoke_context ) .is_ok()); + invoke_context.pop().unwrap(); }); } -/// `feature` can be used to change vote program behavior per bench run. -fn do_bench_process_vote(bencher: &mut Bencher, feature: Option) { - // vote accounts are usually almost full of votes in normal operation - let num_initial_votes = MAX_LOCKOUT_HISTORY as Slot; - - let VoteComponents { - slot_hashes, - clock, - signers, - vote_pubkey, - vote_account, - .. - } = create_components(num_initial_votes); +#[bench] +#[ignore] +fn bench_process_vote(bencher: &mut Bencher) { + let (num_initial_votes, slot_hashes, transaction_accounts, instruction_accounts) = + create_accounts(); let num_vote_slots = 4; let last_vote_slot = num_initial_votes @@ -209,45 +136,25 @@ fn do_bench_process_vote(bencher: &mut Bencher, feature: Option) { .find(|(slot, _hash)| *slot == last_vote_slot) .unwrap() .1; - let vote = Vote::new( (num_initial_votes..=last_vote_slot).collect(), last_vote_hash, ); + let instruction_data = bincode::serialize(&VoteInstruction::Vote(vote)).unwrap(); - let mut feature_set = FeatureSet::all_enabled(); - if let Some(feature) = feature { - feature_set.activate(&feature, 0); - } - let feature_set = Arc::new(feature_set); - - bencher.iter(|| { - let vote_account = RefCell::new(AccountSharedData::from(vote_account.clone())); - let keyed_account = KeyedAccount::new(&vote_pubkey, true, &vote_account); - assert!(vote_state::process_vote( - &keyed_account, - &slot_hashes, - &clock, - &vote, - &signers, - &feature_set, - ) - .is_ok()); - }); + bench_process_vote_instruction( + bencher, + transaction_accounts, + instruction_accounts, + instruction_data, + ); } -fn do_bench_process_vote_state_update(bencher: &mut Bencher) { - // vote accounts are usually almost full of votes in normal operation - let num_initial_votes = MAX_LOCKOUT_HISTORY as Slot; - - let VoteComponents { - slot_hashes, - clock, - signers, - vote_pubkey, - vote_account, - .. - } = create_components(num_initial_votes); +#[bench] +#[ignore] +fn bench_process_vote_state_update(bencher: &mut Bencher) { + let (num_initial_votes, slot_hashes, transaction_accounts, instruction_accounts) = + create_accounts(); let num_vote_slots = MAX_LOCKOUT_HISTORY as Slot; let last_vote_slot = num_initial_votes @@ -258,46 +165,18 @@ fn do_bench_process_vote_state_update(bencher: &mut Bencher) { .find(|(slot, _hash)| *slot == last_vote_slot) .unwrap() .1; - let slots_and_lockouts: Vec<(Slot, u32)> = ((num_initial_votes.saturating_add(1)..=last_vote_slot).zip((1u32..=31).rev())).collect(); - let mut vote_state_update = VoteStateUpdate::from(slots_and_lockouts); vote_state_update.root = Some(num_initial_votes); - vote_state_update.hash = last_vote_hash; + let instruction_data = + bincode::serialize(&VoteInstruction::UpdateVoteState(vote_state_update)).unwrap(); - bencher.iter(|| { - let vote_account = RefCell::new(AccountSharedData::from(vote_account.clone())); - let keyed_account = KeyedAccount::new(&vote_pubkey, true, &vote_account); - let vote_state_update = vote_state_update.clone(); - assert!(vote_state::process_vote_state_update( - &keyed_account, - &slot_hashes, - &clock, - vote_state_update, - &signers, - ) - .is_ok()); - }); -} - -#[bench] -#[ignore] -fn bench_process_vote_instruction(bencher: &mut Bencher) { - do_bench_process_vote_instruction(bencher, None); -} - -// Benches a specific type of vote instruction -#[bench] -#[ignore] -fn bench_process_vote(bencher: &mut Bencher) { - do_bench_process_vote(bencher, None); -} - -// Benches a specific type of vote instruction -#[bench] -#[ignore] -fn bench_process_vote_state_update(bencher: &mut Bencher) { - do_bench_process_vote_state_update(bencher); + bench_process_vote_instruction( + bencher, + transaction_accounts, + instruction_accounts, + instruction_data, + ); } diff --git a/programs/vote/src/vote_processor.rs b/programs/vote/src/vote_processor.rs index 24cc8f0542..cb40f229e0 100644 --- a/programs/vote/src/vote_processor.rs +++ b/programs/vote/src/vote_processor.rs @@ -5,87 +5,157 @@ use { log::*, solana_metrics::inc_new_counter_info, solana_program_runtime::{ - invoke_context::InvokeContext, sysvar_cache::get_sysvar_with_account_check, + invoke_context::InvokeContext, sysvar_cache::get_sysvar_with_account_check2, }, - solana_sdk::{ - feature_set, - instruction::InstructionError, - keyed_account::{get_signers, keyed_account_at_index, KeyedAccount}, - program_utils::limited_deserialize, - pubkey::Pubkey, - sysvar::rent::Rent, - }, - std::collections::HashSet, + solana_sdk::{feature_set, instruction::InstructionError, program_utils::limited_deserialize}, }; +pub mod instruction_account_indices { + pub enum InitializeAccount { + VoteAccount = 0, + Rent = 1, + Clock = 2, + } + + pub enum Authorize { + VoteAccount = 0, + Clock = 1, + } + + pub enum UpdateValidatorIdentity { + VoteAccount = 0, + Node = 1, + } + + pub enum UpdateCommission { + VoteAccount = 0, + } + + pub enum Vote { + VoteAccount = 0, + SlotHashes = 1, + Clock = 2, + } + + pub enum UpdateVoteState { + VoteAccount = 0, + } + + pub enum Withdraw { + VoteAccount = 0, + Recipient = 1, + } + + pub enum AuthorizeChecked { + VoteAccount = 0, + Clock = 1, + // Ignores = 2, + Voter = 3, + } +} + pub fn process_instruction( - first_instruction_account: usize, + _first_instruction_account: usize, data: &[u8], invoke_context: &mut InvokeContext, ) -> Result<(), InstructionError> { - let keyed_accounts = invoke_context.get_keyed_accounts()?; + let transaction_context = &invoke_context.transaction_context; + let instruction_context = transaction_context.get_current_instruction_context()?; trace!("process_instruction: {:?}", data); - trace!("keyed_accounts: {:?}", keyed_accounts); - let me = &mut keyed_account_at_index(keyed_accounts, first_instruction_account)?; - if me.owner()? != id() { - return Err(InstructionError::InvalidAccountOwner); + { + let vote_account = + instruction_context.try_borrow_instruction_account(transaction_context, 0)?; + if vote_account.get_owner() != &id() { + return Err(InstructionError::InvalidAccountOwner); + } } - let signers: HashSet = get_signers(&keyed_accounts[first_instruction_account..]); + let signers = instruction_context.get_signers(transaction_context); match limited_deserialize(data)? { VoteInstruction::InitializeAccount(vote_init) => { - let rent = get_sysvar_with_account_check::rent( - keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?, + let rent = get_sysvar_with_account_check2::rent( invoke_context, + instruction_context, + instruction_account_indices::InitializeAccount::Rent as usize, )?; - verify_rent_exemption(me, &rent)?; - let clock = get_sysvar_with_account_check::clock( - keyed_account_at_index(keyed_accounts, first_instruction_account + 2)?, + { + // Verify rent exemption + let vote_account = instruction_context.try_borrow_instruction_account( + transaction_context, + instruction_account_indices::InitializeAccount::VoteAccount as usize, + )?; + if !rent.is_exempt(vote_account.get_lamports(), vote_account.get_data().len()) { + return Err(InstructionError::InsufficientFunds); + } + } + let _clock = get_sysvar_with_account_check2::clock( invoke_context, + instruction_context, + instruction_account_indices::InitializeAccount::Clock as usize, )?; - vote_state::initialize_account(me, &vote_init, &signers, &clock) - } - VoteInstruction::Authorize(voter_pubkey, vote_authorize) => { - let clock = get_sysvar_with_account_check::clock( - keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?, + vote_state::initialize_account( invoke_context, - )?; - vote_state::authorize( - me, - &voter_pubkey, - vote_authorize, + instruction_context, &signers, - &clock, - &invoke_context.feature_set, + instruction_account_indices::InitializeAccount::VoteAccount as usize, + &vote_init, ) } - VoteInstruction::UpdateValidatorIdentity => vote_state::update_validator_identity( - me, - keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?.unsigned_key(), - &signers, - ), - VoteInstruction::UpdateCommission(commission) => { - vote_state::update_commission(me, commission, &signers) + VoteInstruction::Authorize(voter_pubkey, vote_authorize) => { + let _clock = get_sysvar_with_account_check2::clock( + invoke_context, + instruction_context, + instruction_account_indices::Authorize::Clock as usize, + )?; + vote_state::authorize( + invoke_context, + instruction_context, + &signers, + instruction_account_indices::Authorize::VoteAccount as usize, + &voter_pubkey, + vote_authorize, + ) } + VoteInstruction::UpdateValidatorIdentity => { + instruction_context.check_number_of_instruction_accounts(2)?; + vote_state::update_validator_identity( + invoke_context, + instruction_context, + &signers, + instruction_account_indices::UpdateValidatorIdentity::VoteAccount as usize, + instruction_context.get_instruction_account_key( + transaction_context, + instruction_account_indices::UpdateValidatorIdentity::Node as usize, + )?, + ) + } + VoteInstruction::UpdateCommission(commission) => vote_state::update_commission( + invoke_context, + instruction_context, + &signers, + instruction_account_indices::UpdateCommission::VoteAccount as usize, + commission, + ), VoteInstruction::Vote(vote) | VoteInstruction::VoteSwitch(vote, _) => { inc_new_counter_info!("vote-native", 1); - let slot_hashes = get_sysvar_with_account_check::slot_hashes( - keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?, + let _slot_hashes = get_sysvar_with_account_check2::slot_hashes( invoke_context, + instruction_context, + instruction_account_indices::Vote::SlotHashes as usize, )?; - let clock = get_sysvar_with_account_check::clock( - keyed_account_at_index(keyed_accounts, first_instruction_account + 2)?, + let _clock = get_sysvar_with_account_check2::clock( invoke_context, + instruction_context, + instruction_account_indices::Vote::Clock as usize, )?; vote_state::process_vote( - me, - &slot_hashes, - &clock, - &vote, + invoke_context, + instruction_context, &signers, - &invoke_context.feature_set, + instruction_account_indices::Vote::VoteAccount as usize, + &vote, ) } VoteInstruction::UpdateVoteState(vote_state_update) @@ -95,47 +165,26 @@ pub fn process_instruction( .is_active(&feature_set::allow_votes_to_directly_update_vote_state::id()) { inc_new_counter_info!("vote-state-native", 1); - let sysvar_cache = invoke_context.get_sysvar_cache(); - let slot_hashes = sysvar_cache.get_slot_hashes()?; - let clock = sysvar_cache.get_clock()?; vote_state::process_vote_state_update( - me, - slot_hashes.slot_hashes(), - &clock, - vote_state_update, + invoke_context, + instruction_context, &signers, + instruction_account_indices::UpdateVoteState::VoteAccount as usize, + vote_state_update, ) } else { Err(InstructionError::InvalidInstructionData) } } VoteInstruction::Withdraw(lamports) => { - let to = keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?; - let rent_sysvar = if invoke_context - .feature_set - .is_active(&feature_set::reject_non_rent_exempt_vote_withdraws::id()) - { - Some(invoke_context.get_sysvar_cache().get_rent()?) - } else { - None - }; - - let clock_if_feature_active = if invoke_context - .feature_set - .is_active(&feature_set::reject_vote_account_close_unless_zero_credit_epoch::id()) - { - Some(invoke_context.get_sysvar_cache().get_clock()?) - } else { - None - }; - + instruction_context.check_number_of_instruction_accounts(2)?; vote_state::withdraw( - me, - lamports, - to, + invoke_context, + instruction_context, &signers, - rent_sysvar.as_deref(), - clock_if_feature_active.as_deref(), + instruction_account_indices::Withdraw::VoteAccount as usize, + instruction_account_indices::Withdraw::Recipient as usize, + lamports, ) } VoteInstruction::AuthorizeChecked(vote_authorize) => { @@ -143,21 +192,28 @@ pub fn process_instruction( .feature_set .is_active(&feature_set::vote_stake_checked_instructions::id()) { - let voter_pubkey = - &keyed_account_at_index(keyed_accounts, first_instruction_account + 3)? - .signer_key() - .ok_or(InstructionError::MissingRequiredSignature)?; - let clock = get_sysvar_with_account_check::clock( - keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?, + instruction_context.check_number_of_instruction_accounts(4)?; + if !instruction_context.is_signer( + instruction_context.get_number_of_program_accounts() + + instruction_account_indices::AuthorizeChecked::Voter as usize, + )? { + return Err(InstructionError::MissingRequiredSignature); + } + let _clock = get_sysvar_with_account_check2::clock( invoke_context, + instruction_context, + instruction_account_indices::AuthorizeChecked::Clock as usize, )?; vote_state::authorize( - me, - voter_pubkey, - vote_authorize, + invoke_context, + instruction_context, &signers, - &clock, - &invoke_context.feature_set, + instruction_account_indices::AuthorizeChecked::VoteAccount as usize, + instruction_context.get_instruction_account_key( + transaction_context, + instruction_account_indices::AuthorizeChecked::Voter as usize, + )?, + vote_authorize, ) } else { Err(InstructionError::InvalidInstructionData) @@ -166,17 +222,6 @@ pub fn process_instruction( } } -fn verify_rent_exemption( - keyed_account: &KeyedAccount, - rent: &Rent, -) -> Result<(), InstructionError> { - if !rent.is_exempt(keyed_account.lamports()?, keyed_account.data_len()?) { - Err(InstructionError::InsufficientFunds) - } else { - Ok(()) - } -} - #[cfg(test)] mod tests { use { @@ -201,9 +246,10 @@ mod tests { feature_set::FeatureSet, hash::Hash, instruction::{AccountMeta, Instruction}, - sysvar::{self, clock::Clock, slot_hashes::SlotHashes}, + pubkey::Pubkey, + sysvar::{self, clock::Clock, rent::Rent, slot_hashes::SlotHashes}, }, - std::str::FromStr, + std::{collections::HashSet, str::FromStr}, }; fn create_default_account() -> AccountSharedData { diff --git a/programs/vote/src/vote_state/mod.rs b/programs/vote/src/vote_state/mod.rs index ba9a1a1a70..4f3a812bf9 100644 --- a/programs/vote/src/vote_state/mod.rs +++ b/programs/vote/src/vote_state/mod.rs @@ -5,19 +5,19 @@ use { bincode::{deserialize, serialize_into, serialized_size, ErrorKind}, log::*, serde_derive::{Deserialize, Serialize}, + solana_program_runtime::invoke_context::InvokeContext, solana_sdk::{ account::{AccountSharedData, ReadableAccount, WritableAccount}, - account_utils::State, clock::{Epoch, Slot, UnixTimestamp}, epoch_schedule::MAX_LEADER_SCHEDULE_EPOCH_OFFSET, feature_set::{self, filter_votes_outside_slot_hashes, FeatureSet}, hash::Hash, instruction::InstructionError, - keyed_account::KeyedAccount, pubkey::Pubkey, rent::Rent, slot_hashes::SlotHash, sysvar::clock::Clock, + transaction_context::{BorrowedAccount, InstructionContext}, }, std::{ cmp::Ordering, @@ -1165,19 +1165,24 @@ impl VoteState { /// but will implicitly withdraw authorization from the previously authorized /// key pub fn authorize( - vote_account: &KeyedAccount, + invoke_context: &InvokeContext, + instruction_context: &InstructionContext, + signers: &HashSet, + vote_account_index: usize, authorized: &Pubkey, vote_authorize: VoteAuthorize, - signers: &HashSet, - clock: &Clock, - feature_set: &FeatureSet, ) -> Result<(), InstructionError> { - let mut vote_state: VoteState = - State::::state(vote_account)?.convert_to_current(); + let mut vote_account = instruction_context + .try_borrow_instruction_account(invoke_context.transaction_context, vote_account_index)?; + let clock = invoke_context.get_sysvar_cache().get_clock()?; + let mut vote_state: VoteState = vote_account + .get_state::()? + .convert_to_current(); match vote_authorize { VoteAuthorize::Voter => { - let authorized_withdrawer_signer = if feature_set + let authorized_withdrawer_signer = if invoke_context + .feature_set .is_active(&feature_set::vote_withdraw_authority_may_change_authorized_voter::id()) { verify_authorized_signer(&vote_state.authorized_withdrawer, signers).is_ok() @@ -1211,12 +1216,17 @@ pub fn authorize( /// Update the node_pubkey, requires signature of the authorized voter pub fn update_validator_identity( - vote_account: &KeyedAccount, - node_pubkey: &Pubkey, + invoke_context: &InvokeContext, + instruction_context: &InstructionContext, signers: &HashSet, + vote_account_index: usize, + node_pubkey: &Pubkey, ) -> Result<(), InstructionError> { - let mut vote_state: VoteState = - State::::state(vote_account)?.convert_to_current(); + let mut vote_account = instruction_context + .try_borrow_instruction_account(invoke_context.transaction_context, vote_account_index)?; + let mut vote_state: VoteState = vote_account + .get_state::()? + .convert_to_current(); // current authorized withdrawer must say "yay" verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?; @@ -1231,12 +1241,17 @@ pub fn update_validator_identity( /// Update the vote account's commission pub fn update_commission( - vote_account: &KeyedAccount, - commission: u8, + invoke_context: &InvokeContext, + instruction_context: &InstructionContext, signers: &HashSet, + vote_account_index: usize, + commission: u8, ) -> Result<(), InstructionError> { - let mut vote_state: VoteState = - State::::state(vote_account)?.convert_to_current(); + let mut vote_account = instruction_context + .try_borrow_instruction_account(invoke_context.transaction_context, vote_account_index)?; + let mut vote_state: VoteState = vote_account + .get_state::()? + .convert_to_current(); // current authorized withdrawer must say "yay" verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?; @@ -1259,20 +1274,42 @@ fn verify_authorized_signer( /// Withdraw funds from the vote account pub fn withdraw( - vote_account: &KeyedAccount, - lamports: u64, - to_account: &KeyedAccount, + invoke_context: &InvokeContext, + instruction_context: &InstructionContext, signers: &HashSet, - rent_sysvar: Option<&Rent>, - clock: Option<&Clock>, + vote_account_index: usize, + recipient_account_index: usize, + lamports: u64, ) -> Result<(), InstructionError> { - let vote_state: VoteState = - State::::state(vote_account)?.convert_to_current(); + let mut vote_account = instruction_context + .try_borrow_instruction_account(invoke_context.transaction_context, vote_account_index)?; + + let rent_sysvar = if invoke_context + .feature_set + .is_active(&feature_set::reject_non_rent_exempt_vote_withdraws::id()) + { + Some(invoke_context.get_sysvar_cache().get_rent()?) + } else { + None + }; + + let clock = if invoke_context + .feature_set + .is_active(&feature_set::reject_vote_account_close_unless_zero_credit_epoch::id()) + { + Some(invoke_context.get_sysvar_cache().get_clock()?) + } else { + None + }; + + let vote_state: VoteState = vote_account + .get_state::()? + .convert_to_current(); verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?; let remaining_balance = vote_account - .lamports()? + .get_lamports() .checked_sub(lamports) .ok_or(InstructionError::InsufficientFunds)?; @@ -1295,18 +1332,19 @@ pub fn withdraw( vote_account.set_state(&VoteStateVersions::new_current(VoteState::default()))?; } } else if let Some(rent_sysvar) = rent_sysvar { - let min_rent_exempt_balance = rent_sysvar.minimum_balance(vote_account.data_len()?); + let min_rent_exempt_balance = rent_sysvar.minimum_balance(vote_account.get_data().len()); if remaining_balance < min_rent_exempt_balance { return Err(InstructionError::InsufficientFunds); } } - vote_account - .try_account_ref_mut()? - .checked_sub_lamports(lamports)?; - to_account - .try_account_ref_mut()? - .checked_add_lamports(lamports)?; + vote_account.checked_sub_lamports(lamports)?; + drop(vote_account); + let mut recipient_account = instruction_context.try_borrow_instruction_account( + invoke_context.transaction_context, + recipient_account_index, + )?; + recipient_account.checked_add_lamports(lamports)?; Ok(()) } @@ -1314,15 +1352,19 @@ pub fn withdraw( /// Assumes that the account is being init as part of a account creation or balance transfer and /// that the transaction must be signed by the staker's keys pub fn initialize_account( - vote_account: &KeyedAccount, - vote_init: &VoteInit, + invoke_context: &InvokeContext, + instruction_context: &InstructionContext, signers: &HashSet, - clock: &Clock, + vote_account_index: usize, + vote_init: &VoteInit, ) -> Result<(), InstructionError> { - if vote_account.data_len()? != VoteState::size_of() { + let mut vote_account = instruction_context + .try_borrow_instruction_account(invoke_context.transaction_context, vote_account_index)?; + let clock = invoke_context.get_sysvar_cache().get_clock()?; + if vote_account.get_data().len() != VoteState::size_of() { return Err(InstructionError::InvalidAccountData); } - let versioned = State::::state(vote_account)?; + let versioned = vote_account.get_state::()?; if !versioned.is_uninitialized() { return Err(InstructionError::AccountAlreadyInitialized); @@ -1332,16 +1374,16 @@ pub fn initialize_account( verify_authorized_signer(&vote_init.node_pubkey, signers)?; vote_account.set_state(&VoteStateVersions::new_current(VoteState::new( - vote_init, clock, + vote_init, &clock, ))) } fn verify_and_get_vote_state( - vote_account: &KeyedAccount, + vote_account: &BorrowedAccount, clock: &Clock, signers: &HashSet, ) -> Result { - let versioned = State::::state(vote_account)?; + let versioned = vote_account.get_state::()?; if versioned.is_uninitialized() { return Err(InstructionError::UninitializedAccount); @@ -1355,16 +1397,25 @@ fn verify_and_get_vote_state( } pub fn process_vote( - vote_account: &KeyedAccount, - slot_hashes: &[SlotHash], - clock: &Clock, - vote: &Vote, + invoke_context: &InvokeContext, + instruction_context: &InstructionContext, signers: &HashSet, - feature_set: &FeatureSet, + vote_account_index: usize, + vote: &Vote, ) -> Result<(), InstructionError> { - let mut vote_state = verify_and_get_vote_state(vote_account, clock, signers)?; + let mut vote_account = instruction_context + .try_borrow_instruction_account(invoke_context.transaction_context, vote_account_index)?; + let sysvar_cache = invoke_context.get_sysvar_cache(); + let slot_hashes = sysvar_cache.get_slot_hashes()?; + let clock = sysvar_cache.get_clock()?; + let mut vote_state = verify_and_get_vote_state(&vote_account, &clock, signers)?; - vote_state.process_vote(vote, slot_hashes, clock.epoch, Some(feature_set))?; + vote_state.process_vote( + vote, + slot_hashes.slot_hashes(), + clock.epoch, + Some(&invoke_context.feature_set), + )?; if let Some(timestamp) = vote.timestamp { vote.slots .iter() @@ -1376,14 +1427,19 @@ pub fn process_vote( } pub fn process_vote_state_update( - vote_account: &KeyedAccount, - slot_hashes: &[SlotHash], - clock: &Clock, - mut vote_state_update: VoteStateUpdate, + invoke_context: &InvokeContext, + instruction_context: &InstructionContext, signers: &HashSet, + vote_account_index: usize, + mut vote_state_update: VoteStateUpdate, ) -> Result<(), InstructionError> { - let mut vote_state = verify_and_get_vote_state(vote_account, clock, signers)?; - vote_state.check_update_vote_state_slots_are_valid(&mut vote_state_update, slot_hashes)?; + let mut vote_account = instruction_context + .try_borrow_instruction_account(invoke_context.transaction_context, vote_account_index)?; + let sysvar_cache = invoke_context.get_sysvar_cache(); + let slot_hashes = sysvar_cache.get_slot_hashes()?; + let clock = sysvar_cache.get_clock()?; + let mut vote_state = verify_and_get_vote_state(&vote_account, &clock, signers)?; + vote_state.check_update_vote_state_slots_are_valid(&mut vote_state_update, &slot_hashes)?; vote_state.process_new_vote_state( vote_state_update.lockouts, vote_state_update.root,