From 4b7450e89e4af62552034828a34f51ba5bf7ed3e Mon Sep 17 00:00:00 2001 From: Justin Starry Date: Sat, 22 Jan 2022 14:09:05 +0800 Subject: [PATCH] Perf: Store deserialized sysvars in the sysvars cache (backport #22455) (#22627) * Perf: Store deserialized sysvars in the sysvars cache (#22455) * resolve conflicts * remove bench --- program-test/src/lib.rs | 34 +++--- programs/bpf_loader/src/syscalls.rs | 85 ++++++++------ programs/stake/src/stake_instruction.rs | 24 ++-- programs/vote/src/vote_instruction.rs | 71 +++++++----- programs/vote/src/vote_state/mod.rs | 8 +- runtime/src/bank.rs | 13 +-- runtime/src/bank/sysvar_cache.rs | 146 ++++++++++++++++++++++-- sdk/src/process_instruction.rs | 21 ---- sdk/src/sysvar_cache.rs | 72 +++++++++--- 9 files changed, 321 insertions(+), 153 deletions(-) diff --git a/program-test/src/lib.rs b/program-test/src/lib.rs index ca66f0d6f3..83fed81129 100644 --- a/program-test/src/lib.rs +++ b/program-test/src/lib.rs @@ -20,9 +20,8 @@ use { solana_sdk::{ account::{Account, AccountSharedData, ReadableAccount, WritableAccount}, account_info::AccountInfo, - clock::{Clock, Slot}, + clock::Slot, entrypoint::{ProgramResult, SUCCESS}, - epoch_schedule::EpochSchedule, execute_timings::ExecuteTimings, feature_set::demote_program_write_locks, fee_calculator::{FeeCalculator, FeeRateGovernor}, @@ -33,17 +32,13 @@ use { native_token::sol_to_lamports, poh_config::PohConfig, process_instruction::{ - self, stable_log, BpfComputeBudget, InvokeContext, ProcessInstructionWithContext, + stable_log, BpfComputeBudget, InvokeContext, ProcessInstructionWithContext, }, program_error::{ProgramError, ACCOUNT_BORROW_FAILED, UNSUPPORTED_SYSVAR}, pubkey::Pubkey, rent::Rent, signature::{Keypair, Signer}, - sysvar::{ - clock, epoch_schedule, - fees::{self, Fees}, - rent, Sysvar, - }, + sysvar::Sysvar, }, solana_vote_program::vote_state::{VoteState, VoteStateVersions}, std::{ @@ -192,8 +187,8 @@ macro_rules! processor { }; } -fn get_sysvar( - id: &Pubkey, +fn get_sysvar( + sysvar: Result, InstructionError>, var_addr: *mut u8, ) -> u64 { let invoke_context = get_invoke_context(); @@ -207,9 +202,10 @@ fn get_sysvar( { panic!("Exceeded compute budget"); } - match process_instruction::get_sysvar::(invoke_context, id) { + + match sysvar { Ok(sysvar_data) => unsafe { - *(var_addr as *mut _ as *mut T) = sysvar_data; + *(var_addr as *mut _ as *mut T) = T::clone(&sysvar_data); SUCCESS }, Err(_) => UNSUPPORTED_SYSVAR, @@ -368,19 +364,25 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs { } fn sol_get_clock_sysvar(&self, var_addr: *mut u8) -> u64 { - get_sysvar::(&clock::id(), var_addr) + get_sysvar( + get_invoke_context().get_sysvar_cache().get_clock(), + var_addr, + ) } fn sol_get_epoch_schedule_sysvar(&self, var_addr: *mut u8) -> u64 { - get_sysvar::(&epoch_schedule::id(), var_addr) + get_sysvar( + get_invoke_context().get_sysvar_cache().get_epoch_schedule(), + var_addr, + ) } fn sol_get_fees_sysvar(&self, var_addr: *mut u8) -> u64 { - get_sysvar::(&fees::id(), var_addr) + get_sysvar(get_invoke_context().get_sysvar_cache().get_fees(), var_addr) } fn sol_get_rent_sysvar(&self, var_addr: *mut u8) -> u64 { - get_sysvar::(&rent::id(), var_addr) + get_sysvar(get_invoke_context().get_sysvar_cache().get_rent(), var_addr) } } diff --git a/programs/bpf_loader/src/syscalls.rs b/programs/bpf_loader/src/syscalls.rs index 601431ab60..3e9e2cf5ae 100644 --- a/programs/bpf_loader/src/syscalls.rs +++ b/programs/bpf_loader/src/syscalls.rs @@ -16,9 +16,7 @@ use { account_utils::StateMut, bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable::{self, UpgradeableLoaderState}, - clock::Clock, entrypoint::{BPF_ALIGN_OF_U128, MAX_PERMITTED_DATA_INCREASE, SUCCESS}, - epoch_schedule::EpochSchedule, feature_set::{ allow_native_ids, check_seed_length, close_upgradeable_program_accounts, cpi_data_cost, demote_program_write_locks, enforce_aligned_host_addrs, keccak256_syscall_enabled, @@ -33,14 +31,13 @@ use { keccak, keyed_account::KeyedAccount, native_loader, - process_instruction::{self, stable_log, ComputeMeter, InvokeContext, Logger}, + process_instruction::{stable_log, ComputeMeter, InvokeContext, Logger}, program::MAX_RETURN_DATA, pubkey::{Pubkey, PubkeyError, MAX_SEEDS, MAX_SEED_LEN}, - rent::Rent, secp256k1_recover::{ Secp256k1RecoverError, SECP256K1_PUBLIC_KEY_LENGTH, SECP256K1_SIGNATURE_LENGTH, }, - sysvar::{self, fees::Fees, Sysvar, SysvarId}, + sysvar::{self, Sysvar, SysvarId}, }, std::{ alloc::Layout, @@ -50,6 +47,7 @@ use { rc::Rc, slice::from_raw_parts_mut, str::{from_utf8, Utf8Error}, + sync::Arc, }, thiserror::Error as ThisError, }; @@ -1142,17 +1140,13 @@ impl<'a> SyscallObject for SyscallSha256<'a> { } } -fn get_sysvar( - id: &Pubkey, +fn get_sysvar( + sysvar: Result, InstructionError>, var_addr: u64, loader_id: &Pubkey, memory_mapping: &MemoryMapping, - invoke_context: Rc>, + invoke_context: &dyn InvokeContext, ) -> Result> { - let invoke_context = invoke_context - .try_borrow() - .map_err(|_| SyscallError::InvokeContextBorrowFailed)?; - invoke_context.get_compute_meter().consume( invoke_context.get_bpf_compute_budget().sysvar_base_cost + size_of::() as u64, )?; @@ -1163,8 +1157,8 @@ fn get_sysvar( invoke_context.is_feature_active(&enforce_aligned_host_addrs::id()), )?; - *var = process_instruction::get_sysvar::(*invoke_context, id) - .map_err(SyscallError::InstructionError)?; + let sysvar: Arc = sysvar.map_err(SyscallError::InstructionError)?; + *var = T::clone(sysvar.as_ref()); Ok(SUCCESS) } @@ -1185,12 +1179,18 @@ impl<'a> SyscallObject for SyscallGetClockSysvar<'a> { memory_mapping: &MemoryMapping, result: &mut Result>, ) { - *result = get_sysvar::( - &sysvar::clock::id(), + let invoke_context = question_mark!( + self.invoke_context + .try_borrow() + .map_err(|_| SyscallError::InvokeContextBorrowFailed), + result + ); + *result = get_sysvar( + invoke_context.get_sysvar_cache().get_clock(), var_addr, self.loader_id, memory_mapping, - self.invoke_context.clone(), + *invoke_context, ); } } @@ -1210,12 +1210,18 @@ impl<'a> SyscallObject for SyscallGetEpochScheduleSysvar<'a> { memory_mapping: &MemoryMapping, result: &mut Result>, ) { - *result = get_sysvar::( - &sysvar::epoch_schedule::id(), + let invoke_context = question_mark!( + self.invoke_context + .try_borrow() + .map_err(|_| SyscallError::InvokeContextBorrowFailed), + result + ); + *result = get_sysvar( + invoke_context.get_sysvar_cache().get_epoch_schedule(), var_addr, self.loader_id, memory_mapping, - self.invoke_context.clone(), + *invoke_context, ); } } @@ -1235,12 +1241,18 @@ impl<'a> SyscallObject for SyscallGetFeesSysvar<'a> { memory_mapping: &MemoryMapping, result: &mut Result>, ) { - *result = get_sysvar::( - &sysvar::fees::id(), + let invoke_context = question_mark!( + self.invoke_context + .try_borrow() + .map_err(|_| SyscallError::InvokeContextBorrowFailed), + result + ); + *result = get_sysvar( + invoke_context.get_sysvar_cache().get_fees(), var_addr, self.loader_id, memory_mapping, - self.invoke_context.clone(), + *invoke_context, ); } } @@ -1260,12 +1272,18 @@ impl<'a> SyscallObject for SyscallGetRentSysvar<'a> { memory_mapping: &MemoryMapping, result: &mut Result>, ) { - *result = get_sysvar::( - &sysvar::rent::id(), + let invoke_context = question_mark!( + self.invoke_context + .try_borrow() + .map_err(|_| SyscallError::InvokeContextBorrowFailed), + result + ); + *result = get_sysvar( + invoke_context.get_sysvar_cache().get_rent(), var_addr, self.loader_id, memory_mapping, - self.invoke_context.clone(), + *invoke_context, ); } } @@ -2771,9 +2789,13 @@ mod tests { }, solana_sdk::{ bpf_loader, + clock::Clock, + epoch_schedule::EpochSchedule, fee_calculator::FeeCalculator, hash::hashv, process_instruction::{MockComputeMeter, MockInvokeContext, MockLogger}, + rent::Rent, + sysvar::fees::Fees, sysvar_cache::SysvarCache, }, std::{borrow::Cow, str::FromStr}, @@ -3647,13 +3669,10 @@ mod tests { }; let mut sysvar_cache = SysvarCache::default(); - sysvar_cache.push_entry(sysvar::clock::id(), bincode::serialize(&src_clock).unwrap()); - sysvar_cache.push_entry( - sysvar::epoch_schedule::id(), - bincode::serialize(&src_epochschedule).unwrap(), - ); - sysvar_cache.push_entry(sysvar::fees::id(), bincode::serialize(&src_fees).unwrap()); - sysvar_cache.push_entry(sysvar::rent::id(), bincode::serialize(&src_rent).unwrap()); + sysvar_cache.set_clock(src_clock.clone()); + sysvar_cache.set_epoch_schedule(src_epochschedule); + sysvar_cache.set_fees(src_fees.clone()); + sysvar_cache.set_rent(src_rent); // Test clock sysvar { diff --git a/programs/stake/src/stake_instruction.rs b/programs/stake/src/stake_instruction.rs index 62a7335289..1189e8ba1b 100644 --- a/programs/stake/src/stake_instruction.rs +++ b/programs/stake/src/stake_instruction.rs @@ -10,7 +10,7 @@ use { feature_set, instruction::InstructionError, keyed_account::{from_keyed_account, get_signers, keyed_account_at_index}, - process_instruction::{get_sysvar, InvokeContext}, + process_instruction::InvokeContext, program_utils::limited_deserialize, pubkey::Pubkey, stake::{ @@ -18,7 +18,7 @@ use { program::id, state::{Authorized, Lockup}, }, - sysvar::{self, clock::Clock, rent::Rent, stake_history::StakeHistory}, + sysvar::{clock::Clock, rent::Rent, stake_history::StakeHistory}, }, }; @@ -167,11 +167,11 @@ pub fn process_instruction( ), StakeInstruction::SetLockup(lockup) => { let clock = if invoke_context.is_feature_active(&feature_set::stake_program_v4::id()) { - Some(get_sysvar::(invoke_context, &sysvar::clock::id())?) + Some(invoke_context.get_sysvar_cache().get_clock()?) } else { None }; - me.set_lockup(&lockup, &signers, clock.as_ref()) + me.set_lockup(&lockup, &signers, clock.as_deref()) } StakeInstruction::InitializeChecked => { if invoke_context.is_feature_active(&feature_set::vote_stake_checked_instructions::id()) @@ -262,8 +262,8 @@ pub fn process_instruction( epoch: lockup_checked.epoch, custodian, }; - let clock = Some(get_sysvar::(invoke_context, &sysvar::clock::id())?); - me.set_lockup(&lockup, &signers, clock.as_ref()) + let clock = Some(invoke_context.get_sysvar_cache().get_clock()?); + me.set_lockup(&lockup, &signers, clock.as_deref()) } else { Err(InstructionError::InvalidInstructionData) } @@ -288,7 +288,7 @@ mod tests { instruction::{self, LockupArgs}, state::{Authorized, Lockup, StakeAuthorize}, }, - sysvar::stake_history::StakeHistory, + sysvar::{self, stake_history::StakeHistory}, sysvar_cache::SysvarCache, }, std::{borrow::Cow, cell::RefCell, str::FromStr}, @@ -372,10 +372,7 @@ mod tests { let mut invoke_context = MockInvokeContext::new(keyed_accounts); let mut sysvar_cache = SysvarCache::default(); - sysvar_cache.push_entry( - sysvar::clock::id(), - bincode::serialize(&Clock::default()).unwrap(), - ); + sysvar_cache.set_clock(Clock::default()); invoke_context.sysvar_cache = Cow::Owned(sysvar_cache); super::process_instruction(&Pubkey::default(), &instruction.data, &mut invoke_context) } @@ -1040,10 +1037,7 @@ mod tests { let mut invoke_context = MockInvokeContext::new(keyed_accounts); let mut sysvar_cache = SysvarCache::default(); - sysvar_cache.push_entry( - sysvar::clock::id(), - bincode::serialize(&Clock::default()).unwrap(), - ); + sysvar_cache.set_clock(Clock::default()); invoke_context.sysvar_cache = Cow::Owned(sysvar_cache); assert_eq!( diff --git a/programs/vote/src/vote_instruction.rs b/programs/vote/src/vote_instruction.rs index f213201b59..028f4ee9af 100644 --- a/programs/vote/src/vote_instruction.rs +++ b/programs/vote/src/vote_instruction.rs @@ -19,13 +19,13 @@ use { check_sysvar_keyed_account, from_keyed_account, get_signers, keyed_account_at_index, KeyedAccount, }, - process_instruction::{get_sysvar, InvokeContext}, + process_instruction::InvokeContext, program_utils::limited_deserialize, pubkey::Pubkey, system_instruction, - sysvar::{self, clock::Clock, rent::Rent, slot_hashes::SlotHashes, Sysvar}, + sysvar::{self, clock::Clock, rent::Rent, slot_hashes::SlotHashes}, }, - std::collections::HashSet, + std::{collections::HashSet, sync::Arc}, thiserror::Error, }; @@ -311,17 +311,37 @@ fn verify_rent_exemption( } } -/// This method facilitates a transition from fetching sysvars from keyed +/// 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, it continues to enforce the same checks +/// 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. -fn get_sysvar_with_keyed_account_check( - keyed_account: &KeyedAccount, - invoke_context: &dyn InvokeContext, -) -> Result { - check_sysvar_keyed_account::(keyed_account)?; - get_sysvar(invoke_context, keyed_account.unsigned_key()) +mod get_sysvar_with_keyed_account_check { + use super::*; + + pub fn clock( + keyed_account: &KeyedAccount, + invoke_context: &dyn InvokeContext, + ) -> Result, InstructionError> { + check_sysvar_keyed_account::(keyed_account)?; + invoke_context.get_sysvar_cache().get_clock() + } + + pub fn rent( + keyed_account: &KeyedAccount, + invoke_context: &dyn InvokeContext, + ) -> Result, InstructionError> { + check_sysvar_keyed_account::(keyed_account)?; + invoke_context.get_sysvar_cache().get_rent() + } + + pub fn slot_hashes( + keyed_account: &KeyedAccount, + invoke_context: &dyn InvokeContext, + ) -> Result, InstructionError> { + check_sysvar_keyed_account::(keyed_account)?; + invoke_context.get_sysvar_cache().get_slot_hashes() + } } pub fn process_instruction( @@ -346,19 +366,19 @@ pub fn process_instruction( match limited_deserialize(data)? { VoteInstruction::InitializeAccount(vote_init) => { - let rent: Rent = get_sysvar_with_keyed_account_check( + let rent = get_sysvar_with_keyed_account_check::rent( keyed_account_at_index(keyed_accounts, 1)?, invoke_context, )?; verify_rent_exemption(me, &rent)?; - let clock: Clock = get_sysvar_with_keyed_account_check( + let clock = get_sysvar_with_keyed_account_check::clock( keyed_account_at_index(keyed_accounts, 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( + let clock = get_sysvar_with_keyed_account_check::clock( keyed_account_at_index(keyed_accounts, 1)?, invoke_context, )?; @@ -374,11 +394,11 @@ pub fn process_instruction( } VoteInstruction::Vote(vote) | VoteInstruction::VoteSwitch(vote, _) => { inc_new_counter_info!("vote-native", 1); - let slot_hashes: SlotHashes = get_sysvar_with_keyed_account_check( + let slot_hashes = get_sysvar_with_keyed_account_check::slot_hashes( keyed_account_at_index(keyed_accounts, 1)?, invoke_context, )?; - let clock: Clock = get_sysvar_with_keyed_account_check( + let clock = get_sysvar_with_keyed_account_check::clock( keyed_account_at_index(keyed_accounts, 2)?, invoke_context, )?; @@ -389,11 +409,11 @@ pub fn process_instruction( let rent_sysvar = if invoke_context .is_feature_active(&feature_set::reject_non_rent_exempt_vote_withdraws::id()) { - Some(get_sysvar(invoke_context, &sysvar::rent::id())?) + Some(invoke_context.get_sysvar_cache().get_rent()?) } else { None }; - vote_state::withdraw(me, lamports, to, &signers, rent_sysvar) + vote_state::withdraw(me, lamports, to, &signers, rent_sysvar.as_deref()) } VoteInstruction::AuthorizeChecked(vote_authorize) => { if invoke_context.is_feature_active(&feature_set::vote_stake_checked_instructions::id()) @@ -484,18 +504,9 @@ mod tests { .collect(); let mut invoke_context = MockInvokeContext::new(keyed_accounts); let mut sysvar_cache = SysvarCache::default(); - sysvar_cache.push_entry( - 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(), - ); + sysvar_cache.set_rent(Rent::free()); + sysvar_cache.set_clock(Clock::default()); + sysvar_cache.set_slot_hashes(SlotHashes::default()); invoke_context.sysvar_cache = Cow::Owned(sysvar_cache); super::process_instruction(&Pubkey::default(), &instruction.data, &mut invoke_context) } diff --git a/programs/vote/src/vote_state/mod.rs b/programs/vote/src/vote_state/mod.rs index 60fc8987a9..754888e44d 100644 --- a/programs/vote/src/vote_state/mod.rs +++ b/programs/vote/src/vote_state/mod.rs @@ -681,7 +681,7 @@ pub fn withdraw( lamports: u64, to_account: &KeyedAccount, signers: &HashSet, - rent_sysvar: Option, + rent_sysvar: Option<&Rent>, ) -> Result<(), InstructionError> { let vote_state: VoteState = State::::state(vote_account)?.convert_to_current(); @@ -1742,7 +1742,7 @@ mod tests { &RefCell::new(AccountSharedData::default()), ), &signers, - Some(rent_sysvar), + Some(&rent_sysvar), ); assert_eq!(res, Err(InstructionError::InsufficientFunds)); } @@ -1765,7 +1765,7 @@ mod tests { withdraw_lamports, &KeyedAccount::new(&solana_sdk::pubkey::new_rand(), false, &to_account), &signers, - Some(rent_sysvar), + Some(&rent_sysvar), ); assert_eq!(res, Ok(())); assert_eq!( @@ -1789,7 +1789,7 @@ mod tests { lamports, &KeyedAccount::new(&solana_sdk::pubkey::new_rand(), false, &to_account), &signers, - *rent_sysvar, + rent_sysvar.as_ref(), ); assert_eq!(res, Ok(())); assert_eq!(vote_account.borrow().lamports(), 0); diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index b02b0f986a..e7c49808dc 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -114,8 +114,7 @@ use { signature::{Keypair, Signature}, slot_hashes::SlotHashes, slot_history::SlotHistory, - system_transaction, - sysvar::{self}, + system_transaction, sysvar, sysvar_cache::SysvarCache, timing::years_as_slots, transaction::{self, Result, Transaction, TransactionError}, @@ -1295,7 +1294,7 @@ impl Bank { bank.update_rent(); bank.update_epoch_schedule(); bank.update_recent_blockhashes(); - bank.fill_sysvar_cache(); + bank.fill_missing_sysvar_cache_entries(); bank } @@ -1478,7 +1477,7 @@ impl Bank { if !new.fix_recent_blockhashes_sysvar_delay() { new.update_recent_blockhashes(); } - new.fill_sysvar_cache(); + new.fill_missing_sysvar_cache_entries(); time.stop(); @@ -1537,7 +1536,7 @@ impl Bank { new.inherit_specially_retained_account_fields(account), ) }); - + new.fill_missing_sysvar_cache_entries(); new.freeze(); new } @@ -1789,10 +1788,6 @@ impl Bank { } self.store_account_and_update_capitalization(pubkey, &new_account); - - // Update the entry in the cache - let mut sysvar_cache = self.sysvar_cache.write().unwrap(); - sysvar_cache.update_entry(pubkey, &new_account); } fn inherit_specially_retained_account_fields( diff --git a/runtime/src/bank/sysvar_cache.rs b/runtime/src/bank/sysvar_cache.rs index d6e3d42291..4b9950ec79 100644 --- a/runtime/src/bank/sysvar_cache.rs +++ b/runtime/src/bank/sysvar_cache.rs @@ -1,17 +1,149 @@ use { super::Bank, - solana_sdk::{account::ReadableAccount, sysvar}, + solana_sdk::{account::ReadableAccount, sysvar::Sysvar}, }; impl Bank { - pub(crate) fn fill_sysvar_cache(&mut self) { + pub(crate) fn fill_missing_sysvar_cache_entries(&self) { let mut sysvar_cache = self.sysvar_cache.write().unwrap(); - for id in sysvar::ALL_IDS.iter() { - if !sysvar_cache.iter().any(|(key, _data)| key == id) { - if let Some(account) = self.get_account_with_fixed_root(id) { - sysvar_cache.push_entry(*id, account.data().to_vec()); - } + if sysvar_cache.get_clock().is_err() { + if let Some(clock) = self.load_sysvar_account() { + sysvar_cache.set_clock(clock); + } + } + if sysvar_cache.get_epoch_schedule().is_err() { + if let Some(epoch_schedule) = self.load_sysvar_account() { + sysvar_cache.set_epoch_schedule(epoch_schedule); + } + } + if sysvar_cache.get_fees().is_err() { + if let Some(fees) = self.load_sysvar_account() { + sysvar_cache.set_fees(fees); + } + } + if sysvar_cache.get_rent().is_err() { + if let Some(rent) = self.load_sysvar_account() { + sysvar_cache.set_rent(rent); + } + } + if sysvar_cache.get_slot_hashes().is_err() { + if let Some(slot_hashes) = self.load_sysvar_account() { + sysvar_cache.set_slot_hashes(slot_hashes); } } } + + fn load_sysvar_account(&self) -> Option { + if let Some(account) = self.get_account_with_fixed_root(&T::id()) { + bincode::deserialize(account.data()).ok() + } else { + None + } + } +} + +#[cfg(test)] +mod tests { + use { + super::*, + solana_sdk::{ + genesis_config::create_genesis_config, pubkey::Pubkey, sysvar_cache::SysvarCache, + }, + std::sync::Arc, + }; + + impl Bank { + pub(crate) fn reset_sysvar_cache(&self) { + let mut sysvar_cache = self.sysvar_cache.write().unwrap(); + *sysvar_cache = SysvarCache::default(); + } + } + + #[test] + fn test_sysvar_cache_initialization() { + let (genesis_config, _mint_keypair) = create_genesis_config(100_000); + let bank0 = Arc::new(Bank::new(&genesis_config)); + + let bank0_sysvar_cache = bank0.sysvar_cache.read().unwrap(); + let bank0_cached_clock = bank0_sysvar_cache.get_clock(); + let bank0_cached_epoch_schedule = bank0_sysvar_cache.get_epoch_schedule(); + let bank0_cached_fees = bank0_sysvar_cache.get_fees(); + let bank0_cached_rent = bank0_sysvar_cache.get_rent(); + + assert!(bank0_cached_clock.is_ok()); + assert!(bank0_cached_epoch_schedule.is_ok()); + assert!(bank0_cached_fees.is_ok()); + assert!(bank0_cached_rent.is_ok()); + assert!(bank0 + .sysvar_cache + .read() + .unwrap() + .get_slot_hashes() + .is_err()); + + let bank1 = Bank::new_from_parent(&bank0, &Pubkey::default(), bank0.slot() + 1); + + let bank1_sysvar_cache = bank1.sysvar_cache.read().unwrap(); + let bank1_cached_clock = bank1_sysvar_cache.get_clock(); + let bank1_cached_epoch_schedule = bank0_sysvar_cache.get_epoch_schedule(); + let bank1_cached_fees = bank0_sysvar_cache.get_fees(); + let bank1_cached_rent = bank0_sysvar_cache.get_rent(); + + assert!(bank1_cached_clock.is_ok()); + assert!(bank1_cached_epoch_schedule.is_ok()); + assert!(bank1_cached_fees.is_ok()); + assert!(bank1_cached_rent.is_ok()); + assert!(bank1.sysvar_cache.read().unwrap().get_slot_hashes().is_ok()); + + assert_ne!(bank0_cached_clock, bank1_cached_clock); + assert_eq!(bank0_cached_epoch_schedule, bank1_cached_epoch_schedule); + assert_eq!(bank0_cached_fees, bank1_cached_fees); + assert_eq!(bank0_cached_rent, bank1_cached_rent); + } + + #[test] + fn test_reset_and_fill_sysvar_cache() { + let (genesis_config, _mint_keypair) = create_genesis_config(100_000); + let bank0 = Arc::new(Bank::new(&genesis_config)); + let bank1 = Bank::new_from_parent(&bank0, &Pubkey::default(), bank0.slot() + 1); + + let bank1_sysvar_cache = bank1.sysvar_cache.read().unwrap(); + let bank1_cached_clock = bank1_sysvar_cache.get_clock(); + let bank1_cached_epoch_schedule = bank1_sysvar_cache.get_epoch_schedule(); + let bank1_cached_fees = bank1_sysvar_cache.get_fees(); + let bank1_cached_rent = bank1_sysvar_cache.get_rent(); + let bank1_cached_slot_hashes = bank1_sysvar_cache.get_slot_hashes(); + + assert!(bank1_cached_clock.is_ok()); + assert!(bank1_cached_epoch_schedule.is_ok()); + assert!(bank1_cached_fees.is_ok()); + assert!(bank1_cached_rent.is_ok()); + assert!(bank1_cached_slot_hashes.is_ok()); + + drop(bank1_sysvar_cache); + bank1.reset_sysvar_cache(); + + let bank1_sysvar_cache = bank1.sysvar_cache.read().unwrap(); + assert!(bank1_sysvar_cache.get_clock().is_err()); + assert!(bank1_sysvar_cache.get_epoch_schedule().is_err()); + assert!(bank1_sysvar_cache.get_fees().is_err()); + assert!(bank1_sysvar_cache.get_rent().is_err()); + assert!(bank1_sysvar_cache.get_slot_hashes().is_err()); + + drop(bank1_sysvar_cache); + bank1.fill_missing_sysvar_cache_entries(); + + let bank1_sysvar_cache = bank1.sysvar_cache.read().unwrap(); + assert_eq!(bank1_sysvar_cache.get_clock(), bank1_cached_clock); + assert_eq!( + bank1_sysvar_cache.get_epoch_schedule(), + bank1_cached_epoch_schedule + ); + assert_eq!(bank1_sysvar_cache.get_fees(), bank1_cached_fees); + assert_eq!(bank1_sysvar_cache.get_rent(), bank1_cached_rent); + assert_eq!( + bank1_sysvar_cache.get_slot_hashes(), + bank1_cached_slot_hashes + ); + } } diff --git a/sdk/src/process_instruction.rs b/sdk/src/process_instruction.rs index 061fb6933d..2c26a7a94c 100644 --- a/sdk/src/process_instruction.rs +++ b/sdk/src/process_instruction.rs @@ -7,7 +7,6 @@ use { instruction::{CompiledInstruction, Instruction, InstructionError}, keyed_account::{create_keyed_accounts_unified, KeyedAccount}, pubkey::Pubkey, - sysvar::Sysvar, }, std::{borrow::Cow, cell::RefCell, collections::HashSet, fmt::Debug, rc::Rc, sync::Arc}, }; @@ -145,26 +144,6 @@ macro_rules! ic_msg { }; } -pub fn get_sysvar( - invoke_context: &dyn InvokeContext, - id: &Pubkey, -) -> Result { - invoke_context - .get_sysvar_cache() - .iter() - .find_map(|(key, data)| { - if id == key { - bincode::deserialize(data).ok() - } else { - None - } - }) - .ok_or_else(|| { - ic_msg!(invoke_context, "Unable to get sysvar {}", id); - InstructionError::UnsupportedSysvar - }) -} - #[derive(Clone, Copy, Debug, AbiExample, PartialEq)] pub struct BpfComputeBudget { /// Number of compute units that an instruction is allowed. Compute units diff --git a/sdk/src/sysvar_cache.rs b/sdk/src/sysvar_cache.rs index ba9629c992..71e69b7ca5 100644 --- a/sdk/src/sysvar_cache.rs +++ b/sdk/src/sysvar_cache.rs @@ -1,9 +1,12 @@ use { solana_sdk::{ - account::{AccountSharedData, ReadableAccount}, - pubkey::Pubkey, + instruction::InstructionError, + sysvar::{ + clock::Clock, epoch_schedule::EpochSchedule, fees::Fees, rent::Rent, + slot_hashes::SlotHashes, + }, }, - std::ops::Deref, + std::sync::Arc, }; #[cfg(RUSTC_WITH_SPECIALIZATION)] @@ -15,25 +18,58 @@ impl ::solana_frozen_abi::abi_example::AbiExample for SysvarCache { } #[derive(Default, Clone, Debug)] -pub struct SysvarCache(Vec<(Pubkey, Vec)>); - -impl Deref for SysvarCache { - type Target = Vec<(Pubkey, Vec)>; - fn deref(&self) -> &Self::Target { - &self.0 - } +pub struct SysvarCache { + clock: Option>, + epoch_schedule: Option>, + fees: Option>, + rent: Option>, + slot_hashes: Option>, } impl SysvarCache { - pub fn push_entry(&mut self, pubkey: Pubkey, data: Vec) { - self.0.push((pubkey, data)); + pub fn get_clock(&self) -> Result, InstructionError> { + self.clock + .clone() + .ok_or(InstructionError::UnsupportedSysvar) } - pub fn update_entry(&mut self, pubkey: &Pubkey, new_account: &AccountSharedData) { - if let Some(position) = self.iter().position(|(id, _data)| id == pubkey) { - self.0[position].1 = new_account.data().to_vec(); - } else { - self.0.push((*pubkey, new_account.data().to_vec())); - } + pub fn set_clock(&mut self, clock: Clock) { + self.clock = Some(Arc::new(clock)); + } + + pub fn get_epoch_schedule(&self) -> Result, InstructionError> { + self.epoch_schedule + .clone() + .ok_or(InstructionError::UnsupportedSysvar) + } + + pub fn set_epoch_schedule(&mut self, epoch_schedule: EpochSchedule) { + self.epoch_schedule = Some(Arc::new(epoch_schedule)); + } + + pub fn get_fees(&self) -> Result, InstructionError> { + self.fees.clone().ok_or(InstructionError::UnsupportedSysvar) + } + + pub fn set_fees(&mut self, fees: Fees) { + self.fees = Some(Arc::new(fees)); + } + + pub fn get_rent(&self) -> Result, InstructionError> { + self.rent.clone().ok_or(InstructionError::UnsupportedSysvar) + } + + pub fn set_rent(&mut self, rent: Rent) { + self.rent = Some(Arc::new(rent)); + } + + pub fn get_slot_hashes(&self) -> Result, InstructionError> { + self.slot_hashes + .clone() + .ok_or(InstructionError::UnsupportedSysvar) + } + + pub fn set_slot_hashes(&mut self, slot_hashes: SlotHashes) { + self.slot_hashes = Some(Arc::new(slot_hashes)); } }