Skip to content

Commit

Permalink
Fetch sysvars from invoke context for vote program (solana-labs#22444)
Browse files Browse the repository at this point in the history
  • Loading branch information
jstarry committed Jan 20, 2022
1 parent cb5106a commit c66a134
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 9 deletions.
92 changes: 88 additions & 4 deletions programs/vote/src/vote_instruction.rs
Expand Up @@ -15,12 +15,19 @@ use {
feature_set,
hash::Hash,
instruction::{AccountMeta, Instruction, InstructionError},
<<<<<<< HEAD
keyed_account::{from_keyed_account, get_signers, keyed_account_at_index, KeyedAccount},
process_instruction::{get_sysvar, InvokeContext},
=======
keyed_account::{
check_sysvar_keyed_account, from_keyed_account, get_signers, keyed_account_at_index,
KeyedAccount,
},
>>>>>>> b211f839cb (Fetch sysvars from invoke context for vote program (#22444))
program_utils::limited_deserialize,
pubkey::Pubkey,
system_instruction,
sysvar::{self, clock::Clock, slot_hashes::SlotHashes},
sysvar::{self, clock::Clock, rent::Rent, slot_hashes::SlotHashes, Sysvar},
},
std::collections::HashSet,
thiserror::Error,
Expand Down Expand Up @@ -299,16 +306,28 @@ pub fn withdraw(

fn verify_rent_exemption(
keyed_account: &KeyedAccount,
rent_sysvar_account: &KeyedAccount,
rent: &Rent,
) -> Result<(), InstructionError> {
let rent: sysvar::rent::Rent = from_keyed_account(rent_sysvar_account)?;
if !rent.is_exempt(keyed_account.lamports()?, keyed_account.data_len()?) {
Err(InstructionError::InsufficientFunds)
} else {
Ok(())
}
}

/// 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(
_program_id: &Pubkey,
data: &[u8],
Expand All @@ -331,6 +350,7 @@ pub fn process_instruction(

match limited_deserialize(data)? {
VoteInstruction::InitializeAccount(vote_init) => {
<<<<<<< HEAD
verify_rent_exemption(me, keyed_account_at_index(keyed_accounts, 1)?)?;
vote_state::initialize_account(
me,
Expand All @@ -346,6 +366,26 @@ pub fn process_instruction(
&signers,
&from_keyed_account::<Clock>(keyed_account_at_index(keyed_accounts, 1)?)?,
),
=======
let rent: Rent = get_sysvar_with_keyed_account_check(
keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?,
invoke_context,
)?;
verify_rent_exemption(me, &rent)?;
let clock: Clock = get_sysvar_with_keyed_account_check(
keyed_account_at_index(keyed_accounts, first_instruction_account + 2)?,
invoke_context,
)?;
vote_state::initialize_account(me, &vote_init, &signers, &clock)
}
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)
}
>>>>>>> b211f839cb (Fetch sysvars from invoke context for vote program (#22444))
VoteInstruction::UpdateValidatorIdentity => vote_state::update_validator_identity(
me,
keyed_account_at_index(keyed_accounts, 1)?.unsigned_key(),
Expand All @@ -356,13 +396,25 @@ pub fn process_instruction(
}
VoteInstruction::Vote(vote) | VoteInstruction::VoteSwitch(vote, _) => {
inc_new_counter_info!("vote-native", 1);
<<<<<<< HEAD
vote_state::process_vote(
me,
&from_keyed_account::<SlotHashes>(keyed_account_at_index(keyed_accounts, 1)?)?,
&from_keyed_account::<Clock>(keyed_account_at_index(keyed_accounts, 2)?)?,
&vote,
&signers,
)
=======
let slot_hashes: SlotHashes = get_sysvar_with_keyed_account_check(
keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?,
invoke_context,
)?;
let clock: Clock = get_sysvar_with_keyed_account_check(
keyed_account_at_index(keyed_accounts, first_instruction_account + 2)?,
invoke_context,
)?;
vote_state::process_vote(me, &slot_hashes, &clock, &vote, &signers)
>>>>>>> b211f839cb (Fetch sysvars from invoke context for vote program (#22444))
}
VoteInstruction::Withdraw(lamports) => {
let to = keyed_account_at_index(keyed_accounts, 1)?;
Expand Down Expand Up @@ -400,13 +452,22 @@ mod tests {
use {
super::*,
bincode::serialize,
<<<<<<< HEAD
solana_sdk::{
account::{self, Account, AccountSharedData},
process_instruction::MockInvokeContext,
rent::Rent,
sysvar_cache::SysvarCache,
},
std::{borrow::Cow, cell::RefCell, str::FromStr},
=======
solana_program_runtime::{
invoke_context::{mock_process_instruction, mock_process_instruction_with_sysvars},
sysvar_cache::SysvarCache,
},
solana_sdk::account::{self, Account, AccountSharedData},
std::str::FromStr,
>>>>>>> b211f839cb (Fetch sysvars from invoke context for vote program (#22444))
};

fn create_default_account() -> RefCell<AccountSharedData> {
Expand Down Expand Up @@ -451,6 +512,7 @@ mod tests {
})
})
.collect();
<<<<<<< HEAD

for _ in 0..instruction.accounts.len() {
accounts.push(RefCell::new(AccountSharedData::default()));
Expand Down Expand Up @@ -479,6 +541,28 @@ mod tests {
invoke_context.sysvar_cache = Cow::Owned(sysvar_cache);
super::process_instruction(&Pubkey::default(), &instruction.data, &mut invoke_context)
}
=======
let mut sysvar_cache = SysvarCache::default();
let rent = Rent::free();
sysvar_cache.push_entry(sysvar::rent::id(), bincode::serialize(&rent).unwrap());
let clock = Clock::default();
sysvar_cache.push_entry(sysvar::clock::id(), bincode::serialize(&clock).unwrap());
let slot_hashes = SlotHashes::default();
sysvar_cache.push_entry(
sysvar::slot_hashes::id(),
bincode::serialize(&slot_hashes).unwrap(),
);
mock_process_instruction_with_sysvars(
&id(),
Vec::new(),
&instruction.data,
transaction_accounts,
instruction.accounts.clone(),
expected_result,
&sysvar_cache,
super::process_instruction,
)
>>>>>>> b211f839cb (Fetch sysvars from invoke context for vote program (#22444))
}

fn invalid_vote_state_pubkey() -> Pubkey {
Expand Down Expand Up @@ -665,7 +749,7 @@ mod tests {

#[test]
fn test_minimum_balance() {
let rent = solana_sdk::rent::Rent::default();
let rent = Rent::default();
let minimum_balance = rent.minimum_balance(VoteState::size_of());
// golden, may need updating when vote_state grows
assert!(minimum_balance as f64 / 10f64.powf(9.0) < 0.04)
Expand Down
17 changes: 12 additions & 5 deletions sdk/src/keyed_account.rs
Expand Up @@ -7,6 +7,7 @@ use {
std::{
cell::{Ref, RefCell, RefMut},
iter::FromIterator,
ops::Deref,
rc::Rc,
},
};
Expand Down Expand Up @@ -246,14 +247,20 @@ where
}
}

pub fn from_keyed_account<S: Sysvar>(
keyed_account: &crate::keyed_account::KeyedAccount,
) -> Result<S, InstructionError> {
pub fn check_sysvar_keyed_account<'a, S: Sysvar>(
keyed_account: &'a crate::keyed_account::KeyedAccount<'_>,
) -> Result<impl Deref<Target = AccountSharedData> + 'a, InstructionError> {
if !S::check_id(keyed_account.unsigned_key()) {
return Err(InstructionError::InvalidArgument);
}
from_account::<S, AccountSharedData>(&*keyed_account.try_account_ref()?)
.ok_or(InstructionError::InvalidArgument)
keyed_account.try_account_ref()
}

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)]
Expand Down

0 comments on commit c66a134

Please sign in to comment.