diff --git a/program-runtime/src/executor_cache.rs b/program-runtime/src/executor_cache.rs index 412acb9900..678d13b4c2 100644 --- a/program-runtime/src/executor_cache.rs +++ b/program-runtime/src/executor_cache.rs @@ -1,5 +1,5 @@ use { - crate::loaded_programs::LoadedProgram, + crate::loaded_programs::{LoadedProgram, LoadedProgramType}, log::*, rand::Rng, solana_sdk::{pubkey::Pubkey, saturating_add_assign, slot_history::Slot, stake_history::Epoch}, @@ -42,8 +42,13 @@ impl TransactionExecutorCache { } pub fn set_tombstone(&mut self, key: Pubkey, slot: Slot) { - self.visible - .insert(key, Arc::new(LoadedProgram::new_tombstone(slot))); + self.visible.insert( + key, + Arc::new(LoadedProgram::new_tombstone( + slot, + LoadedProgramType::Closed, + )), + ); } pub fn set( @@ -58,7 +63,13 @@ impl TransactionExecutorCache { if delay_visibility_of_program_deployment { // Place a tombstone in the cache so that // we don't load the new version from the database as it should remain invisible - self.set_tombstone(key, current_slot); + self.visible.insert( + key, + Arc::new(LoadedProgram::new_tombstone( + current_slot, + LoadedProgramType::DelayVisibility, + )), + ); } else { self.visible.insert(key, executor.clone()); } diff --git a/program-runtime/src/invoke_context.rs b/program-runtime/src/invoke_context.rs index 6e7b94385b..0f7abe76e0 100644 --- a/program-runtime/src/invoke_context.rs +++ b/program-runtime/src/invoke_context.rs @@ -14,7 +14,6 @@ use { solana_rbpf::vm::ContextObject, solana_sdk::{ account::{AccountSharedData, ReadableAccount}, - bpf_loader_upgradeable::{self, UpgradeableLoaderState}, feature_set::{ enable_early_verification_of_account_modifications, native_programs_consume_cu, FeatureSet, @@ -624,37 +623,11 @@ impl<'a> InvokeContext<'a> { ic_msg!(self, "Account {} is not executable", callee_program_id); return Err(InstructionError::AccountNotExecutable); } - let mut program_indices = vec![]; - if borrowed_program_account.get_owner() == &bpf_loader_upgradeable::id() { - if let UpgradeableLoaderState::Program { - programdata_address, - } = borrowed_program_account.get_state()? - { - if let Some(programdata_account_index) = self - .transaction_context - .find_index_of_program_account(&programdata_address) - { - program_indices.push(programdata_account_index); - } else { - ic_msg!( - self, - "Unknown upgradeable programdata account {}", - programdata_address, - ); - return Err(InstructionError::MissingAccount); - } - } else { - ic_msg!( - self, - "Invalid upgradeable program account {}", - callee_program_id, - ); - return Err(InstructionError::MissingAccount); - } - } - program_indices.push(borrowed_program_account.get_index_in_transaction()); - Ok((instruction_accounts, program_indices)) + Ok(( + instruction_accounts, + vec![borrowed_program_account.get_index_in_transaction()], + )) } /// Processes an instruction and returns how many compute units were used diff --git a/program-runtime/src/loaded_programs.rs b/program-runtime/src/loaded_programs.rs index 9866c73ed0..d91ea90b95 100644 --- a/program-runtime/src/loaded_programs.rs +++ b/program-runtime/src/loaded_programs.rs @@ -58,7 +58,9 @@ pub trait WorkingSlot { pub enum LoadedProgramType { /// Tombstone for undeployed, closed or unloadable programs #[default] - Invalid, + FailedVerification, + Closed, + DelayVisibility, LegacyV0(VerifiedExecutable>), LegacyV1(VerifiedExecutable>), Typed(VerifiedExecutable>), @@ -68,7 +70,11 @@ pub enum LoadedProgramType { impl Debug for LoadedProgramType { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - LoadedProgramType::Invalid => write!(f, "LoadedProgramType::Invalid"), + LoadedProgramType::FailedVerification => { + write!(f, "LoadedProgramType::FailedVerification") + } + LoadedProgramType::Closed => write!(f, "LoadedProgramType::Closed"), + LoadedProgramType::DelayVisibility => write!(f, "LoadedProgramType::DelayVisibility"), LoadedProgramType::LegacyV0(_) => write!(f, "LoadedProgramType::LegacyV0"), LoadedProgramType::LegacyV1(_) => write!(f, "LoadedProgramType::LegacyV1"), LoadedProgramType::Typed(_) => write!(f, "LoadedProgramType::Typed"), @@ -190,18 +196,25 @@ impl LoadedProgram { } } - pub fn new_tombstone(slot: Slot) -> Self { - Self { - program: LoadedProgramType::Invalid, + pub fn new_tombstone(slot: Slot, reason: LoadedProgramType) -> Self { + let tombstone = Self { + program: reason, account_size: 0, deployment_slot: slot, effective_slot: slot, usage_counter: AtomicU64::default(), - } + }; + debug_assert!(tombstone.is_tombstone()); + tombstone } pub fn is_tombstone(&self) -> bool { - matches!(self.program, LoadedProgramType::Invalid) + matches!( + self.program, + LoadedProgramType::FailedVerification + | LoadedProgramType::Closed + | LoadedProgramType::DelayVisibility + ) } } @@ -266,7 +279,15 @@ impl LoadedPrograms { let existing = second_level .get(index) .expect("Missing entry, even though position was found"); - assert!( + if existing.is_tombstone() + && entry.is_tombstone() + && existing.deployment_slot == entry.deployment_slot + { + // If there's already a tombstone for the program at the given slot, let's return + // the existing entry instead of adding another. + return existing.clone(); + } + debug_assert!( existing.deployment_slot != entry.deployment_slot || existing.effective_slot != entry.effective_slot ); @@ -395,7 +416,13 @@ mod tests { } fn set_tombstone(cache: &mut LoadedPrograms, key: Pubkey, slot: Slot) -> Arc { - cache.assign_program(key, Arc::new(LoadedProgram::new_tombstone(slot))) + cache.assign_program( + key, + Arc::new(LoadedProgram::new_tombstone( + slot, + LoadedProgramType::FailedVerification, + )), + ) } #[test] @@ -637,14 +664,17 @@ mod tests { #[test] fn test_tombstone() { - let tombstone = LoadedProgram::new_tombstone(0); - assert!(matches!(tombstone.program, LoadedProgramType::Invalid)); + let tombstone = LoadedProgram::new_tombstone(0, LoadedProgramType::FailedVerification); + assert!(matches!( + tombstone.program, + LoadedProgramType::FailedVerification + )); assert!(tombstone.is_tombstone()); assert_eq!(tombstone.deployment_slot, 0); assert_eq!(tombstone.effective_slot, 0); - let tombstone = LoadedProgram::new_tombstone(100); - assert!(matches!(tombstone.program, LoadedProgramType::Invalid)); + let tombstone = LoadedProgram::new_tombstone(100, LoadedProgramType::Closed); + assert!(matches!(tombstone.program, LoadedProgramType::Closed)); assert!(tombstone.is_tombstone()); assert_eq!(tombstone.deployment_slot, 100); assert_eq!(tombstone.effective_slot, 100); @@ -835,7 +865,7 @@ mod tests { usage_counter: AtomicU64, ) -> Arc { Arc::new(LoadedProgram { - program: LoadedProgramType::Invalid, + program: LoadedProgramType::FailedVerification, account_size: 0, deployment_slot, effective_slot, diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index 56ccbcd07d..ca17a06b4d 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -135,7 +135,7 @@ pub fn load_program_from_bytes( Ok(loaded_program) } -fn get_programdata_offset_and_depoyment_offset( +fn get_programdata_offset_and_deployment_offset( log_collector: &Option>>, program: &BorrowedAccount, programdata: &BorrowedAccount, @@ -181,14 +181,10 @@ pub fn load_program_from_account( return Err(InstructionError::IncorrectProgramId); } - let (programdata_offset, deployment_slot) = - get_programdata_offset_and_depoyment_offset(&log_collector, program, programdata)?; - if let Some(ref tx_executor_cache) = tx_executor_cache { if let Some(loaded_program) = tx_executor_cache.get(program.get_key()) { if loaded_program.is_tombstone() { // We cached that the Executor does not exist, abort - // This case can only happen once delay_visibility_of_program_deployment is active. return Err(InstructionError::InvalidAccountData); } // Executor exists and is cached, use it @@ -196,6 +192,9 @@ pub fn load_program_from_account( } } + let (programdata_offset, deployment_slot) = + get_programdata_offset_and_deployment_offset(&log_collector, program, programdata)?; + let programdata_size = if programdata_offset != 0 { programdata.get_data().len() } else { @@ -556,14 +555,16 @@ fn process_instruction_common( ic_logger_msg!(log_collector, "Program is not executable"); return Err(InstructionError::IncorrectProgramId); } + let programdata_account = if bpf_loader_upgradeable::check_id(program_account.get_owner()) { - let programdata_account = instruction_context.try_borrow_program_account( - transaction_context, - instruction_context - .get_number_of_program_accounts() - .saturating_sub(2), - )?; - Some(programdata_account) + instruction_context + .try_borrow_program_account( + transaction_context, + instruction_context + .get_number_of_program_accounts() + .saturating_sub(2), + ) + .ok() } else { None }; @@ -591,7 +592,9 @@ fn process_instruction_common( executor.usage_counter.fetch_add(1, Ordering::Relaxed); match &executor.program { - LoadedProgramType::Invalid => Err(InstructionError::InvalidAccountData), + LoadedProgramType::FailedVerification + | LoadedProgramType::Closed + | LoadedProgramType::DelayVisibility => Err(InstructionError::InvalidAccountData), LoadedProgramType::LegacyV0(executable) => execute(executable, invoke_context), LoadedProgramType::LegacyV1(executable) => execute(executable, invoke_context), _ => Err(InstructionError::IncorrectProgramId), diff --git a/programs/loader-v3/src/lib.rs b/programs/loader-v3/src/lib.rs index 2e9d4ed31b..a30195894a 100644 --- a/programs/loader-v3/src/lib.rs +++ b/programs/loader-v3/src/lib.rs @@ -590,7 +590,9 @@ pub fn process_instruction(invoke_context: &mut InvokeContext) -> Result<(), Ins drop(program); loaded_program.usage_counter.fetch_add(1, Ordering::Relaxed); match &loaded_program.program { - LoadedProgramType::Invalid => Err(InstructionError::InvalidAccountData), + LoadedProgramType::FailedVerification + | LoadedProgramType::Closed + | LoadedProgramType::DelayVisibility => Err(InstructionError::InvalidAccountData), LoadedProgramType::Typed(executable) => execute(invoke_context, executable), _ => Err(InstructionError::IncorrectProgramId), } diff --git a/runtime/src/accounts.rs b/runtime/src/accounts.rs index 87f85e1807..5a6ce226f5 100644 --- a/runtime/src/accounts.rs +++ b/runtime/src/accounts.rs @@ -24,16 +24,21 @@ use { transaction_error_metrics::TransactionErrorMetrics, }, dashmap::DashMap, + itertools::Itertools, log::*, solana_address_lookup_table_program::{error::AddressLookupError, state::AddressLookupTable}, - solana_program_runtime::compute_budget::{self, ComputeBudget}, + solana_program_runtime::{ + compute_budget::{self, ComputeBudget}, + loaded_programs::{LoadedProgram, LoadedProgramType}, + }, solana_sdk::{ account::{Account, AccountSharedData, ReadableAccount, WritableAccount}, account_utils::StateMut, - bpf_loader_upgradeable::{self, UpgradeableLoaderState}, + bpf_loader_upgradeable, clock::{BankId, Slot}, feature_set::{ - self, add_set_tx_loaded_accounts_data_size_instruction, enable_request_heap_frame_ix, + self, add_set_tx_loaded_accounts_data_size_instruction, + delay_visibility_of_program_deployment, enable_request_heap_frame_ix, include_loaded_accounts_data_size_in_fee_calculation, remove_congestion_multiplier_from_fee_calculation, remove_deprecated_request_unit_ix, use_default_units_in_fee_calculation, FeatureSet, @@ -287,6 +292,38 @@ impl Accounts { } } + fn account_shared_data_from_program( + key: &Pubkey, + feature_set: &FeatureSet, + program: &LoadedProgram, + program_accounts: &HashMap, + ) -> Result { + // Check for tombstone + // Ignoring the tombstone here for now. The loader will catch this condition and return + // error. + let _ignore = match &program.program { + LoadedProgramType::FailedVerification | LoadedProgramType::Closed => { + Err(TransactionError::InvalidProgramForExecution) + } + LoadedProgramType::DelayVisibility => { + debug_assert!(feature_set.is_active(&delay_visibility_of_program_deployment::id())); + Err(TransactionError::InvalidProgramForExecution) + } + _ => Ok(()), + }; + // It's an executable program account. The program is already loaded in the cache. + // So the account data is not needed. Return a dummy AccountSharedData with meta + // information. + let mut program_account = AccountSharedData::default(); + let program_owner = program_accounts + .get(key) + .ok_or(TransactionError::AccountNotFound)?; + program_account.set_owner(**program_owner); + program_account.set_executable(true); + Ok(program_account) + } + + #[allow(clippy::too_many_arguments)] fn load_transaction_accounts( &self, ancestors: &Ancestors, @@ -296,6 +333,8 @@ impl Accounts { rent_collector: &RentCollector, feature_set: &FeatureSet, account_overrides: Option<&AccountOverrides>, + program_accounts: &HashMap, + loaded_programs: &HashMap>, ) -> Result { // NOTE: this check will never fail because `tx` is sanitized if tx.signatures().is_empty() && fee != 0 { @@ -308,7 +347,7 @@ impl Accounts { let mut tx_rent: TransactionRent = 0; let message = tx.message(); let account_keys = message.account_keys(); - let mut account_found_and_dep_index = Vec::with_capacity(account_keys.len()); + let mut accounts_found = Vec::with_capacity(account_keys.len()); let mut account_deps = Vec::with_capacity(account_keys.len()); let mut rent_debits = RentDebits::default(); @@ -319,12 +358,18 @@ impl Accounts { Self::get_requested_loaded_accounts_data_size_limit(tx, feature_set)?; let mut accumulated_accounts_data_size: usize = 0; + let instruction_accounts = message + .instructions() + .iter() + .flat_map(|instruction| &instruction.accounts) + .unique() + .collect::>(); + let mut accounts = account_keys .iter() .enumerate() .map(|(i, key)| { let mut account_found = true; - let mut account_dep_index = None; #[allow(clippy::collapsible_else_if)] let account = if solana_sdk::sysvar::instructions::check_id(key) { Self::construct_instructions_account( @@ -333,10 +378,30 @@ impl Accounts { .is_active(&feature_set::instructions_sysvar_owned_by_sysvar::id()), ) } else { - let (mut account, rent) = if let Some(account_override) = + let instruction_account = u8::try_from(i) + .map(|i| instruction_accounts.contains(&&i)) + .unwrap_or(false); + let (account_size, mut account, rent) = if let Some(account_override) = account_overrides.and_then(|overrides| overrides.get(key)) { - (account_override.clone(), 0) + (account_override.data().len(), account_override.clone(), 0) + } else if let Some(program) = (!instruction_account && !message.is_writable(i)) + .then_some(()) + .and_then(|_| loaded_programs.get(key)) + { + // This condition block does special handling for accounts that are passed + // as instruction account to any of the instructions in the transaction. + // It's been noticed that some programs are reading other program accounts + // (that are passed to the program as instruction accounts). So such accounts + // are needed to be loaded even though corresponding compiled program may + // already be present in the cache. + Self::account_shared_data_from_program( + key, + feature_set, + program, + program_accounts, + ) + .map(|program_account| (program.account_size, program_account, 0))? } else { self.accounts_db .load_with_fixed_root(ancestors, key) @@ -350,9 +415,9 @@ impl Accounts { set_exempt_rent_epoch_max, ) .rent_amount; - (account, rent_due) + (account.data().len(), account, rent_due) } else { - (account, 0) + (account.data().len(), account, 0) } }) .unwrap_or_else(|| { @@ -364,12 +429,12 @@ impl Accounts { // with this field already set would allow us to skip rent collection for these accounts. default_account.set_rent_epoch(u64::MAX); } - (default_account, 0) + (default_account.data().len(), default_account, 0) }) }; Self::accumulate_and_check_loaded_account_data_size( &mut accumulated_accounts_data_size, - account.data().len(), + account_size, requested_loaded_accounts_data_size_limit, error_counters, )?; @@ -397,36 +462,6 @@ impl Accounts { error_counters.invalid_writable_account += 1; return Err(TransactionError::InvalidWritableAccount); } - - if account.executable() { - // The upgradeable loader requires the derived ProgramData account - if let Ok(UpgradeableLoaderState::Program { - programdata_address, - }) = account.state() - { - if let Some((programdata_account, _)) = self - .accounts_db - .load_with_fixed_root(ancestors, &programdata_address) - { - Self::accumulate_and_check_loaded_account_data_size( - &mut accumulated_accounts_data_size, - programdata_account.data().len(), - requested_loaded_accounts_data_size_limit, - error_counters, - )?; - account_dep_index = - Some(account_keys.len().saturating_add(account_deps.len()) - as IndexOfAccount); - account_deps.push((programdata_address, programdata_account)); - } else { - error_counters.account_not_found += 1; - return Err(TransactionError::ProgramAccountNotFound); - } - } else { - error_counters.invalid_program_for_execution += 1; - return Err(TransactionError::InvalidProgramForExecution); - } - } } else if account.executable() && message.is_writable(i) { error_counters.invalid_writable_account += 1; return Err(TransactionError::InvalidWritableAccount); @@ -438,7 +473,7 @@ impl Accounts { account }; - account_found_and_dep_index.push((account_found, account_dep_index)); + accounts_found.push(account_found); Ok((*key, account)) }) .collect::>>()?; @@ -468,9 +503,7 @@ impl Accounts { let (program_id, program_account) = accounts .get(program_index) .ok_or(TransactionError::ProgramAccountNotFound)?; - let (account_found, account_dep_index) = account_found_and_dep_index - .get(program_index) - .unwrap_or(&(true, None)); + let account_found = accounts_found.get(program_index).unwrap_or(&true); if native_loader::check_id(program_id) { return Ok(account_indices); } @@ -483,9 +516,6 @@ impl Accounts { return Err(TransactionError::InvalidProgramForExecution); } account_indices.insert(0, program_index as IndexOfAccount); - if let Some(account_index) = account_dep_index { - account_indices.insert(0, *account_index); - } let owner_id = program_account.owner(); if native_loader::check_id(owner_id) { return Ok(account_indices); @@ -641,6 +671,8 @@ impl Accounts { feature_set: &FeatureSet, fee_structure: &FeeStructure, account_overrides: Option<&AccountOverrides>, + program_accounts: &HashMap, + loaded_programs: &HashMap>, ) -> Vec { txs.iter() .zip(lock_results) @@ -676,6 +708,8 @@ impl Accounts { rent_collector, feature_set, account_overrides, + program_accounts, + loaded_programs, ) { Ok(loaded_transaction) => loaded_transaction, Err(e) => return (Err(e), None), @@ -1416,6 +1450,7 @@ mod tests { solana_program_runtime::executor_cache::TransactionExecutorCache, solana_sdk::{ account::{AccountSharedData, WritableAccount}, + bpf_loader_upgradeable::UpgradeableLoaderState, epoch_schedule::EpochSchedule, genesis_config::ClusterType, hash::Hash, @@ -1500,6 +1535,8 @@ mod tests { feature_set, fee_structure, None, + &HashMap::new(), + &HashMap::new(), ) } @@ -2542,10 +2579,6 @@ mod tests { ); assert_eq!( result.accounts[result.program_indices[0][1] as usize], - accounts[4] - ); - assert_eq!( - result.accounts[result.program_indices[0][2] as usize], accounts[3] ); } @@ -3272,6 +3305,8 @@ mod tests { &FeatureSet::all_enabled(), &FeeStructure::default(), account_overrides, + &HashMap::new(), + &HashMap::new(), ) } diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 32a73e0534..637400dd1a 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -95,7 +95,9 @@ use { compute_budget::{self, ComputeBudget}, executor_cache::{BankExecutorCache, TransactionExecutorCache, MAX_CACHED_EXECUTORS}, invoke_context::{BuiltinProgram, ProcessInstructionWithContext}, - loaded_programs::{LoadedProgram, LoadedProgramEntry, LoadedPrograms, WorkingSlot}, + loaded_programs::{ + LoadedProgram, LoadedProgramEntry, LoadedProgramType, LoadedPrograms, WorkingSlot, + }, log_collector::LogCollector, sysvar_cache::SysvarCache, timings::{ExecuteTimingType, ExecuteTimings}, @@ -4043,6 +4045,7 @@ impl Bank { } /// Get any cached executors needed by the transaction + #[cfg(test)] fn get_tx_executor_cache( &self, accounts: &[TransactionAccount], @@ -4197,15 +4200,8 @@ impl Bank { timings: &mut ExecuteTimings, error_counters: &mut TransactionErrorMetrics, log_messages_bytes_limit: Option, + tx_executor_cache: Rc>, ) -> TransactionExecutionResult { - let mut get_tx_executor_cache_time = Measure::start("get_tx_executor_cache_time"); - let tx_executor_cache = self.get_tx_executor_cache(&loaded_transaction.accounts); - get_tx_executor_cache_time.stop(); - saturating_add_assign!( - timings.execute_accessories.get_executors_us, - get_tx_executor_cache_time.as_us() - ); - let prev_accounts_data_len = self.load_accounts_data_size(); let transaction_accounts = std::mem::take(&mut loaded_transaction.accounts); let mut transaction_context = TransactionContext::new( @@ -4439,11 +4435,13 @@ impl Bank { Err(e) => { // Create a tombstone for the program in the cache debug!("Failed to load program {}, error {:?}", pubkey, e); - let tombstone = self - .loaded_programs_cache - .write() - .unwrap() - .assign_program(*pubkey, Arc::new(LoadedProgram::new_tombstone(self.slot))); + let tombstone = self.loaded_programs_cache.write().unwrap().assign_program( + *pubkey, + Arc::new(LoadedProgram::new_tombstone( + self.slot, + LoadedProgramType::FailedVerification, + )), + ); loaded_programs_for_txs.insert(*pubkey, tombstone); } }); @@ -4451,11 +4449,14 @@ impl Bank { (program_accounts_map, loaded_programs_for_txs) } - fn replenish_executor_cache( + fn replenish_executor_cache<'a>( &self, - program_owners: &[&Pubkey], + program_owners: &[&'a Pubkey], sanitized_txs: &[SanitizedTransaction], check_results: &mut [TransactionCheckResult], + ) -> ( + HashMap, + HashMap>, ) { let mut filter_programs_time = Measure::start("filter_programs_accounts"); let program_accounts_map = self.rc.accounts.filter_executable_program_accounts( @@ -4467,6 +4468,7 @@ impl Bank { ); filter_programs_time.stop(); + let mut loaded_programs_for_txs = HashMap::new(); let mut filter_missing_programs_time = Measure::start("filter_missing_programs_accounts"); let missing_executors = program_accounts_map .keys() @@ -4475,6 +4477,10 @@ impl Bank { .read() .unwrap() .get(key) + .map(|program| { + loaded_programs_for_txs.insert(*key, program.clone()); + program + }) .is_none() .then_some(key) }) @@ -4484,15 +4490,27 @@ impl Bank { let executors = missing_executors .iter() .map(|pubkey| match self.load_program(pubkey) { - Ok(program) => (**pubkey, program), + Ok(program) => { + loaded_programs_for_txs.insert(**pubkey, program.clone()); + (**pubkey, program) + } // Create a tombstone for the programs that failed to load - Err(_) => (**pubkey, Arc::new(LoadedProgram::new_tombstone(self.slot))), + Err(_) => { + let tombstone = Arc::new(LoadedProgram::new_tombstone( + self.slot, + LoadedProgramType::FailedVerification, + )); + loaded_programs_for_txs.insert(**pubkey, tombstone.clone()); + (**pubkey, tombstone) + } }); // avoid locking the cache if there are no new executors if executors.len() > 0 { self.executor_cache.write().unwrap().put(executors); } + + (program_accounts_map, loaded_programs_for_txs) } #[allow(clippy::type_complexity)] @@ -4561,7 +4579,6 @@ impl Bank { bpf_loader_upgradeable::id(), bpf_loader::id(), bpf_loader_deprecated::id(), - native_loader::id(), ]; let program_owners_refs: Vec<&Pubkey> = program_owners.iter().collect(); @@ -4574,7 +4591,12 @@ impl Bank { &check_results, ); */ - self.replenish_executor_cache(&program_owners_refs, sanitized_txs, &mut check_results); + let (executable_programs_in_tx_batch, loaded_programs_map) = + self.replenish_executor_cache(&program_owners_refs, sanitized_txs, &mut check_results); + + let tx_executor_cache = Rc::new(RefCell::new(TransactionExecutorCache::new( + loaded_programs_map.clone().into_iter(), + ))); let mut load_time = Measure::start("accounts_load"); let mut loaded_transactions = self.rc.accounts.load_accounts( @@ -4587,6 +4609,8 @@ impl Bank { &self.feature_set, &self.fee_structure, account_overrides, + &executable_programs_in_tx_batch, + &loaded_programs_map, ); load_time.stop(); @@ -4643,6 +4667,7 @@ impl Bank { timings, &mut error_counters, log_messages_bytes_limit, + tx_executor_cache.clone(), ) } }) diff --git a/runtime/src/bank/tests.rs b/runtime/src/bank/tests.rs index f804a5e22c..8d21a4d7bc 100644 --- a/runtime/src/bank/tests.rs +++ b/runtime/src/bank/tests.rs @@ -11740,6 +11740,8 @@ fn test_rent_state_list_len() { &bank.feature_set, &FeeStructure::default(), None, + &HashMap::new(), + &HashMap::new(), ); let compute_budget = bank.runtime_config.compute_budget.unwrap_or_else(|| {