Remove KeyedAccount in builtin program "vote" (#24189)

* Uses transaction_context.get_key_of_account_at_index() in vote.

* Inline keyed_account_at_index() in all instructions of vote
which have more than one KeyedAccount parameter,
because these could cause a borrow collision.

* Replaces KeyedAccount by BorrowedAccount in vote.
This commit is contained in:
Alexander Meißner
2022-04-08 20:40:50 +02:00
committed by GitHub
parent fad9bd0538
commit 2e5042d8bd
2 changed files with 62 additions and 58 deletions

View File

@ -7,10 +7,7 @@ use {
solana_program_runtime::{ solana_program_runtime::{
invoke_context::InvokeContext, sysvar_cache::get_sysvar_with_account_check, invoke_context::InvokeContext, sysvar_cache::get_sysvar_with_account_check,
}, },
solana_sdk::{ solana_sdk::{feature_set, instruction::InstructionError, program_utils::limited_deserialize},
feature_set, instruction::InstructionError, keyed_account::keyed_account_at_index,
program_utils::limited_deserialize,
},
}; };
pub fn process_instruction( pub fn process_instruction(
@ -20,13 +17,12 @@ pub fn process_instruction(
let transaction_context = &invoke_context.transaction_context; let transaction_context = &invoke_context.transaction_context;
let instruction_context = transaction_context.get_current_instruction_context()?; let instruction_context = transaction_context.get_current_instruction_context()?;
let data = instruction_context.get_instruction_data(); let data = instruction_context.get_instruction_data();
let keyed_accounts = invoke_context.get_keyed_accounts()?;
trace!("process_instruction: {:?}", data); trace!("process_instruction: {:?}", data);
trace!("keyed_accounts: {:?}", keyed_accounts);
let me = &mut keyed_account_at_index(keyed_accounts, first_instruction_account)?; let mut me =
if me.owner()? != id() { instruction_context.try_borrow_account(transaction_context, first_instruction_account)?;
if *me.get_owner() != id() {
return Err(InstructionError::InvalidAccountOwner); return Err(InstructionError::InvalidAccountOwner);
} }
@ -34,18 +30,18 @@ pub fn process_instruction(
match limited_deserialize(data)? { match limited_deserialize(data)? {
VoteInstruction::InitializeAccount(vote_init) => { VoteInstruction::InitializeAccount(vote_init) => {
let rent = get_sysvar_with_account_check::rent(invoke_context, instruction_context, 1)?; let rent = get_sysvar_with_account_check::rent(invoke_context, instruction_context, 1)?;
if !rent.is_exempt(me.lamports()?, me.data_len()?) { if !rent.is_exempt(me.get_lamports(), me.get_data().len()) {
return Err(InstructionError::InsufficientFunds); return Err(InstructionError::InsufficientFunds);
} }
let clock = let clock =
get_sysvar_with_account_check::clock(invoke_context, instruction_context, 2)?; get_sysvar_with_account_check::clock(invoke_context, instruction_context, 2)?;
vote_state::initialize_account(me, &vote_init, &signers, &clock) vote_state::initialize_account(&mut me, &vote_init, &signers, &clock)
} }
VoteInstruction::Authorize(voter_pubkey, vote_authorize) => { VoteInstruction::Authorize(voter_pubkey, vote_authorize) => {
let clock = let clock =
get_sysvar_with_account_check::clock(invoke_context, instruction_context, 1)?; get_sysvar_with_account_check::clock(invoke_context, instruction_context, 1)?;
vote_state::authorize( vote_state::authorize(
me, &mut me,
&voter_pubkey, &voter_pubkey,
vote_authorize, vote_authorize,
&signers, &signers,
@ -55,15 +51,13 @@ pub fn process_instruction(
} }
VoteInstruction::UpdateValidatorIdentity => { VoteInstruction::UpdateValidatorIdentity => {
instruction_context.check_number_of_instruction_accounts(2)?; instruction_context.check_number_of_instruction_accounts(2)?;
vote_state::update_validator_identity( let node_pubkey = transaction_context.get_key_of_account_at_index(
me, instruction_context.get_index_in_transaction(first_instruction_account + 1)?,
keyed_account_at_index(keyed_accounts, first_instruction_account + 1)? )?;
.unsigned_key(), vote_state::update_validator_identity(&mut me, node_pubkey, &signers)
&signers,
)
} }
VoteInstruction::UpdateCommission(commission) => { VoteInstruction::UpdateCommission(commission) => {
vote_state::update_commission(me, commission, &signers) vote_state::update_commission(&mut me, commission, &signers)
} }
VoteInstruction::Vote(vote) | VoteInstruction::VoteSwitch(vote, _) => { VoteInstruction::Vote(vote) | VoteInstruction::VoteSwitch(vote, _) => {
inc_new_counter_info!("vote-native", 1); inc_new_counter_info!("vote-native", 1);
@ -72,7 +66,7 @@ pub fn process_instruction(
let clock = let clock =
get_sysvar_with_account_check::clock(invoke_context, instruction_context, 2)?; get_sysvar_with_account_check::clock(invoke_context, instruction_context, 2)?;
vote_state::process_vote( vote_state::process_vote(
me, &mut me,
&slot_hashes, &slot_hashes,
&clock, &clock,
&vote, &vote,
@ -91,7 +85,7 @@ pub fn process_instruction(
let slot_hashes = sysvar_cache.get_slot_hashes()?; let slot_hashes = sysvar_cache.get_slot_hashes()?;
let clock = sysvar_cache.get_clock()?; let clock = sysvar_cache.get_clock()?;
vote_state::process_vote_state_update( vote_state::process_vote_state_update(
me, &mut me,
slot_hashes.slot_hashes(), slot_hashes.slot_hashes(),
&clock, &clock,
vote_state_update, vote_state_update,
@ -103,7 +97,6 @@ pub fn process_instruction(
} }
VoteInstruction::Withdraw(lamports) => { VoteInstruction::Withdraw(lamports) => {
instruction_context.check_number_of_instruction_accounts(2)?; instruction_context.check_number_of_instruction_accounts(2)?;
let to = keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?;
let rent_sysvar = if invoke_context let rent_sysvar = if invoke_context
.feature_set .feature_set
.is_active(&feature_set::reject_non_rent_exempt_vote_withdraws::id()) .is_active(&feature_set::reject_non_rent_exempt_vote_withdraws::id())
@ -122,10 +115,13 @@ pub fn process_instruction(
None None
}; };
drop(me);
vote_state::withdraw( vote_state::withdraw(
me, transaction_context,
instruction_context,
first_instruction_account,
lamports, lamports,
to, first_instruction_account + 1,
&signers, &signers,
rent_sysvar.as_deref(), rent_sysvar.as_deref(),
clock_if_feature_active.as_deref(), clock_if_feature_active.as_deref(),
@ -137,14 +133,16 @@ pub fn process_instruction(
.is_active(&feature_set::vote_stake_checked_instructions::id()) .is_active(&feature_set::vote_stake_checked_instructions::id())
{ {
instruction_context.check_number_of_instruction_accounts(4)?; instruction_context.check_number_of_instruction_accounts(4)?;
let voter_pubkey = let voter_pubkey = transaction_context.get_key_of_account_at_index(
&keyed_account_at_index(keyed_accounts, first_instruction_account + 3)? instruction_context.get_index_in_transaction(first_instruction_account + 3)?,
.signer_key() )?;
.ok_or(InstructionError::MissingRequiredSignature)?; if !instruction_context.is_signer(first_instruction_account + 3)? {
return Err(InstructionError::MissingRequiredSignature);
}
let clock = let clock =
get_sysvar_with_account_check::clock(invoke_context, instruction_context, 1)?; get_sysvar_with_account_check::clock(invoke_context, instruction_context, 1)?;
vote_state::authorize( vote_state::authorize(
me, &mut me,
voter_pubkey, voter_pubkey,
vote_authorize, vote_authorize,
&signers, &signers,

View File

@ -7,17 +7,16 @@ use {
serde_derive::{Deserialize, Serialize}, serde_derive::{Deserialize, Serialize},
solana_sdk::{ solana_sdk::{
account::{AccountSharedData, ReadableAccount, WritableAccount}, account::{AccountSharedData, ReadableAccount, WritableAccount},
account_utils::State,
clock::{Epoch, Slot, UnixTimestamp}, clock::{Epoch, Slot, UnixTimestamp},
epoch_schedule::MAX_LEADER_SCHEDULE_EPOCH_OFFSET, epoch_schedule::MAX_LEADER_SCHEDULE_EPOCH_OFFSET,
feature_set::{self, filter_votes_outside_slot_hashes, FeatureSet}, feature_set::{self, filter_votes_outside_slot_hashes, FeatureSet},
hash::Hash, hash::Hash,
instruction::InstructionError, instruction::InstructionError,
keyed_account::KeyedAccount,
pubkey::Pubkey, pubkey::Pubkey,
rent::Rent, rent::Rent,
slot_hashes::SlotHash, slot_hashes::SlotHash,
sysvar::clock::Clock, sysvar::clock::Clock,
transaction_context::{BorrowedAccount, InstructionContext, TransactionContext},
}, },
std::{ std::{
cmp::Ordering, cmp::Ordering,
@ -1165,15 +1164,16 @@ impl VoteState {
/// but will implicitly withdraw authorization from the previously authorized /// but will implicitly withdraw authorization from the previously authorized
/// key /// key
pub fn authorize<S: std::hash::BuildHasher>( pub fn authorize<S: std::hash::BuildHasher>(
vote_account: &KeyedAccount, vote_account: &mut BorrowedAccount,
authorized: &Pubkey, authorized: &Pubkey,
vote_authorize: VoteAuthorize, vote_authorize: VoteAuthorize,
signers: &HashSet<Pubkey, S>, signers: &HashSet<Pubkey, S>,
clock: &Clock, clock: &Clock,
feature_set: &FeatureSet, feature_set: &FeatureSet,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
let mut vote_state: VoteState = let mut vote_state: VoteState = vote_account
State::<VoteStateVersions>::state(vote_account)?.convert_to_current(); .get_state::<VoteStateVersions>()?
.convert_to_current();
match vote_authorize { match vote_authorize {
VoteAuthorize::Voter => { VoteAuthorize::Voter => {
@ -1211,12 +1211,13 @@ pub fn authorize<S: std::hash::BuildHasher>(
/// Update the node_pubkey, requires signature of the authorized voter /// Update the node_pubkey, requires signature of the authorized voter
pub fn update_validator_identity<S: std::hash::BuildHasher>( pub fn update_validator_identity<S: std::hash::BuildHasher>(
vote_account: &KeyedAccount, vote_account: &mut BorrowedAccount,
node_pubkey: &Pubkey, node_pubkey: &Pubkey,
signers: &HashSet<Pubkey, S>, signers: &HashSet<Pubkey, S>,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
let mut vote_state: VoteState = let mut vote_state: VoteState = vote_account
State::<VoteStateVersions>::state(vote_account)?.convert_to_current(); .get_state::<VoteStateVersions>()?
.convert_to_current();
// current authorized withdrawer must say "yay" // current authorized withdrawer must say "yay"
verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?; verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
@ -1231,12 +1232,13 @@ pub fn update_validator_identity<S: std::hash::BuildHasher>(
/// Update the vote account's commission /// Update the vote account's commission
pub fn update_commission<S: std::hash::BuildHasher>( pub fn update_commission<S: std::hash::BuildHasher>(
vote_account: &KeyedAccount, vote_account: &mut BorrowedAccount,
commission: u8, commission: u8,
signers: &HashSet<Pubkey, S>, signers: &HashSet<Pubkey, S>,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
let mut vote_state: VoteState = let mut vote_state: VoteState = vote_account
State::<VoteStateVersions>::state(vote_account)?.convert_to_current(); .get_state::<VoteStateVersions>()?
.convert_to_current();
// current authorized withdrawer must say "yay" // current authorized withdrawer must say "yay"
verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?; verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
@ -1259,20 +1261,25 @@ fn verify_authorized_signer<S: std::hash::BuildHasher>(
/// Withdraw funds from the vote account /// Withdraw funds from the vote account
pub fn withdraw<S: std::hash::BuildHasher>( pub fn withdraw<S: std::hash::BuildHasher>(
vote_account: &KeyedAccount, transaction_context: &TransactionContext,
instruction_context: &InstructionContext,
vote_account_index: usize,
lamports: u64, lamports: u64,
to_account: &KeyedAccount, to_account_index: usize,
signers: &HashSet<Pubkey, S>, signers: &HashSet<Pubkey, S>,
rent_sysvar: Option<&Rent>, rent_sysvar: Option<&Rent>,
clock: Option<&Clock>, clock: Option<&Clock>,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
let vote_state: VoteState = let mut vote_account =
State::<VoteStateVersions>::state(vote_account)?.convert_to_current(); instruction_context.try_borrow_account(transaction_context, vote_account_index)?;
let vote_state: VoteState = vote_account
.get_state::<VoteStateVersions>()?
.convert_to_current();
verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?; verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
let remaining_balance = vote_account let remaining_balance = vote_account
.lamports()? .get_lamports()
.checked_sub(lamports) .checked_sub(lamports)
.ok_or(InstructionError::InsufficientFunds)?; .ok_or(InstructionError::InsufficientFunds)?;
@ -1295,18 +1302,17 @@ pub fn withdraw<S: std::hash::BuildHasher>(
vote_account.set_state(&VoteStateVersions::new_current(VoteState::default()))?; vote_account.set_state(&VoteStateVersions::new_current(VoteState::default()))?;
} }
} else if let Some(rent_sysvar) = rent_sysvar { } 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 { if remaining_balance < min_rent_exempt_balance {
return Err(InstructionError::InsufficientFunds); return Err(InstructionError::InsufficientFunds);
} }
} }
vote_account vote_account.checked_sub_lamports(lamports)?;
.try_account_ref_mut()? drop(vote_account);
.checked_sub_lamports(lamports)?; let mut to_account =
to_account instruction_context.try_borrow_account(transaction_context, to_account_index)?;
.try_account_ref_mut()? to_account.checked_add_lamports(lamports)?;
.checked_add_lamports(lamports)?;
Ok(()) Ok(())
} }
@ -1314,15 +1320,15 @@ pub fn withdraw<S: std::hash::BuildHasher>(
/// Assumes that the account is being init as part of a account creation or balance transfer and /// 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 /// that the transaction must be signed by the staker's keys
pub fn initialize_account<S: std::hash::BuildHasher>( pub fn initialize_account<S: std::hash::BuildHasher>(
vote_account: &KeyedAccount, vote_account: &mut BorrowedAccount,
vote_init: &VoteInit, vote_init: &VoteInit,
signers: &HashSet<Pubkey, S>, signers: &HashSet<Pubkey, S>,
clock: &Clock, clock: &Clock,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
if vote_account.data_len()? != VoteState::size_of() { if vote_account.get_data().len() != VoteState::size_of() {
return Err(InstructionError::InvalidAccountData); return Err(InstructionError::InvalidAccountData);
} }
let versioned = State::<VoteStateVersions>::state(vote_account)?; let versioned = vote_account.get_state::<VoteStateVersions>()?;
if !versioned.is_uninitialized() { if !versioned.is_uninitialized() {
return Err(InstructionError::AccountAlreadyInitialized); return Err(InstructionError::AccountAlreadyInitialized);
@ -1337,11 +1343,11 @@ pub fn initialize_account<S: std::hash::BuildHasher>(
} }
fn verify_and_get_vote_state<S: std::hash::BuildHasher>( fn verify_and_get_vote_state<S: std::hash::BuildHasher>(
vote_account: &KeyedAccount, vote_account: &BorrowedAccount,
clock: &Clock, clock: &Clock,
signers: &HashSet<Pubkey, S>, signers: &HashSet<Pubkey, S>,
) -> Result<VoteState, InstructionError> { ) -> Result<VoteState, InstructionError> {
let versioned = State::<VoteStateVersions>::state(vote_account)?; let versioned = vote_account.get_state::<VoteStateVersions>()?;
if versioned.is_uninitialized() { if versioned.is_uninitialized() {
return Err(InstructionError::UninitializedAccount); return Err(InstructionError::UninitializedAccount);
@ -1355,7 +1361,7 @@ fn verify_and_get_vote_state<S: std::hash::BuildHasher>(
} }
pub fn process_vote<S: std::hash::BuildHasher>( pub fn process_vote<S: std::hash::BuildHasher>(
vote_account: &KeyedAccount, vote_account: &mut BorrowedAccount,
slot_hashes: &[SlotHash], slot_hashes: &[SlotHash],
clock: &Clock, clock: &Clock,
vote: &Vote, vote: &Vote,
@ -1376,7 +1382,7 @@ pub fn process_vote<S: std::hash::BuildHasher>(
} }
pub fn process_vote_state_update<S: std::hash::BuildHasher>( pub fn process_vote_state_update<S: std::hash::BuildHasher>(
vote_account: &KeyedAccount, vote_account: &mut BorrowedAccount,
slot_hashes: &[SlotHash], slot_hashes: &[SlotHash],
clock: &Clock, clock: &Clock,
mut vote_state_update: VoteStateUpdate, mut vote_state_update: VoteStateUpdate,