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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 62 additions and 58 deletions

View File

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

View File

@ -7,17 +7,16 @@ use {
serde_derive::{Deserialize, Serialize},
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, TransactionContext},
},
std::{
cmp::Ordering,
@ -1165,15 +1164,16 @@ impl VoteState {
/// but will implicitly withdraw authorization from the previously authorized
/// key
pub fn authorize<S: std::hash::BuildHasher>(
vote_account: &KeyedAccount,
vote_account: &mut BorrowedAccount,
authorized: &Pubkey,
vote_authorize: VoteAuthorize,
signers: &HashSet<Pubkey, S>,
clock: &Clock,
feature_set: &FeatureSet,
) -> Result<(), InstructionError> {
let mut vote_state: VoteState =
State::<VoteStateVersions>::state(vote_account)?.convert_to_current();
let mut vote_state: VoteState = vote_account
.get_state::<VoteStateVersions>()?
.convert_to_current();
match vote_authorize {
VoteAuthorize::Voter => {
@ -1211,12 +1211,13 @@ pub fn authorize<S: std::hash::BuildHasher>(
/// Update the node_pubkey, requires signature of the authorized voter
pub fn update_validator_identity<S: std::hash::BuildHasher>(
vote_account: &KeyedAccount,
vote_account: &mut BorrowedAccount,
node_pubkey: &Pubkey,
signers: &HashSet<Pubkey, S>,
) -> Result<(), InstructionError> {
let mut vote_state: VoteState =
State::<VoteStateVersions>::state(vote_account)?.convert_to_current();
let mut vote_state: VoteState = vote_account
.get_state::<VoteStateVersions>()?
.convert_to_current();
// current authorized withdrawer must say "yay"
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
pub fn update_commission<S: std::hash::BuildHasher>(
vote_account: &KeyedAccount,
vote_account: &mut BorrowedAccount,
commission: u8,
signers: &HashSet<Pubkey, S>,
) -> Result<(), InstructionError> {
let mut vote_state: VoteState =
State::<VoteStateVersions>::state(vote_account)?.convert_to_current();
let mut vote_state: VoteState = vote_account
.get_state::<VoteStateVersions>()?
.convert_to_current();
// current authorized withdrawer must say "yay"
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
pub fn withdraw<S: std::hash::BuildHasher>(
vote_account: &KeyedAccount,
transaction_context: &TransactionContext,
instruction_context: &InstructionContext,
vote_account_index: usize,
lamports: u64,
to_account: &KeyedAccount,
to_account_index: usize,
signers: &HashSet<Pubkey, S>,
rent_sysvar: Option<&Rent>,
clock: Option<&Clock>,
) -> Result<(), InstructionError> {
let vote_state: VoteState =
State::<VoteStateVersions>::state(vote_account)?.convert_to_current();
let mut vote_account =
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)?;
let remaining_balance = vote_account
.lamports()?
.get_lamports()
.checked_sub(lamports)
.ok_or(InstructionError::InsufficientFunds)?;
@ -1295,18 +1302,17 @@ pub fn withdraw<S: std::hash::BuildHasher>(
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 to_account =
instruction_context.try_borrow_account(transaction_context, to_account_index)?;
to_account.checked_add_lamports(lamports)?;
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
/// that the transaction must be signed by the staker's keys
pub fn initialize_account<S: std::hash::BuildHasher>(
vote_account: &KeyedAccount,
vote_account: &mut BorrowedAccount,
vote_init: &VoteInit,
signers: &HashSet<Pubkey, S>,
clock: &Clock,
) -> Result<(), InstructionError> {
if vote_account.data_len()? != VoteState::size_of() {
if vote_account.get_data().len() != VoteState::size_of() {
return Err(InstructionError::InvalidAccountData);
}
let versioned = State::<VoteStateVersions>::state(vote_account)?;
let versioned = vote_account.get_state::<VoteStateVersions>()?;
if !versioned.is_uninitialized() {
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>(
vote_account: &KeyedAccount,
vote_account: &BorrowedAccount,
clock: &Clock,
signers: &HashSet<Pubkey, S>,
) -> Result<VoteState, InstructionError> {
let versioned = State::<VoteStateVersions>::state(vote_account)?;
let versioned = vote_account.get_state::<VoteStateVersions>()?;
if versioned.is_uninitialized() {
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>(
vote_account: &KeyedAccount,
vote_account: &mut BorrowedAccount,
slot_hashes: &[SlotHash],
clock: &Clock,
vote: &Vote,
@ -1376,7 +1382,7 @@ pub fn process_vote<S: std::hash::BuildHasher>(
}
pub fn process_vote_state_update<S: std::hash::BuildHasher>(
vote_account: &KeyedAccount,
vote_account: &mut BorrowedAccount,
slot_hashes: &[SlotHash],
clock: &Clock,
mut vote_state_update: VoteStateUpdate,