Refactor: Unify SysvarCache (#22843)

* Unifies SysvarCache filling in the runtime and tests.
Removes new_mock_with_sysvars_and_features()
Removes mock_process_instruction_with_sysvars().
Replaces from_keyed_account() by SysvarCache in vote processor.

* Replaces from_keyed_account() by SysvarCache in BPF loader.
This commit is contained in:
Alexander Meißner
2022-01-31 17:53:50 +01:00
committed by GitHub
parent 6a0c45fa2e
commit bc800a8d5a
7 changed files with 246 additions and 239 deletions

View File

@ -232,35 +232,30 @@ impl<'a> InvokeContext<'a> {
}
}
pub fn new_mock_with_sysvars_and_features(
transaction_context: &'a mut TransactionContext,
sysvar_cache: &'a SysvarCache,
feature_set: Arc<FeatureSet>,
) -> Self {
Self::new(
transaction_context,
Rent::default(),
&[],
Cow::Borrowed(sysvar_cache),
Some(LogCollector::new_ref()),
ComputeBudget::default(),
Rc::new(RefCell::new(Executors::default())),
feature_set,
Hash::default(),
0,
0,
)
}
pub fn new_mock(
transaction_context: &'a mut TransactionContext,
builtin_programs: &'a [BuiltinProgram],
) -> Self {
let mut sysvar_cache = SysvarCache::default();
sysvar_cache.fill_missing_entries(|pubkey| {
(0..transaction_context.get_number_of_accounts()).find_map(|index| {
if transaction_context.get_key_of_account_at_index(index) == pubkey {
Some(
transaction_context
.get_account_at_index(index)
.borrow()
.clone(),
)
} else {
None
}
})
});
Self::new(
transaction_context,
Rent::default(),
builtin_programs,
Cow::Owned(SysvarCache::default()),
Cow::Owned(sysvar_cache),
Some(LogCollector::new_ref()),
ComputeBudget::default(),
Rc::new(RefCell::new(Executors::default())),
@ -1072,14 +1067,13 @@ pub fn with_mock_invoke_context<R, F: FnMut(&mut InvokeContext) -> R>(
callback(&mut invoke_context)
}
pub fn mock_process_instruction_with_sysvars(
pub fn mock_process_instruction(
loader_id: &Pubkey,
mut program_indices: Vec<usize>,
instruction_data: &[u8],
transaction_accounts: Vec<TransactionAccount>,
instruction_accounts: Vec<AccountMeta>,
expected_result: Result<(), InstructionError>,
sysvar_cache: &SysvarCache,
process_instruction: ProcessInstructionWithContext,
) -> Vec<AccountSharedData> {
program_indices.insert(0, transaction_accounts.len());
@ -1095,7 +1089,6 @@ pub fn mock_process_instruction_with_sysvars(
1,
);
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
invoke_context.sysvar_cache = Cow::Borrowed(sysvar_cache);
let result = invoke_context
.push(
&preparation.instruction_accounts,
@ -1110,27 +1103,6 @@ pub fn mock_process_instruction_with_sysvars(
transaction_accounts
}
pub fn mock_process_instruction(
loader_id: &Pubkey,
program_indices: Vec<usize>,
instruction_data: &[u8],
transaction_accounts: Vec<TransactionAccount>,
instruction_accounts: Vec<AccountMeta>,
expected_result: Result<(), InstructionError>,
process_instruction: ProcessInstructionWithContext,
) -> Vec<AccountSharedData> {
mock_process_instruction_with_sysvars(
loader_id,
program_indices,
instruction_data,
transaction_accounts,
instruction_accounts,
expected_result,
&SysvarCache::default(),
process_instruction,
)
}
/// Visit each unique instruction account index once
fn visit_each_account_once(
instruction_accounts: &[InstructionAccount],

View File

@ -1,10 +1,15 @@
use crate::invoke_context::InvokeContext;
#[allow(deprecated)]
use solana_sdk::sysvar::fees::Fees;
use solana_sdk::sysvar::{fees::Fees, recent_blockhashes::RecentBlockhashes};
use {
solana_sdk::{
account::{AccountSharedData, ReadableAccount},
instruction::InstructionError,
keyed_account::{check_sysvar_keyed_account, KeyedAccount},
pubkey::Pubkey,
sysvar::{
clock::Clock, epoch_schedule::EpochSchedule, rent::Rent, slot_hashes::SlotHashes,
stake_history::StakeHistory, SysvarId,
},
},
std::sync::Arc,
@ -26,6 +31,9 @@ pub struct SysvarCache {
fees: Option<Arc<Fees>>,
rent: Option<Arc<Rent>>,
slot_hashes: Option<Arc<SlotHashes>>,
#[allow(deprecated)]
recent_blockhashes: Option<Arc<RecentBlockhashes>>,
stake_history: Option<Arc<StakeHistory>>,
}
impl SysvarCache {
@ -78,4 +86,139 @@ impl SysvarCache {
pub fn set_slot_hashes(&mut self, slot_hashes: SlotHashes) {
self.slot_hashes = Some(Arc::new(slot_hashes));
}
#[deprecated]
#[allow(deprecated)]
pub fn get_recent_blockhashes(&self) -> Result<Arc<RecentBlockhashes>, InstructionError> {
self.recent_blockhashes
.clone()
.ok_or(InstructionError::UnsupportedSysvar)
}
#[deprecated]
#[allow(deprecated)]
pub fn set_recent_blockhashes(&mut self, recent_blockhashes: RecentBlockhashes) {
self.recent_blockhashes = Some(Arc::new(recent_blockhashes));
}
pub fn get_stake_history(&self) -> Result<Arc<StakeHistory>, InstructionError> {
self.stake_history
.clone()
.ok_or(InstructionError::UnsupportedSysvar)
}
pub fn set_stake_history(&mut self, stake_history: StakeHistory) {
self.stake_history = Some(Arc::new(stake_history));
}
pub fn fill_missing_entries<F: FnMut(&Pubkey) -> Option<AccountSharedData>>(
&mut self,
mut load_sysvar_account: F,
) {
if self.get_clock().is_err() {
if let Some(clock) = load_sysvar_account(&Clock::id())
.and_then(|account| bincode::deserialize(account.data()).ok())
{
self.set_clock(clock);
}
}
if self.get_epoch_schedule().is_err() {
if let Some(epoch_schedule) = load_sysvar_account(&EpochSchedule::id())
.and_then(|account| bincode::deserialize(account.data()).ok())
{
self.set_epoch_schedule(epoch_schedule);
}
}
#[allow(deprecated)]
if self.get_fees().is_err() {
if let Some(fees) = load_sysvar_account(&Fees::id())
.and_then(|account| bincode::deserialize(account.data()).ok())
{
self.set_fees(fees);
}
}
if self.get_rent().is_err() {
if let Some(rent) = load_sysvar_account(&Rent::id())
.and_then(|account| bincode::deserialize(account.data()).ok())
{
self.set_rent(rent);
}
}
if self.get_slot_hashes().is_err() {
if let Some(slot_hashes) = load_sysvar_account(&SlotHashes::id())
.and_then(|account| bincode::deserialize(account.data()).ok())
{
self.set_slot_hashes(slot_hashes);
}
}
#[allow(deprecated)]
if self.get_recent_blockhashes().is_err() {
if let Some(recent_blockhashes) = load_sysvar_account(&RecentBlockhashes::id())
.and_then(|account| bincode::deserialize(account.data()).ok())
{
self.set_recent_blockhashes(recent_blockhashes);
}
}
if self.get_stake_history().is_err() {
if let Some(stake_history) = load_sysvar_account(&StakeHistory::id())
.and_then(|account| bincode::deserialize(account.data()).ok())
{
self.set_stake_history(stake_history);
}
}
}
pub fn reset(&mut self) {
*self = SysvarCache::default();
}
}
/// These methods facilitate a transition from fetching sysvars from keyed
/// accounts to fetching from the sysvar cache without breaking consensus. In
/// order to keep consistent behavior, they continue to enforce the same checks
/// as `solana_sdk::keyed_account::from_keyed_account` despite dynamically
/// loading them instead of deserializing from account data.
pub mod get_sysvar_with_account_check {
use super::*;
pub fn clock(
keyed_account: &KeyedAccount,
invoke_context: &InvokeContext,
) -> Result<Arc<Clock>, InstructionError> {
check_sysvar_keyed_account::<Clock>(keyed_account)?;
invoke_context.get_sysvar_cache().get_clock()
}
pub fn rent(
keyed_account: &KeyedAccount,
invoke_context: &InvokeContext,
) -> Result<Arc<Rent>, InstructionError> {
check_sysvar_keyed_account::<Rent>(keyed_account)?;
invoke_context.get_sysvar_cache().get_rent()
}
pub fn slot_hashes(
keyed_account: &KeyedAccount,
invoke_context: &InvokeContext,
) -> Result<Arc<SlotHashes>, InstructionError> {
check_sysvar_keyed_account::<SlotHashes>(keyed_account)?;
invoke_context.get_sysvar_cache().get_slot_hashes()
}
#[allow(deprecated)]
pub fn recent_blockhashes(
keyed_account: &KeyedAccount,
invoke_context: &InvokeContext,
) -> Result<Arc<RecentBlockhashes>, InstructionError> {
check_sysvar_keyed_account::<RecentBlockhashes>(keyed_account)?;
invoke_context.get_sysvar_cache().get_recent_blockhashes()
}
pub fn stake_history(
keyed_account: &KeyedAccount,
invoke_context: &InvokeContext,
) -> Result<Arc<StakeHistory>, InstructionError> {
check_sysvar_keyed_account::<StakeHistory>(keyed_account)?;
invoke_context.get_sysvar_cache().get_stake_history()
}
}