diff --git a/program-test/src/lib.rs b/program-test/src/lib.rs index cf02a6e355..ca6cfa7d3d 100644 --- a/program-test/src/lib.rs +++ b/program-test/src/lib.rs @@ -32,7 +32,7 @@ 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, @@ -196,6 +196,24 @@ fn get_sysvar( var_addr: *mut u8, ) -> u64 { let invoke_context = get_invoke_context(); + + let sysvar_data = match invoke_context.get_sysvar_data(id).ok_or_else(|| { + ic_msg!(invoke_context, "Unable to get Sysvar {}", id); + UNSUPPORTED_SYSVAR + }) { + Ok(sysvar_data) => sysvar_data, + Err(err) => return err, + }; + + let var: T = match bincode::deserialize(&sysvar_data) { + Ok(sysvar_data) => sysvar_data, + Err(_) => return UNSUPPORTED_SYSVAR, + }; + + unsafe { + *(var_addr as *mut _ as *mut T) = var; + } + if invoke_context .get_compute_meter() .try_borrow_mut() @@ -206,13 +224,8 @@ fn get_sysvar( { panic!("Exceeded compute budget"); } - match process_instruction::get_sysvar::(invoke_context, id) { - Ok(sysvar_data) => unsafe { - *(var_addr as *mut _ as *mut T) = sysvar_data; - SUCCESS - }, - Err(_) => UNSUPPORTED_SYSVAR, - } + + SUCCESS } struct SyscallStubs {} diff --git a/programs/bpf_loader/src/syscalls.rs b/programs/bpf_loader/src/syscalls.rs index d5caeeadc6..d8803dfc6e 100644 --- a/programs/bpf_loader/src/syscalls.rs +++ b/programs/bpf_loader/src/syscalls.rs @@ -3650,7 +3650,9 @@ mod tests { let mut invoke_context = MockInvokeContext::new(vec![]); let mut data = vec![]; bincode::serialize_into(&mut data, &src_clock).unwrap(); - invoke_context.sysvars = vec![(sysvar::clock::id(), data)]; + invoke_context + .sysvars + .push((sysvar::clock::id(), Some(Rc::new(data)))); let mut syscall = SyscallGetClockSysvar { invoke_context: Rc::new(RefCell::new(&mut invoke_context)), @@ -3693,7 +3695,9 @@ mod tests { let mut invoke_context = MockInvokeContext::new(vec![]); let mut data = vec![]; bincode::serialize_into(&mut data, &src_epochschedule).unwrap(); - invoke_context.sysvars = vec![(sysvar::epoch_schedule::id(), data)]; + invoke_context + .sysvars + .push((sysvar::epoch_schedule::id(), Some(Rc::new(data)))); let mut syscall = SyscallGetEpochScheduleSysvar { invoke_context: Rc::new(RefCell::new(&mut invoke_context)), @@ -3742,7 +3746,9 @@ mod tests { let mut invoke_context = MockInvokeContext::new(vec![]); let mut data = vec![]; bincode::serialize_into(&mut data, &src_fees).unwrap(); - invoke_context.sysvars = vec![(sysvar::fees::id(), data)]; + invoke_context + .sysvars + .push((sysvar::fees::id(), Some(Rc::new(data)))); let mut syscall = SyscallGetFeesSysvar { invoke_context: Rc::new(RefCell::new(&mut invoke_context)), @@ -3783,7 +3789,9 @@ mod tests { let mut invoke_context = MockInvokeContext::new(vec![]); let mut data = vec![]; bincode::serialize_into(&mut data, &src_rent).unwrap(); - invoke_context.sysvars = vec![(sysvar::rent::id(), data)]; + invoke_context + .sysvars + .push((sysvar::rent::id(), Some(Rc::new(data)))); let mut syscall = SyscallGetRentSysvar { invoke_context: Rc::new(RefCell::new(&mut invoke_context)), diff --git a/programs/stake/src/stake_instruction.rs b/programs/stake/src/stake_instruction.rs index 74bd839870..ae37c58059 100644 --- a/programs/stake/src/stake_instruction.rs +++ b/programs/stake/src/stake_instruction.rs @@ -281,16 +281,16 @@ mod tests { account::{self, Account, AccountSharedData, WritableAccount}, instruction::{AccountMeta, Instruction}, keyed_account::KeyedAccount, - process_instruction::MockInvokeContext, + process_instruction::{mock_set_sysvar, MockInvokeContext}, rent::Rent, stake::{ config as stake_config, instruction::{self, LockupArgs}, state::{Authorized, Lockup, StakeAuthorize}, }, - sysvar::{stake_history::StakeHistory, Sysvar}, + sysvar::stake_history::StakeHistory, }, - std::{cell::RefCell, str::FromStr}, + std::{cell::RefCell, rc::Rc, str::FromStr}, }; fn create_default_account() -> RefCell { @@ -370,9 +370,12 @@ mod tests { .collect(); let mut invoke_context = MockInvokeContext::new(keyed_accounts); - let mut data = Vec::with_capacity(sysvar::clock::Clock::size_of()); - bincode::serialize_into(&mut data, &sysvar::clock::Clock::default()).unwrap(); - invoke_context.sysvars = vec![(sysvar::clock::id(), data)]; + mock_set_sysvar( + &mut invoke_context, + sysvar::clock::id(), + sysvar::clock::Clock::default(), + ) + .unwrap(); super::process_instruction(&Pubkey::default(), &instruction.data, &mut invoke_context) } } @@ -1035,9 +1038,12 @@ mod tests { ]; let mut invoke_context = MockInvokeContext::new(keyed_accounts); - let mut data = Vec::with_capacity(sysvar::clock::Clock::size_of()); - bincode::serialize_into(&mut data, &sysvar::clock::Clock::default()).unwrap(); - invoke_context.sysvars = vec![(sysvar::clock::id(), data)]; + let clock = Clock::default(); + let mut data = vec![]; + bincode::serialize_into(&mut data, &clock).unwrap(); + invoke_context + .sysvars + .push((sysvar::clock::id(), Some(Rc::new(data)))); assert_eq!( super::process_instruction( diff --git a/programs/vote/src/vote_instruction.rs b/programs/vote/src/vote_instruction.rs index 3af13d2e19..22b13bac6f 100644 --- a/programs/vote/src/vote_instruction.rs +++ b/programs/vote/src/vote_instruction.rs @@ -402,9 +402,8 @@ mod tests { bincode::serialize, solana_sdk::{ account::{self, Account, AccountSharedData}, - process_instruction::MockInvokeContext, + process_instruction::{mock_set_sysvar, MockInvokeContext}, rent::Rent, - sysvar::Sysvar, }, std::{cell::RefCell, str::FromStr}, }; @@ -463,9 +462,12 @@ mod tests { .map(|(meta, account)| KeyedAccount::new(&meta.pubkey, meta.is_signer, account)) .collect(); let mut invoke_context = MockInvokeContext::new(keyed_accounts); - let mut data = Vec::with_capacity(sysvar::rent::Rent::size_of()); - bincode::serialize_into(&mut data, &sysvar::rent::Rent::default()).unwrap(); - invoke_context.sysvars = vec![(sysvar::rent::id(), data)]; + mock_set_sysvar( + &mut invoke_context, + sysvar::rent::id(), + sysvar::rent::Rent::default(), + ) + .unwrap(); super::process_instruction(&Pubkey::default(), &instruction.data, &mut invoke_context) } } diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 7cf44e0cad..f951d29403 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -1184,8 +1184,6 @@ pub struct Bank { vote_only_bank: bool, pub cost_tracker: RwLock, - - sysvar_cache: RwLock)>>, } impl Default for BlockhashQueue { @@ -1318,7 +1316,6 @@ impl Bank { bank.update_rent(); bank.update_epoch_schedule(); bank.update_recent_blockhashes(); - bank.fill_sysvar_cache(); bank } @@ -1459,7 +1456,6 @@ impl Bank { )), freeze_started: AtomicBool::new(false), cost_tracker: RwLock::new(CostTracker::default()), - sysvar_cache: RwLock::new(Vec::new()), }; let mut ancestors = Vec::with_capacity(1 + new.parents().len()); @@ -1501,7 +1497,6 @@ impl Bank { if !new.fix_recent_blockhashes_sysvar_delay() { new.update_recent_blockhashes(); } - new.fill_sysvar_cache(); time.stop(); @@ -1636,7 +1631,6 @@ impl Bank { freeze_started: AtomicBool::new(fields.hash != Hash::default()), vote_only_bank: false, cost_tracker: RwLock::new(CostTracker::default()), - sysvar_cache: RwLock::new(Vec::new()), }; bank.finish_init( genesis_config, @@ -1812,14 +1806,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(); - if let Some(position) = sysvar_cache.iter().position(|(id, _data)| id == pubkey) { - sysvar_cache[position].1 = new_account.data().to_vec(); - } else { - sysvar_cache.push((*pubkey, new_account.data().to_vec())); - } } fn inherit_specially_retained_account_fields( @@ -1957,17 +1943,6 @@ impl Bank { }); } - fn fill_sysvar_cache(&mut 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((*id, account.data().to_vec())); - } - } - } - } - pub fn get_slot_history(&self) -> SlotHistory { from_account(&self.get_account(&sysvar::slot_history::id()).unwrap()).unwrap() } @@ -3623,7 +3598,8 @@ impl Bank { bpf_compute_budget, compute_meter, &mut timings.details, - &*self.sysvar_cache.read().unwrap(), + self.rc.accounts.clone(), + &self.ancestors, ); transaction_log_messages.push(Self::collect_log_messages(log_collector)); diff --git a/runtime/src/message_processor.rs b/runtime/src/message_processor.rs index c4cc89404f..aabc1ac3f8 100644 --- a/runtime/src/message_processor.rs +++ b/runtime/src/message_processor.rs @@ -1,7 +1,8 @@ use { crate::{ - bank::TransactionAccountRefCell, instruction_recorder::InstructionRecorder, - log_collector::LogCollector, native_loader::NativeLoader, rent_collector::RentCollector, + accounts::Accounts, ancestors::Ancestors, bank::TransactionAccountRefCell, + instruction_recorder::InstructionRecorder, log_collector::LogCollector, + native_loader::NativeLoader, rent_collector::RentCollector, }, log::*, serde::{Deserialize, Serialize}, @@ -370,7 +371,6 @@ pub struct ThisInvokeContext<'a> { pre_accounts: Vec, accounts: &'a [TransactionAccountRefCell], programs: &'a [(Pubkey, ProcessInstructionWithContext)], - sysvars: &'a [(Pubkey, Vec)], logger: Rc>, bpf_compute_budget: BpfComputeBudget, compute_meter: Rc>, @@ -378,6 +378,10 @@ pub struct ThisInvokeContext<'a> { instruction_recorder: Option, feature_set: Arc, pub timings: ExecuteDetailsTimings, + account_db: Arc, + ancestors: &'a Ancestors, + #[allow(clippy::type_complexity)] + sysvars: RefCell>>)>>, // return data and program_id that set it return_data: Option<(Pubkey, Vec)>, } @@ -391,13 +395,14 @@ impl<'a> ThisInvokeContext<'a> { executable_accounts: &'a [TransactionAccountRefCell], accounts: &'a [TransactionAccountRefCell], programs: &'a [(Pubkey, ProcessInstructionWithContext)], - sysvars: &'a [(Pubkey, Vec)], log_collector: Option>, bpf_compute_budget: BpfComputeBudget, compute_meter: Rc>, executors: Rc>, instruction_recorder: Option, feature_set: Arc, + account_db: Arc, + ancestors: &'a Ancestors, ) -> Self { let pre_accounts = MessageProcessor::create_pre_accounts(message, instruction, accounts); let keyed_accounts = MessageProcessor::create_keyed_accounts( @@ -420,7 +425,6 @@ impl<'a> ThisInvokeContext<'a> { pre_accounts, accounts, programs, - sysvars, logger: Rc::new(RefCell::new(ThisLogger { log_collector })), bpf_compute_budget, compute_meter, @@ -428,6 +432,9 @@ impl<'a> ThisInvokeContext<'a> { instruction_recorder, feature_set, timings: ExecuteDetailsTimings::default(), + account_db, + ancestors, + sysvars: RefCell::new(vec![]), return_data: None, }; invoke_context @@ -600,8 +607,25 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> { self.timings.execute_us += execute_us; self.timings.deserialize_us += deserialize_us; } - fn get_sysvars(&self) -> &[(Pubkey, Vec)] { - self.sysvars + fn get_sysvar_data(&self, id: &Pubkey) -> Option>> { + if let Ok(mut sysvars) = self.sysvars.try_borrow_mut() { + // Try share from cache + let mut result = sysvars + .iter() + .find_map(|(key, sysvar)| if id == key { sysvar.clone() } else { None }); + if result.is_none() { + // Load it + result = self + .account_db + .load_with_fixed_root(self.ancestors, id) + .map(|(account, _)| Rc::new(account.data().to_vec())); + // Cache it + sysvars.push((*id, result.clone())); + } + result + } else { + None + } } fn set_return_data(&mut self, return_data: Option<(Pubkey, Vec)>) { self.return_data = return_data; @@ -1274,7 +1298,6 @@ impl MessageProcessor { executable_accounts: &[TransactionAccountRefCell], accounts: &[TransactionAccountRefCell], rent_collector: &RentCollector, - sysvars: &[(Pubkey, Vec)], log_collector: Option>, executors: Rc>, instruction_recorder: Option, @@ -1283,6 +1306,8 @@ impl MessageProcessor { bpf_compute_budget: BpfComputeBudget, compute_meter: Rc>, timings: &mut ExecuteDetailsTimings, + account_db: Arc, + ancestors: &Ancestors, ) -> Result<(), InstructionError> { // Fixup the special instructions key if present // before the account pre-values are taken care of @@ -1324,13 +1349,14 @@ impl MessageProcessor { executable_accounts, accounts, &self.programs, - sysvars, log_collector, bpf_compute_budget, compute_meter, executors, instruction_recorder, feature_set, + account_db, + ancestors, ); let pre_remaining_units = invoke_context.get_compute_meter().borrow().get_remaining(); let mut time = Measure::start("execute_instruction"); @@ -1385,7 +1411,8 @@ impl MessageProcessor { bpf_compute_budget: BpfComputeBudget, compute_meter: Rc>, timings: &mut ExecuteDetailsTimings, - sysvars: &[(Pubkey, Vec)], + account_db: Arc, + ancestors: &Ancestors, ) -> Result<(), TransactionError> { for (instruction_index, instruction) in message.instructions.iter().enumerate() { let instruction_recorder = instruction_recorders @@ -1398,7 +1425,6 @@ impl MessageProcessor { &loaders[instruction_index], accounts, rent_collector, - sysvars, log_collector.clone(), executors.clone(), instruction_recorder, @@ -1407,6 +1433,8 @@ impl MessageProcessor { bpf_compute_budget, compute_meter.clone(), timings, + account_db.clone(), + ancestors, ) .map_err(|err| TransactionError::InstructionError(instruction_index as u8, err)); @@ -1464,6 +1492,7 @@ mod tests { &[Instruction::new_with_bytes(invoke_stack[0], &[0], metas)], None, ); + let ancestors = Ancestors::default(); let mut invoke_context = ThisInvokeContext::new( &invoke_stack[0], Rent::default(), @@ -1472,13 +1501,14 @@ mod tests { &[], &accounts, &[], - &[], None, BpfComputeBudget::default(), Rc::new(RefCell::new(MockComputeMeter::default())), Rc::new(RefCell::new(Executors::default())), None, Arc::new(FeatureSet::all_enabled()), + Arc::new(Accounts::default()), + &ancestors, ); // Check call depth increases and has a limit @@ -2062,6 +2092,7 @@ mod tests { let loaders = vec![vec![(mock_system_program_id, account)]]; let executors = Rc::new(RefCell::new(Executors::default())); + let ancestors = Ancestors::default(); let account_metas = vec![ AccountMeta::new(accounts[0].0, true), @@ -2088,7 +2119,8 @@ mod tests { BpfComputeBudget::new(), Rc::new(RefCell::new(MockComputeMeter::default())), &mut ExecuteDetailsTimings::default(), - &[], + Arc::new(Accounts::default()), + &ancestors, ); assert_eq!(result, Ok(())); assert_eq!(accounts[0].1.borrow().lamports(), 100); @@ -2115,7 +2147,8 @@ mod tests { BpfComputeBudget::new(), Rc::new(RefCell::new(MockComputeMeter::default())), &mut ExecuteDetailsTimings::default(), - &[], + Arc::new(Accounts::default()), + &ancestors, ); assert_eq!( result, @@ -2146,7 +2179,8 @@ mod tests { BpfComputeBudget::new(), Rc::new(RefCell::new(MockComputeMeter::default())), &mut ExecuteDetailsTimings::default(), - &[], + Arc::new(Accounts::default()), + &ancestors, ); assert_eq!( result, @@ -2240,6 +2274,7 @@ mod tests { let loaders = vec![vec![(mock_program_id, account)]]; let executors = Rc::new(RefCell::new(Executors::default())); + let ancestors = Ancestors::default(); let account_metas = vec![ AccountMeta::new(accounts[0].0, true), @@ -2268,7 +2303,8 @@ mod tests { BpfComputeBudget::new(), Rc::new(RefCell::new(MockComputeMeter::default())), &mut ExecuteDetailsTimings::default(), - &[], + Arc::new(Accounts::default()), + &ancestors, ); assert_eq!( result, @@ -2299,7 +2335,8 @@ mod tests { BpfComputeBudget::new(), Rc::new(RefCell::new(MockComputeMeter::default())), &mut ExecuteDetailsTimings::default(), - &[], + Arc::new(Accounts::default()), + &ancestors, ); assert_eq!(result, Ok(())); @@ -2315,6 +2352,7 @@ mod tests { )], Some(&accounts[0].0), ); + let ancestors = Ancestors::default(); let result = message_processor.process_message( &message, &loaders, @@ -2327,7 +2365,8 @@ mod tests { BpfComputeBudget::new(), Rc::new(RefCell::new(MockComputeMeter::default())), &mut ExecuteDetailsTimings::default(), - &[], + Arc::new(Accounts::default()), + &ancestors, ); assert_eq!(result, Ok(())); assert_eq!(accounts[0].1.borrow().lamports(), 80); @@ -2428,6 +2467,7 @@ mod tests { let feature_set = FeatureSet::all_enabled(); let demote_program_write_locks = feature_set.is_active(&demote_program_write_locks::id()); + let ancestors = Ancestors::default(); let mut invoke_context = ThisInvokeContext::new( &caller_program_id, Rent::default(), @@ -2436,13 +2476,14 @@ mod tests { &executable_accounts, &accounts, programs.as_slice(), - &[], None, BpfComputeBudget::default(), Rc::new(RefCell::new(MockComputeMeter::default())), Rc::new(RefCell::new(Executors::default())), None, Arc::new(feature_set), + Arc::new(Accounts::default()), + &ancestors, ); // not owned account modified by the caller (before the invoke) @@ -2497,6 +2538,7 @@ mod tests { Instruction::new_with_bincode(callee_program_id, &case.0, metas.clone()); let message = Message::new(&[callee_instruction], None); + let ancestors = Ancestors::default(); let mut invoke_context = ThisInvokeContext::new( &caller_program_id, Rent::default(), @@ -2505,13 +2547,14 @@ mod tests { &executable_accounts, &accounts, programs.as_slice(), - &[], None, BpfComputeBudget::default(), Rc::new(RefCell::new(MockComputeMeter::default())), Rc::new(RefCell::new(Executors::default())), None, Arc::new(FeatureSet::all_enabled()), + Arc::new(Accounts::default()), + &ancestors, ); let caller_write_privileges = message @@ -2648,6 +2691,7 @@ mod tests { ); let message = Message::new(&[callee_instruction.clone()], None); + let ancestors = Ancestors::default(); let mut invoke_context = ThisInvokeContext::new( &caller_program_id, Rent::default(), @@ -2656,13 +2700,14 @@ mod tests { &executable_accounts, &accounts, programs.as_slice(), - &[], None, BpfComputeBudget::default(), Rc::new(RefCell::new(MockComputeMeter::default())), Rc::new(RefCell::new(Executors::default())), None, Arc::new(FeatureSet::all_enabled()), + Arc::new(Accounts::default()), + &ancestors, ); // not owned account modified by the invoker @@ -2713,6 +2758,7 @@ mod tests { Instruction::new_with_bincode(callee_program_id, &case.0, metas.clone()); let message = Message::new(&[callee_instruction.clone()], None); + let ancestors = Ancestors::default(); let mut invoke_context = ThisInvokeContext::new( &caller_program_id, Rent::default(), @@ -2721,13 +2767,14 @@ mod tests { &executable_accounts, &accounts, programs.as_slice(), - &[], None, BpfComputeBudget::default(), Rc::new(RefCell::new(MockComputeMeter::default())), Rc::new(RefCell::new(Executors::default())), None, Arc::new(FeatureSet::all_enabled()), + Arc::new(Accounts::default()), + &ancestors, ); assert_eq!( diff --git a/sdk/program/src/sysvar/mod.rs b/sdk/program/src/sysvar/mod.rs index 0d6ea40dc6..f6a539a373 100644 --- a/sdk/program/src/sysvar/mod.rs +++ b/sdk/program/src/sysvar/mod.rs @@ -1,7 +1,6 @@ //! named accounts for synthesized data accounts for bank state, etc. //! use crate::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; -use lazy_static::lazy_static; pub mod clock; pub mod epoch_schedule; @@ -14,25 +13,17 @@ pub mod slot_hashes; pub mod slot_history; pub mod stake_history; -lazy_static! { - pub static ref ALL_IDS: Vec = vec![ - clock::id(), - epoch_schedule::id(), - #[allow(deprecated)] - fees::id(), - #[allow(deprecated)] - recent_blockhashes::id(), - rent::id(), - rewards::id(), - slot_hashes::id(), - slot_history::id(), - stake_history::id(), - instructions::id(), - ]; -} - pub fn is_sysvar_id(id: &Pubkey) -> bool { - ALL_IDS.iter().any(|key| key == id) + clock::check_id(id) + || epoch_schedule::check_id(id) + || fees::check_id(id) + || recent_blockhashes::check_id(id) + || rent::check_id(id) + || rewards::check_id(id) + || slot_hashes::check_id(id) + || slot_history::check_id(id) + || stake_history::check_id(id) + || instructions::check_id(id) } #[macro_export] diff --git a/sdk/src/process_instruction.rs b/sdk/src/process_instruction.rs index 18823ced01..d479c93231 100644 --- a/sdk/src/process_instruction.rs +++ b/sdk/src/process_instruction.rs @@ -103,8 +103,8 @@ pub trait InvokeContext { execute_us: u64, deserialize_us: u64, ); - /// Get sysvars - fn get_sysvars(&self) -> &[(Pubkey, Vec)]; + /// Get sysvar data + fn get_sysvar_data(&self, id: &Pubkey) -> Option>>; /// Set the return data fn set_return_data(&mut self, return_data: Option<(Pubkey, Vec)>); /// Get the return data @@ -145,20 +145,15 @@ pub fn get_sysvar( invoke_context: &dyn InvokeContext, id: &Pubkey, ) -> Result { - invoke_context - .get_sysvars() - .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 - }) + let sysvar_data = invoke_context.get_sysvar_data(id).ok_or_else(|| { + ic_msg!(invoke_context, "Unable to get sysvar {}", id); + InstructionError::UnsupportedSysvar + })?; + + bincode::deserialize(&sysvar_data).map_err(|err| { + ic_msg!(invoke_context, "Unable to get sysvar {}: {:?}", id, err); + InstructionError::UnsupportedSysvar + }) } #[derive(Clone, Copy, Debug, AbiExample, PartialEq)] @@ -391,7 +386,7 @@ pub struct MockInvokeContext<'a> { pub compute_meter: MockComputeMeter, pub programs: Vec<(Pubkey, ProcessInstructionWithContext)>, pub accounts: Vec<(Pubkey, Rc>)>, - pub sysvars: Vec<(Pubkey, Vec)>, + pub sysvars: Vec<(Pubkey, Option>>)>, pub disabled_features: HashSet, pub return_data: Option<(Pubkey, Vec)>, } @@ -422,6 +417,21 @@ impl<'a> MockInvokeContext<'a> { } } +pub fn mock_set_sysvar( + mock_invoke_context: &mut MockInvokeContext, + id: Pubkey, + sysvar: T, +) -> Result<(), InstructionError> { + let mut data = Vec::with_capacity(T::size_of()); + + bincode::serialize_into(&mut data, &sysvar).map_err(|err| { + ic_msg!(mock_invoke_context, "Unable to serialize sysvar: {:?}", err); + InstructionError::GenericError + })?; + mock_invoke_context.sysvars.push((id, Some(Rc::new(data)))); + Ok(()) +} + impl<'a> InvokeContext for MockInvokeContext<'a> { fn push( &mut self, @@ -509,8 +519,10 @@ impl<'a> InvokeContext for MockInvokeContext<'a> { _deserialize_us: u64, ) { } - fn get_sysvars(&self) -> &[(Pubkey, Vec)] { - &self.sysvars + fn get_sysvar_data(&self, id: &Pubkey) -> Option>> { + self.sysvars + .iter() + .find_map(|(key, sysvar)| if id == key { sysvar.clone() } else { None }) } fn set_return_data(&mut self, return_data: Option<(Pubkey, Vec)>) { self.return_data = return_data;