Replaces `KeyedAccount` by `BorrowedAccount` in vote processor (#23348)

* Use instruction_account_indices, get_sysvar_with_account_check2 and instruction_context.get_signers in vote processor.

* Replaces KeyedAccount by BorrowedAccount in vote processor.

* Removes KeyedAccount from benches in vote processor.
This commit is contained in:
Alexander Meißner 2022-02-25 17:22:54 +01:00 committed by GitHub
parent 533eca3b4c
commit e2fa6a0f7a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 336 additions and 355 deletions

View File

@ -7,36 +7,30 @@ use {
solana_sdk::{ solana_sdk::{
account::{create_account_for_test, Account, AccountSharedData}, account::{create_account_for_test, Account, AccountSharedData},
clock::{Clock, Slot}, clock::{Clock, Slot},
feature_set::FeatureSet,
hash::Hash, hash::Hash,
instruction::{AccountMeta, Instruction},
keyed_account::KeyedAccount,
pubkey::Pubkey, pubkey::Pubkey,
slot_hashes::{SlotHashes, MAX_ENTRIES}, slot_hashes::{SlotHashes, MAX_ENTRIES},
sysvar, sysvar,
transaction_context::{InstructionAccount, TransactionContext}, transaction_context::{InstructionAccount, TransactionAccount, TransactionContext},
}, },
solana_vote_program::{ solana_vote_program::{
vote_instruction::VoteInstruction, vote_instruction::VoteInstruction,
vote_state::{ vote_state::{
self, Vote, VoteInit, VoteState, VoteStateUpdate, VoteStateVersions, Vote, VoteInit, VoteState, VoteStateUpdate, VoteStateVersions, MAX_LOCKOUT_HISTORY,
MAX_LOCKOUT_HISTORY,
}, },
}, },
std::{cell::RefCell, collections::HashSet, sync::Arc},
test::Bencher, test::Bencher,
}; };
struct VoteComponents { fn create_accounts() -> (
slot_hashes: SlotHashes, Slot,
clock: Clock, SlotHashes,
signers: HashSet<Pubkey>, Vec<TransactionAccount>,
authority_pubkey: Pubkey, Vec<InstructionAccount>,
vote_pubkey: Pubkey, ) {
vote_account: Account, // 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 clock = Clock::default();
let mut slot_hashes = SlotHashes::new(&[]); let mut slot_hashes = SlotHashes::new(&[]);
for i in 0..MAX_ENTRIES { 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 vote_pubkey = Pubkey::new_unique();
let authority_pubkey = Pubkey::new_unique(); let authority_pubkey = Pubkey::new_unique();
let signers: HashSet<Pubkey> = vec![authority_pubkey].into_iter().collect();
let vote_account = { let vote_account = {
let mut vote_state = VoteState::new( let mut vote_state = VoteState::new(
&VoteInit { &VoteInit {
@ -62,7 +55,7 @@ fn create_components(num_initial_votes: Slot) -> VoteComponents {
vote_state.process_next_vote_slot(next_vote_slot, 0); vote_state.process_next_vote_slot(next_vote_slot, 0);
} }
let mut vote_account_data: Vec<u8> = vec![0; VoteState::size_of()]; let mut vote_account_data: Vec<u8> = 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(); VoteState::serialize(&versioned, &mut vote_account_data).unwrap();
Account { Account {
@ -74,131 +67,65 @@ fn create_components(num_initial_votes: Slot) -> VoteComponents {
} }
}; };
VoteComponents { let transaction_accounts = vec![
slot_hashes, (solana_vote_program::id(), AccountSharedData::default()),
clock, (vote_pubkey, AccountSharedData::from(vote_account)),
signers,
authority_pubkey,
vote_pubkey,
vote_account,
}
}
/// `feature` can be used to change vote program behavior per bench run.
fn do_bench_process_vote_instruction(bencher: &mut Bencher, feature: Option<Pubkey>) {
// 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::<Vec<_>>();
bencher.iter(|| {
let mut transaction_context = TransactionContext::new(
vec![
(vote_pubkey, AccountSharedData::from(vote_account.clone())),
( (
sysvar::slot_hashes::id(), sysvar::slot_hashes::id(),
AccountSharedData::from(slot_hashes_account.clone()), AccountSharedData::from(create_account_for_test(&slot_hashes)),
), ),
( (
sysvar::clock::id(), sysvar::clock::id(),
AccountSharedData::from(clock_account.clone()), 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::<Vec<InstructionAccount>>();
instruction_accounts[0].is_writable = true;
instruction_accounts[3].is_signer = true;
( (
authority_pubkey, num_initial_votes,
AccountSharedData::from(authority_account.clone()), slot_hashes,
), transaction_accounts,
(solana_vote_program::id(), AccountSharedData::default()), instruction_accounts,
], )
1, }
1,
);
fn bench_process_vote_instruction(
bencher: &mut Bencher,
transaction_accounts: Vec<TransactionAccount>,
instruction_accounts: Vec<InstructionAccount>,
instruction_data: Vec<u8>,
) {
bencher.iter(|| {
let mut transaction_context = TransactionContext::new(transaction_accounts.clone(), 1, 1);
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
invoke_context.feature_set = feature_set.clone();
invoke_context invoke_context
.push(&instruction_accounts, &program_indices, &[]) .push(&instruction_accounts, &[0], &instruction_data)
.unwrap(); .unwrap();
let first_instruction_account = 1;
assert!(solana_vote_program::vote_processor::process_instruction( assert!(solana_vote_program::vote_processor::process_instruction(
first_instruction_account, 1,
&instruction.data, &instruction_data,
&mut invoke_context &mut invoke_context
) )
.is_ok()); .is_ok());
invoke_context.pop().unwrap();
}); });
} }
/// `feature` can be used to change vote program behavior per bench run. #[bench]
fn do_bench_process_vote(bencher: &mut Bencher, feature: Option<Pubkey>) { #[ignore]
// vote accounts are usually almost full of votes in normal operation fn bench_process_vote(bencher: &mut Bencher) {
let num_initial_votes = MAX_LOCKOUT_HISTORY as Slot; let (num_initial_votes, slot_hashes, transaction_accounts, instruction_accounts) =
create_accounts();
let VoteComponents {
slot_hashes,
clock,
signers,
vote_pubkey,
vote_account,
..
} = create_components(num_initial_votes);
let num_vote_slots = 4; let num_vote_slots = 4;
let last_vote_slot = num_initial_votes let last_vote_slot = num_initial_votes
@ -209,45 +136,25 @@ fn do_bench_process_vote(bencher: &mut Bencher, feature: Option<Pubkey>) {
.find(|(slot, _hash)| *slot == last_vote_slot) .find(|(slot, _hash)| *slot == last_vote_slot)
.unwrap() .unwrap()
.1; .1;
let vote = Vote::new( let vote = Vote::new(
(num_initial_votes..=last_vote_slot).collect(), (num_initial_votes..=last_vote_slot).collect(),
last_vote_hash, last_vote_hash,
); );
let instruction_data = bincode::serialize(&VoteInstruction::Vote(vote)).unwrap();
let mut feature_set = FeatureSet::all_enabled(); bench_process_vote_instruction(
if let Some(feature) = feature { bencher,
feature_set.activate(&feature, 0); transaction_accounts,
} instruction_accounts,
let feature_set = Arc::new(feature_set); instruction_data,
);
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());
});
} }
fn do_bench_process_vote_state_update(bencher: &mut Bencher) { #[bench]
// vote accounts are usually almost full of votes in normal operation #[ignore]
let num_initial_votes = MAX_LOCKOUT_HISTORY as Slot; fn bench_process_vote_state_update(bencher: &mut Bencher) {
let (num_initial_votes, slot_hashes, transaction_accounts, instruction_accounts) =
let VoteComponents { create_accounts();
slot_hashes,
clock,
signers,
vote_pubkey,
vote_account,
..
} = create_components(num_initial_votes);
let num_vote_slots = MAX_LOCKOUT_HISTORY as Slot; let num_vote_slots = MAX_LOCKOUT_HISTORY as Slot;
let last_vote_slot = num_initial_votes 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) .find(|(slot, _hash)| *slot == last_vote_slot)
.unwrap() .unwrap()
.1; .1;
let slots_and_lockouts: Vec<(Slot, u32)> = let slots_and_lockouts: Vec<(Slot, u32)> =
((num_initial_votes.saturating_add(1)..=last_vote_slot).zip((1u32..=31).rev())).collect(); ((num_initial_votes.saturating_add(1)..=last_vote_slot).zip((1u32..=31).rev())).collect();
let mut vote_state_update = VoteStateUpdate::from(slots_and_lockouts); let mut vote_state_update = VoteStateUpdate::from(slots_and_lockouts);
vote_state_update.root = Some(num_initial_votes); vote_state_update.root = Some(num_initial_votes);
vote_state_update.hash = last_vote_hash; vote_state_update.hash = last_vote_hash;
let instruction_data =
bincode::serialize(&VoteInstruction::UpdateVoteState(vote_state_update)).unwrap();
bencher.iter(|| { bench_process_vote_instruction(
let vote_account = RefCell::new(AccountSharedData::from(vote_account.clone())); bencher,
let keyed_account = KeyedAccount::new(&vote_pubkey, true, &vote_account); transaction_accounts,
let vote_state_update = vote_state_update.clone(); instruction_accounts,
assert!(vote_state::process_vote_state_update( instruction_data,
&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);
} }

View File

@ -5,87 +5,157 @@ use {
log::*, log::*,
solana_metrics::inc_new_counter_info, solana_metrics::inc_new_counter_info,
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_check2,
}, },
solana_sdk::{ solana_sdk::{feature_set, instruction::InstructionError, program_utils::limited_deserialize},
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,
}; };
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( pub fn process_instruction(
first_instruction_account: usize, _first_instruction_account: usize,
data: &[u8], data: &[u8],
invoke_context: &mut InvokeContext, invoke_context: &mut InvokeContext,
) -> Result<(), InstructionError> { ) -> 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!("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 vote_account =
instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
if vote_account.get_owner() != &id() {
return Err(InstructionError::InvalidAccountOwner); return Err(InstructionError::InvalidAccountOwner);
} }
}
let signers: HashSet<Pubkey> = get_signers(&keyed_accounts[first_instruction_account..]); let signers = instruction_context.get_signers(transaction_context);
match limited_deserialize(data)? { match limited_deserialize(data)? {
VoteInstruction::InitializeAccount(vote_init) => { VoteInstruction::InitializeAccount(vote_init) => {
let rent = get_sysvar_with_account_check::rent( let rent = get_sysvar_with_account_check2::rent(
keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?,
invoke_context, invoke_context,
instruction_context,
instruction_account_indices::InitializeAccount::Rent as usize,
)?; )?;
verify_rent_exemption(me, &rent)?; {
let clock = get_sysvar_with_account_check::clock( // Verify rent exemption
keyed_account_at_index(keyed_accounts, first_instruction_account + 2)?, let vote_account = instruction_context.try_borrow_instruction_account(
invoke_context, transaction_context,
instruction_account_indices::InitializeAccount::VoteAccount as usize,
)?; )?;
vote_state::initialize_account(me, &vote_init, &signers, &clock) if !rent.is_exempt(vote_account.get_lamports(), vote_account.get_data().len()) {
return Err(InstructionError::InsufficientFunds);
} }
VoteInstruction::Authorize(voter_pubkey, vote_authorize) => { }
let clock = get_sysvar_with_account_check::clock( let _clock = get_sysvar_with_account_check2::clock(
keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?,
invoke_context, invoke_context,
instruction_context,
instruction_account_indices::InitializeAccount::Clock as usize,
)?; )?;
vote_state::authorize( vote_state::initialize_account(
me, invoke_context,
&voter_pubkey, instruction_context,
vote_authorize,
&signers, &signers,
&clock, instruction_account_indices::InitializeAccount::VoteAccount as usize,
&invoke_context.feature_set, &vote_init,
) )
} }
VoteInstruction::UpdateValidatorIdentity => vote_state::update_validator_identity( VoteInstruction::Authorize(voter_pubkey, vote_authorize) => {
me, let _clock = get_sysvar_with_account_check2::clock(
keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?.unsigned_key(), invoke_context,
instruction_context,
instruction_account_indices::Authorize::Clock as usize,
)?;
vote_state::authorize(
invoke_context,
instruction_context,
&signers, &signers,
), instruction_account_indices::Authorize::VoteAccount as usize,
VoteInstruction::UpdateCommission(commission) => { &voter_pubkey,
vote_state::update_commission(me, commission, &signers) 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, _) => { VoteInstruction::Vote(vote) | VoteInstruction::VoteSwitch(vote, _) => {
inc_new_counter_info!("vote-native", 1); inc_new_counter_info!("vote-native", 1);
let slot_hashes = get_sysvar_with_account_check::slot_hashes( let _slot_hashes = get_sysvar_with_account_check2::slot_hashes(
keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?,
invoke_context, invoke_context,
instruction_context,
instruction_account_indices::Vote::SlotHashes as usize,
)?; )?;
let clock = get_sysvar_with_account_check::clock( let _clock = get_sysvar_with_account_check2::clock(
keyed_account_at_index(keyed_accounts, first_instruction_account + 2)?,
invoke_context, invoke_context,
instruction_context,
instruction_account_indices::Vote::Clock as usize,
)?; )?;
vote_state::process_vote( vote_state::process_vote(
me, invoke_context,
&slot_hashes, instruction_context,
&clock,
&vote,
&signers, &signers,
&invoke_context.feature_set, instruction_account_indices::Vote::VoteAccount as usize,
&vote,
) )
} }
VoteInstruction::UpdateVoteState(vote_state_update) 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()) .is_active(&feature_set::allow_votes_to_directly_update_vote_state::id())
{ {
inc_new_counter_info!("vote-state-native", 1); 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( vote_state::process_vote_state_update(
me, invoke_context,
slot_hashes.slot_hashes(), instruction_context,
&clock,
vote_state_update,
&signers, &signers,
instruction_account_indices::UpdateVoteState::VoteAccount as usize,
vote_state_update,
) )
} else { } else {
Err(InstructionError::InvalidInstructionData) Err(InstructionError::InvalidInstructionData)
} }
} }
VoteInstruction::Withdraw(lamports) => { VoteInstruction::Withdraw(lamports) => {
let to = keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?; instruction_context.check_number_of_instruction_accounts(2)?;
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
};
vote_state::withdraw( vote_state::withdraw(
me, invoke_context,
lamports, instruction_context,
to,
&signers, &signers,
rent_sysvar.as_deref(), instruction_account_indices::Withdraw::VoteAccount as usize,
clock_if_feature_active.as_deref(), instruction_account_indices::Withdraw::Recipient as usize,
lamports,
) )
} }
VoteInstruction::AuthorizeChecked(vote_authorize) => { VoteInstruction::AuthorizeChecked(vote_authorize) => {
@ -143,21 +192,28 @@ pub fn process_instruction(
.feature_set .feature_set
.is_active(&feature_set::vote_stake_checked_instructions::id()) .is_active(&feature_set::vote_stake_checked_instructions::id())
{ {
let voter_pubkey = instruction_context.check_number_of_instruction_accounts(4)?;
&keyed_account_at_index(keyed_accounts, first_instruction_account + 3)? if !instruction_context.is_signer(
.signer_key() instruction_context.get_number_of_program_accounts()
.ok_or(InstructionError::MissingRequiredSignature)?; + instruction_account_indices::AuthorizeChecked::Voter as usize,
let clock = get_sysvar_with_account_check::clock( )? {
keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?, return Err(InstructionError::MissingRequiredSignature);
}
let _clock = get_sysvar_with_account_check2::clock(
invoke_context, invoke_context,
instruction_context,
instruction_account_indices::AuthorizeChecked::Clock as usize,
)?; )?;
vote_state::authorize( vote_state::authorize(
me, invoke_context,
voter_pubkey, instruction_context,
vote_authorize,
&signers, &signers,
&clock, instruction_account_indices::AuthorizeChecked::VoteAccount as usize,
&invoke_context.feature_set, instruction_context.get_instruction_account_key(
transaction_context,
instruction_account_indices::AuthorizeChecked::Voter as usize,
)?,
vote_authorize,
) )
} else { } else {
Err(InstructionError::InvalidInstructionData) 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)] #[cfg(test)]
mod tests { mod tests {
use { use {
@ -201,9 +246,10 @@ mod tests {
feature_set::FeatureSet, feature_set::FeatureSet,
hash::Hash, hash::Hash,
instruction::{AccountMeta, Instruction}, 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 { fn create_default_account() -> AccountSharedData {

View File

@ -5,19 +5,19 @@ use {
bincode::{deserialize, serialize_into, serialized_size, ErrorKind}, bincode::{deserialize, serialize_into, serialized_size, ErrorKind},
log::*, log::*,
serde_derive::{Deserialize, Serialize}, serde_derive::{Deserialize, Serialize},
solana_program_runtime::invoke_context::InvokeContext,
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},
}, },
std::{ std::{
cmp::Ordering, cmp::Ordering,
@ -1165,19 +1165,24 @@ 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, invoke_context: &InvokeContext,
instruction_context: &InstructionContext,
signers: &HashSet<Pubkey, S>,
vote_account_index: usize,
authorized: &Pubkey, authorized: &Pubkey,
vote_authorize: VoteAuthorize, vote_authorize: VoteAuthorize,
signers: &HashSet<Pubkey, S>,
clock: &Clock,
feature_set: &FeatureSet,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
let mut vote_state: VoteState = let mut vote_account = instruction_context
State::<VoteStateVersions>::state(vote_account)?.convert_to_current(); .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::<VoteStateVersions>()?
.convert_to_current();
match vote_authorize { match vote_authorize {
VoteAuthorize::Voter => { 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()) .is_active(&feature_set::vote_withdraw_authority_may_change_authorized_voter::id())
{ {
verify_authorized_signer(&vote_state.authorized_withdrawer, signers).is_ok() verify_authorized_signer(&vote_state.authorized_withdrawer, signers).is_ok()
@ -1211,12 +1216,17 @@ 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, invoke_context: &InvokeContext,
node_pubkey: &Pubkey, instruction_context: &InstructionContext,
signers: &HashSet<Pubkey, S>, signers: &HashSet<Pubkey, S>,
vote_account_index: usize,
node_pubkey: &Pubkey,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
let mut vote_state: VoteState = let mut vote_account = instruction_context
State::<VoteStateVersions>::state(vote_account)?.convert_to_current(); .try_borrow_instruction_account(invoke_context.transaction_context, vote_account_index)?;
let mut vote_state: VoteState = vote_account
.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 +1241,17 @@ 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, invoke_context: &InvokeContext,
commission: u8, instruction_context: &InstructionContext,
signers: &HashSet<Pubkey, S>, signers: &HashSet<Pubkey, S>,
vote_account_index: usize,
commission: u8,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
let mut vote_state: VoteState = let mut vote_account = instruction_context
State::<VoteStateVersions>::state(vote_account)?.convert_to_current(); .try_borrow_instruction_account(invoke_context.transaction_context, vote_account_index)?;
let mut vote_state: VoteState = vote_account
.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 +1274,42 @@ 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, invoke_context: &InvokeContext,
lamports: u64, instruction_context: &InstructionContext,
to_account: &KeyedAccount,
signers: &HashSet<Pubkey, S>, signers: &HashSet<Pubkey, S>,
rent_sysvar: Option<&Rent>, vote_account_index: usize,
clock: Option<&Clock>, recipient_account_index: usize,
lamports: u64,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
let vote_state: VoteState = let mut vote_account = instruction_context
State::<VoteStateVersions>::state(vote_account)?.convert_to_current(); .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::<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 +1332,19 @@ 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 recipient_account = instruction_context.try_borrow_instruction_account(
to_account invoke_context.transaction_context,
.try_account_ref_mut()? recipient_account_index,
.checked_add_lamports(lamports)?; )?;
recipient_account.checked_add_lamports(lamports)?;
Ok(()) Ok(())
} }
@ -1314,15 +1352,19 @@ 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, invoke_context: &InvokeContext,
vote_init: &VoteInit, instruction_context: &InstructionContext,
signers: &HashSet<Pubkey, S>, signers: &HashSet<Pubkey, S>,
clock: &Clock, vote_account_index: usize,
vote_init: &VoteInit,
) -> Result<(), InstructionError> { ) -> 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); 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);
@ -1332,16 +1374,16 @@ pub fn initialize_account<S: std::hash::BuildHasher>(
verify_authorized_signer(&vote_init.node_pubkey, signers)?; verify_authorized_signer(&vote_init.node_pubkey, signers)?;
vote_account.set_state(&VoteStateVersions::new_current(VoteState::new( vote_account.set_state(&VoteStateVersions::new_current(VoteState::new(
vote_init, clock, vote_init, &clock,
))) )))
} }
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,16 +1397,25 @@ 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, invoke_context: &InvokeContext,
slot_hashes: &[SlotHash], instruction_context: &InstructionContext,
clock: &Clock,
vote: &Vote,
signers: &HashSet<Pubkey, S>, signers: &HashSet<Pubkey, S>,
feature_set: &FeatureSet, vote_account_index: usize,
vote: &Vote,
) -> Result<(), InstructionError> { ) -> 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 { if let Some(timestamp) = vote.timestamp {
vote.slots vote.slots
.iter() .iter()
@ -1376,14 +1427,19 @@ 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, invoke_context: &InvokeContext,
slot_hashes: &[SlotHash], instruction_context: &InstructionContext,
clock: &Clock,
mut vote_state_update: VoteStateUpdate,
signers: &HashSet<Pubkey, S>, signers: &HashSet<Pubkey, S>,
vote_account_index: usize,
mut vote_state_update: VoteStateUpdate,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
let mut vote_state = verify_and_get_vote_state(vote_account, clock, signers)?; let mut vote_account = instruction_context
vote_state.check_update_vote_state_slots_are_valid(&mut vote_state_update, slot_hashes)?; .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.process_new_vote_state(
vote_state_update.lockouts, vote_state_update.lockouts,
vote_state_update.root, vote_state_update.root,