Fetch sysvars from invoke context for vote program (backport #22444) (#22469)

* Fetch sysvars from invoke context for vote program (#22444)

* resolve conflicts

Co-authored-by: Justin Starry <justin@solana.com>
This commit is contained in:
mergify[bot]
2022-01-15 03:56:00 +00:00
committed by GitHub
parent 054e475c6c
commit a7623ad18c
2 changed files with 68 additions and 46 deletions

View File

@ -16,11 +16,14 @@ use {
feature_set, feature_set,
hash::Hash, hash::Hash,
instruction::{AccountMeta, Instruction, InstructionError}, instruction::{AccountMeta, Instruction, InstructionError},
keyed_account::{from_keyed_account, get_signers, keyed_account_at_index, KeyedAccount}, keyed_account::{
check_sysvar_keyed_account, from_keyed_account, get_signers, keyed_account_at_index,
KeyedAccount,
},
program_utils::limited_deserialize, program_utils::limited_deserialize,
pubkey::Pubkey, pubkey::Pubkey,
system_instruction, system_instruction,
sysvar::{self, clock::Clock, slot_hashes::SlotHashes}, sysvar::{self, clock::Clock, rent::Rent, slot_hashes::SlotHashes, Sysvar},
}, },
std::collections::HashSet, std::collections::HashSet,
thiserror::Error, thiserror::Error,
@ -330,9 +333,8 @@ pub fn withdraw(
fn verify_rent_exemption( fn verify_rent_exemption(
keyed_account: &KeyedAccount, keyed_account: &KeyedAccount,
rent_sysvar_account: &KeyedAccount, rent: &Rent,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
let rent: sysvar::rent::Rent = from_keyed_account(rent_sysvar_account)?;
if !rent.is_exempt(keyed_account.lamports()?, keyed_account.data_len()?) { if !rent.is_exempt(keyed_account.lamports()?, keyed_account.data_len()?) {
Err(InstructionError::InsufficientFunds) Err(InstructionError::InsufficientFunds)
} else { } else {
@ -340,6 +342,19 @@ fn verify_rent_exemption(
} }
} }
/// This method facilitates a transition from fetching sysvars from keyed
/// accounts to fetching from the sysvar cache without breaking consensus. In
/// order to keep consistent behavior, it continues to enforce the same checks
/// as `solana_sdk::keyed_account::from_keyed_account` despite dynamically
/// loading them instead of deserializing from account data.
fn get_sysvar_with_keyed_account_check<S: Sysvar>(
keyed_account: &KeyedAccount,
invoke_context: &InvokeContext,
) -> Result<S, InstructionError> {
check_sysvar_keyed_account::<S>(keyed_account)?;
invoke_context.get_sysvar(keyed_account.unsigned_key())
}
pub fn process_instruction( pub fn process_instruction(
first_instruction_account: usize, first_instruction_account: usize,
data: &[u8], data: &[u8],
@ -358,30 +373,24 @@ pub fn process_instruction(
let signers: HashSet<Pubkey> = get_signers(&keyed_accounts[first_instruction_account..]); let signers: HashSet<Pubkey> = get_signers(&keyed_accounts[first_instruction_account..]);
match limited_deserialize(data)? { match limited_deserialize(data)? {
VoteInstruction::InitializeAccount(vote_init) => { VoteInstruction::InitializeAccount(vote_init) => {
verify_rent_exemption( let rent: Rent = get_sysvar_with_keyed_account_check(
me,
keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?, keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?,
invoke_context,
)?; )?;
vote_state::initialize_account( verify_rent_exemption(me, &rent)?;
me, let clock: Clock = get_sysvar_with_keyed_account_check(
&vote_init, keyed_account_at_index(keyed_accounts, first_instruction_account + 2)?,
&signers, invoke_context,
&from_keyed_account::<Clock>(keyed_account_at_index( )?;
keyed_accounts, vote_state::initialize_account(me, &vote_init, &signers, &clock)
first_instruction_account + 2, }
)?)?, VoteInstruction::Authorize(voter_pubkey, vote_authorize) => {
) let clock: Clock = get_sysvar_with_keyed_account_check(
keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?,
invoke_context,
)?;
vote_state::authorize(me, &voter_pubkey, vote_authorize, &signers, &clock)
} }
VoteInstruction::Authorize(voter_pubkey, vote_authorize) => vote_state::authorize(
me,
&voter_pubkey,
vote_authorize,
&signers,
&from_keyed_account::<Clock>(keyed_account_at_index(
keyed_accounts,
first_instruction_account + 1,
)?)?,
),
VoteInstruction::UpdateValidatorIdentity => vote_state::update_validator_identity( VoteInstruction::UpdateValidatorIdentity => vote_state::update_validator_identity(
me, me,
keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?.unsigned_key(), keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?.unsigned_key(),
@ -392,19 +401,15 @@ pub fn process_instruction(
} }
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);
vote_state::process_vote( let slot_hashes: SlotHashes = get_sysvar_with_keyed_account_check(
me, keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?,
&from_keyed_account::<SlotHashes>(keyed_account_at_index( invoke_context,
keyed_accounts, )?;
first_instruction_account + 1, let clock: Clock = get_sysvar_with_keyed_account_check(
)?)?, keyed_account_at_index(keyed_accounts, first_instruction_account + 2)?,
&from_keyed_account::<Clock>(keyed_account_at_index( invoke_context,
keyed_accounts, )?;
first_instruction_account + 2, vote_state::process_vote(me, &slot_hashes, &clock, &vote, &signers)
)?)?,
&vote,
&signers,
)
} }
VoteInstruction::Withdraw(lamports) => { VoteInstruction::Withdraw(lamports) => {
let to = keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?; let to = keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?;
@ -512,8 +517,18 @@ mod tests {
.collect(); .collect();
let mut sysvar_cache = SysvarCache::default(); let mut sysvar_cache = SysvarCache::default();
let rent = Rent::default(); sysvar_cache.push_entry(
sysvar_cache.push_entry(sysvar::rent::id(), bincode::serialize(&rent).unwrap()); sysvar::rent::id(),
bincode::serialize(&Rent::free()).unwrap(),
);
sysvar_cache.push_entry(
sysvar::clock::id(),
bincode::serialize(&Clock::default()).unwrap(),
);
sysvar_cache.push_entry(
sysvar::slot_hashes::id(),
bincode::serialize(&SlotHashes::default()).unwrap(),
);
solana_program_runtime::invoke_context::mock_process_instruction_with_sysvars( solana_program_runtime::invoke_context::mock_process_instruction_with_sysvars(
&id(), &id(),
Vec::new(), Vec::new(),
@ -709,7 +724,7 @@ mod tests {
#[test] #[test]
fn test_minimum_balance() { fn test_minimum_balance() {
let rent = solana_sdk::rent::Rent::default(); let rent = Rent::default();
let minimum_balance = rent.minimum_balance(VoteState::size_of()); let minimum_balance = rent.minimum_balance(VoteState::size_of());
// golden, may need updating when vote_state grows // golden, may need updating when vote_state grows
assert!(minimum_balance as f64 / 10f64.powf(9.0) < 0.04) assert!(minimum_balance as f64 / 10f64.powf(9.0) < 0.04)

View File

@ -7,6 +7,7 @@ use {
std::{ std::{
cell::{Ref, RefCell, RefMut}, cell::{Ref, RefCell, RefMut},
iter::FromIterator, iter::FromIterator,
ops::Deref,
rc::Rc, rc::Rc,
}, },
}; };
@ -248,14 +249,20 @@ where
} }
} }
pub fn from_keyed_account<S: Sysvar>( pub fn check_sysvar_keyed_account<'a, S: Sysvar>(
keyed_account: &crate::keyed_account::KeyedAccount, keyed_account: &'a crate::keyed_account::KeyedAccount<'_>,
) -> Result<S, InstructionError> { ) -> Result<impl Deref<Target = AccountSharedData> + 'a, InstructionError> {
if !S::check_id(keyed_account.unsigned_key()) { if !S::check_id(keyed_account.unsigned_key()) {
return Err(InstructionError::InvalidArgument); return Err(InstructionError::InvalidArgument);
} }
from_account::<S, AccountSharedData>(&*keyed_account.try_account_ref()?) keyed_account.try_account_ref()
.ok_or(InstructionError::InvalidArgument) }
pub fn from_keyed_account<S: Sysvar>(
keyed_account: &crate::keyed_account::KeyedAccount,
) -> Result<S, InstructionError> {
let sysvar_account = check_sysvar_keyed_account::<S>(keyed_account)?;
from_account::<S, AccountSharedData>(&*sysvar_account).ok_or(InstructionError::InvalidArgument)
} }
#[cfg(test)] #[cfg(test)]