From 4753a8aefaf5fdc2b40a579334bff04ada20a502 Mon Sep 17 00:00:00 2001 From: Lucas Steuernagel <38472950+LucasSte@users.noreply.github.com> Date: Tue, 9 Apr 2024 16:55:09 -0300 Subject: [PATCH] Move `add_builtin` to SVM (#547) --- program-test/src/lib.rs | 5 +- programs/sbf/tests/programs.rs | 5 +- runtime/benches/bank.rs | 1 + runtime/src/bank.rs | 127 ++++++++---------- .../bank/builtins/core_bpf_migration/mod.rs | 20 ++- runtime/src/bank/tests.rs | 11 +- runtime/src/snapshot_minimizer.rs | 5 +- svm/src/transaction_processor.rs | 33 ++++- svm/tests/integration_test.rs | 104 +++++++------- svm/tests/mock_bank.rs | 17 ++- 10 files changed, 190 insertions(+), 138 deletions(-) diff --git a/program-test/src/lib.rs b/program-test/src/lib.rs index b6f53ac728..2f2fd74e45 100644 --- a/program-test/src/lib.rs +++ b/program-test/src/lib.rs @@ -806,7 +806,7 @@ impl ProgramTest { debug!("Payer address: {}", mint_keypair.pubkey()); debug!("Genesis config: {}", genesis_config); - let mut bank = Bank::new_with_paths( + let bank = Bank::new_with_paths( &genesis_config, Arc::new(RuntimeConfig { compute_budget: self.compute_max_units.map(|max_units| ComputeBudget { @@ -837,7 +837,8 @@ impl ProgramTest { let mut builtin_programs = Vec::new(); std::mem::swap(&mut self.builtin_programs, &mut builtin_programs); for (program_id, name, builtin) in builtin_programs.into_iter() { - bank.add_builtin(program_id, name, builtin); + bank.get_transaction_processor() + .add_builtin(&bank, program_id, name, builtin); } for (address, account) in self.accounts.iter() { diff --git a/programs/sbf/tests/programs.rs b/programs/sbf/tests/programs.rs index 187c06e31d..fa67dd1134 100644 --- a/programs/sbf/tests/programs.rs +++ b/programs/sbf/tests/programs.rs @@ -4278,7 +4278,7 @@ fn test_cpi_change_account_data_memory_allocation() { .. } = create_genesis_config(100_123_456_789); - let mut bank = Bank::new_for_tests(&genesis_config); + let bank = Bank::new_for_tests(&genesis_config); declare_process_instruction!(MockBuiltin, 42, |invoke_context| { let transaction_context = &invoke_context.transaction_context; @@ -4314,7 +4314,8 @@ fn test_cpi_change_account_data_memory_allocation() { }); let builtin_program_id = Pubkey::new_unique(); - bank.add_builtin( + bank.get_transaction_processor().add_builtin( + &bank, builtin_program_id, "test_cpi_change_account_data_memory_allocation_builtin", LoadedProgram::new_builtin(0, 42, MockBuiltin::vm), diff --git a/runtime/benches/bank.rs b/runtime/benches/bank.rs index b97236163c..a64d075c5e 100644 --- a/runtime/benches/bank.rs +++ b/runtime/benches/bank.rs @@ -20,6 +20,7 @@ use { signature::{Keypair, Signer}, transaction::Transaction, }, + solana_svm::transaction_processor::TransactionProcessingCallback, std::{sync::Arc, thread::sleep, time::Duration}, test::Bencher, }; diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 2a4397f8de..b7e8217ada 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -555,7 +555,6 @@ impl PartialEq for Bank { epoch_stakes, is_delta, // TODO: Confirm if all these fields are intentionally ignored! - builtin_program_ids: _, runtime_config: _, rewards: _, cluster_type: _, @@ -761,8 +760,6 @@ pub struct Bank { /// stream for the slot == self.slot is_delta: AtomicBool, - builtin_program_ids: HashSet, - /// Optional config parameters that can override runtime behavior pub(crate) runtime_config: Arc, @@ -923,7 +920,6 @@ impl Bank { stakes_cache: StakesCache::default(), epoch_stakes: HashMap::::default(), is_delta: AtomicBool::default(), - builtin_program_ids: HashSet::::default(), runtime_config: Arc::::default(), rewards: RwLock::>::default(), cluster_type: Option::::default(), @@ -958,6 +954,7 @@ impl Bank { Slot::default(), Epoch::default(), ))), + HashSet::default(), ); let accounts_data_size_initial = bank.get_total_accounts_stats().unwrap().data_len as u64; @@ -1110,8 +1107,12 @@ impl Bank { let (epoch_stakes, epoch_stakes_time_us) = measure_us!(parent.epoch_stakes.clone()); - let (builtin_program_ids, builtin_program_ids_time_us) = - measure_us!(parent.builtin_program_ids.clone()); + let (builtin_program_ids, builtin_program_ids_time_us) = measure_us!(parent + .transaction_processor + .builtin_program_ids + .read() + .unwrap() + .clone()); let (rewards_pool_pubkeys, rewards_pool_pubkeys_time_us) = measure_us!(parent.rewards_pool_pubkeys.clone()); @@ -1167,7 +1168,6 @@ impl Bank { ancestors: Ancestors::default(), hash: RwLock::new(Hash::default()), is_delta: AtomicBool::new(false), - builtin_program_ids, tick_height: AtomicU64::new(parent.tick_height.load(Relaxed)), signature_count: AtomicU64::new(0), runtime_config: parent.runtime_config.clone(), @@ -1208,6 +1208,7 @@ impl Bank { new.fee_structure.clone(), new.runtime_config.clone(), parent.transaction_processor.program_cache.clone(), + builtin_program_ids, ); let (_, ancestors_time_us) = measure_us!({ @@ -1629,7 +1630,6 @@ impl Bank { stakes_cache: StakesCache::new(stakes), epoch_stakes: fields.epoch_stakes, is_delta: AtomicBool::new(fields.is_delta), - builtin_program_ids: HashSet::::default(), runtime_config, rewards: RwLock::new(vec![]), cluster_type: Some(genesis_config.cluster_type), @@ -1662,6 +1662,7 @@ impl Bank { bank.fee_structure.clone(), bank.runtime_config.clone(), Arc::new(RwLock::new(ProgramCache::new(fields.slot, fields.epoch))), + HashSet::default(), ); bank.finish_init( @@ -3153,44 +3154,6 @@ impl Bank { self.calculate_and_update_accounts_data_size_delta_off_chain(old_data_size, 0); } - // NOTE: must hold idempotent for the same set of arguments - /// Add a builtin program account - pub fn add_builtin_account(&self, name: &str, program_id: &Pubkey) { - let existing_genuine_program = - self.get_account_with_fixed_root(program_id) - .and_then(|account| { - // it's very unlikely to be squatted at program_id as non-system account because of burden to - // find victim's pubkey/hash. So, when account.owner is indeed native_loader's, it's - // safe to assume it's a genuine program. - if native_loader::check_id(account.owner()) { - Some(account) - } else { - // malicious account is pre-occupying at program_id - self.burn_and_purge_account(program_id, account); - None - } - }); - - // introducing builtin program - if existing_genuine_program.is_some() { - // The existing account is sufficient - return; - } - - assert!( - !self.freeze_started(), - "Can't change frozen bank by adding not-existing new builtin program ({name}, {program_id}). \ - Maybe, inconsistent program activation is detected on snapshot restore?" - ); - - // Add a bogus executable builtin account, which will be loaded and ignored. - let account = native_loader::create_loadable_account_with_fields( - name, - self.inherit_specially_retained_account_fields(&existing_genuine_program), - ); - self.store_account_and_update_capitalization(program_id, &account); - } - /// Add a precompiled program account pub fn add_precompiled_account(&self, program_id: &Pubkey) { self.add_precompiled_account_with_owner(program_id, native_loader::id()) @@ -3919,7 +3882,6 @@ impl Bank { recording_config, timings, account_overrides, - self.builtin_program_ids.iter(), log_messages_bytes_limit, limit_to_load_programs, ); @@ -5338,7 +5300,8 @@ impl Bank { .chain(additional_builtins.unwrap_or(&[]).iter()) { if builtin.enable_feature_id.is_none() { - self.add_builtin( + self.transaction_processor.add_builtin( + self, builtin.program_id, builtin.name, LoadedProgram::new_builtin(0, builtin.name.len(), builtin.entrypoint), @@ -5404,10 +5367,6 @@ impl Bank { } } - pub(crate) fn get_builtin_program_ids(&self) -> &HashSet { - &self.builtin_program_ids - } - // Hi! leaky abstraction here.... // try to use get_account_with_fixed_root() if it's called ONLY from on-chain runtime account // processing. That alternative fn provides more safety. @@ -6425,32 +6384,21 @@ impl Bank { program_id: Pubkey, builtin_function: BuiltinFunctionWithContext, ) { - self.add_builtin( + self.transaction_processor.add_builtin( + self, program_id, "mockup", LoadedProgram::new_builtin(self.slot, 0, builtin_function), ); } - /// Add a built-in program - pub fn add_builtin(&mut self, program_id: Pubkey, name: &str, builtin: LoadedProgram) { - debug!("Adding program {} under {:?}", name, program_id); - self.add_builtin_account(name, &program_id); - self.builtin_program_ids.insert(program_id); - self.transaction_processor - .program_cache - .write() - .unwrap() - .assign_program(program_id, Arc::new(builtin)); - debug!("Added program {} under {:?}", name, program_id); - } - /// Remove a built-in instruction processor pub fn remove_builtin(&mut self, program_id: Pubkey, name: &str) { debug!("Removing program {}", program_id); // Don't remove the account since the bank expects the account state to // be idempotent - self.add_builtin( + self.transaction_processor.add_builtin( + self, program_id, name, LoadedProgram::new_tombstone(self.slot, LoadedProgramType::Closed), @@ -6696,7 +6644,8 @@ impl Bank { self.feature_set.is_active(&feature_id) }; if should_apply_action_for_feature_transition { - self.add_builtin( + self.transaction_processor.add_builtin( + self, builtin.program_id, builtin.name, LoadedProgram::new_builtin( @@ -6878,6 +6827,10 @@ impl Bank { self.transaction_processor .load_program_with_pubkey(self, pubkey, reload, effective_epoch) } + + pub fn get_transaction_processor(&self) -> &TransactionBatchProcessor { + &self.transaction_processor + } } impl TransactionProcessingCallback for Bank { @@ -6940,6 +6893,44 @@ impl TransactionProcessingCallback for Bank { LoadedProgramMatchCriteria::NoCriteria } } + + // NOTE: must hold idempotent for the same set of arguments + /// Add a builtin program account + fn add_builtin_account(&self, name: &str, program_id: &Pubkey) { + let existing_genuine_program = + self.get_account_with_fixed_root(program_id) + .and_then(|account| { + // it's very unlikely to be squatted at program_id as non-system account because of burden to + // find victim's pubkey/hash. So, when account.owner is indeed native_loader's, it's + // safe to assume it's a genuine program. + if native_loader::check_id(account.owner()) { + Some(account) + } else { + // malicious account is pre-occupying at program_id + self.burn_and_purge_account(program_id, account); + None + } + }); + + // introducing builtin program + if existing_genuine_program.is_some() { + // The existing account is sufficient + return; + } + + assert!( + !self.freeze_started(), + "Can't change frozen bank by adding not-existing new builtin program ({name}, {program_id}). \ + Maybe, inconsistent program activation is detected on snapshot restore?" + ); + + // Add a bogus executable builtin account, which will be loaded and ignored. + let account = native_loader::create_loadable_account_with_fields( + name, + self.inherit_specially_retained_account_fields(&existing_genuine_program), + ); + self.store_account_and_update_capitalization(program_id, &account); + } } #[cfg(feature = "dev-context-only-utils")] diff --git a/runtime/src/bank/builtins/core_bpf_migration/mod.rs b/runtime/src/bank/builtins/core_bpf_migration/mod.rs index 6dbd4bc950..97ed1d250b 100644 --- a/runtime/src/bank/builtins/core_bpf_migration/mod.rs +++ b/runtime/src/bank/builtins/core_bpf_migration/mod.rs @@ -281,7 +281,11 @@ impl Bank { self.store_account(&source.program_data_address, &AccountSharedData::default()); // Remove the built-in program from the bank's list of built-ins. - self.builtin_program_ids.remove(&target.program_address); + self.transaction_processor + .builtin_program_ids + .write() + .unwrap() + .remove(&target.program_address); // Update the account data size delta. self.calculate_and_update_accounts_data_size_delta_off_chain(old_data_size, new_data_size); @@ -427,7 +431,12 @@ mod tests { // The bank's builtins should no longer contain the builtin // program ID. - assert!(!bank.builtin_program_ids.contains(&self.builtin_id)); + assert!(!bank + .transaction_processor + .builtin_program_ids + .read() + .unwrap() + .contains(&self.builtin_id)); // The cache should contain the target program. let program_cache = bank.transaction_processor.program_cache.read().unwrap(); @@ -468,7 +477,12 @@ mod tests { let account = AccountSharedData::new_data(1, &builtin_name, &native_loader::id()).unwrap(); bank.store_account_and_update_capitalization(&builtin_id, &account); - bank.add_builtin(builtin_id, builtin_name.as_str(), LoadedProgram::default()); + bank.transaction_processor.add_builtin( + &bank, + builtin_id, + builtin_name.as_str(), + LoadedProgram::default(), + ); account }; assert_eq!(&bank.get_account(&builtin_id).unwrap(), &builtin_account); diff --git a/runtime/src/bank/tests.rs b/runtime/src/bank/tests.rs index 39a5bd18af..43e7280d96 100644 --- a/runtime/src/bank/tests.rs +++ b/runtime/src/bank/tests.rs @@ -4700,12 +4700,14 @@ fn test_add_instruction_processor_for_existing_unrelated_accounts() { continue; } - bank.add_builtin( + bank.transaction_processor.add_builtin( + &bank, vote_id, "mock_program1", LoadedProgram::new_builtin(0, 0, MockBuiltin::vm), ); - bank.add_builtin( + bank.transaction_processor.add_builtin( + &bank, stake_id, "mock_program2", LoadedProgram::new_builtin(0, 0, MockBuiltin::vm), @@ -6286,7 +6288,7 @@ fn test_ref_account_key_after_program_id() { fn test_fuzz_instructions() { solana_logger::setup(); use rand::{thread_rng, Rng}; - let mut bank = create_simple_test_bank(1_000_000_000); + let bank = create_simple_test_bank(1_000_000_000); let max_programs = 5; let program_keys: Vec<_> = (0..max_programs) @@ -6294,7 +6296,8 @@ fn test_fuzz_instructions() { .map(|i| { let key = solana_sdk::pubkey::new_rand(); let name = format!("program{i:?}"); - bank.add_builtin( + bank.transaction_processor.add_builtin( + &bank, key, name.as_str(), LoadedProgram::new_builtin(0, 0, MockBuiltin::vm), diff --git a/runtime/src/snapshot_minimizer.rs b/runtime/src/snapshot_minimizer.rs index de87714539..2e5f6a3bd8 100644 --- a/runtime/src/snapshot_minimizer.rs +++ b/runtime/src/snapshot_minimizer.rs @@ -112,7 +112,10 @@ impl<'a> SnapshotMinimizer<'a> { /// Used to get builtin accounts in `minimize` fn get_builtins(&self) { self.bank - .get_builtin_program_ids() + .get_transaction_processor() + .builtin_program_ids + .read() + .unwrap() .iter() .for_each(|program_id| { self.minimized_account_set.insert(*program_id); diff --git a/svm/src/transaction_processor.rs b/svm/src/transaction_processor.rs index 05b0bd4c14..afb6896cd4 100644 --- a/svm/src/transaction_processor.rs +++ b/svm/src/transaction_processor.rs @@ -49,7 +49,7 @@ use { }, std::{ cell::RefCell, - collections::{hash_map::Entry, HashMap}, + collections::{hash_map::Entry, HashMap, HashSet}, fmt::{Debug, Formatter}, rc::Rc, sync::{atomic::Ordering, Arc, RwLock}, @@ -108,6 +108,8 @@ pub trait TransactionProcessingCallback { fn get_program_match_criteria(&self, _program: &Pubkey) -> LoadedProgramMatchCriteria { LoadedProgramMatchCriteria::NoCriteria } + + fn add_builtin_account(&self, _name: &str, _program_id: &Pubkey) {} } #[derive(Debug)] @@ -142,6 +144,9 @@ pub struct TransactionBatchProcessor { /// Programs required for transaction batch processing pub program_cache: Arc>>, + + /// Builtin program ids + pub builtin_program_ids: RwLock>, } impl Debug for TransactionBatchProcessor { @@ -171,6 +176,7 @@ impl Default for TransactionBatchProcessor { Slot::default(), Epoch::default(), ))), + builtin_program_ids: RwLock::new(HashSet::new()), } } } @@ -183,6 +189,7 @@ impl TransactionBatchProcessor { fee_structure: FeeStructure, runtime_config: Arc, program_cache: Arc>>, + builtin_program_ids: HashSet, ) -> Self { Self { slot, @@ -192,12 +199,13 @@ impl TransactionBatchProcessor { runtime_config, sysvar_cache: RwLock::::default(), program_cache, + builtin_program_ids: RwLock::new(builtin_program_ids), } } /// Main entrypoint to the SVM. #[allow(clippy::too_many_arguments)] - pub fn load_and_execute_sanitized_transactions<'a, CB: TransactionProcessingCallback>( + pub fn load_and_execute_sanitized_transactions( &self, callbacks: &CB, sanitized_txs: &[SanitizedTransaction], @@ -206,7 +214,6 @@ impl TransactionBatchProcessor { recording_config: ExecutionRecordingConfig, timings: &mut ExecuteTimings, account_overrides: Option<&AccountOverrides>, - builtin_programs: impl Iterator, log_messages_bytes_limit: Option, limit_to_load_programs: bool, ) -> LoadAndExecuteSanitizedTransactionsOutput { @@ -218,7 +225,7 @@ impl TransactionBatchProcessor { PROGRAM_OWNERS, ); let native_loader = native_loader::id(); - for builtin_program in builtin_programs { + for builtin_program in self.builtin_program_ids.read().unwrap().iter() { program_accounts_map.insert(*builtin_program, (&native_loader, 0)); } @@ -954,6 +961,24 @@ impl TransactionBatchProcessor { pub fn get_sysvar_cache_for_tests(&self) -> SysvarCache { self.sysvar_cache.read().unwrap().clone() } + + /// Add a built-in program + pub fn add_builtin( + &self, + callbacks: &CB, + program_id: Pubkey, + name: &str, + builtin: LoadedProgram, + ) { + debug!("Adding program {} under {:?}", name, program_id); + callbacks.add_builtin_account(name, &program_id); + self.builtin_program_ids.write().unwrap().insert(program_id); + self.program_cache + .write() + .unwrap() + .assign_program(program_id, Arc::new(builtin)); + debug!("Added program {} under {:?}", name, program_id); + } } #[cfg(test)] diff --git a/svm/tests/integration_test.rs b/svm/tests/integration_test.rs index 1902c24e8b..1d2e47150e 100644 --- a/svm/tests/integration_test.rs +++ b/svm/tests/integration_test.rs @@ -26,7 +26,6 @@ use { fee::FeeStructure, hash::Hash, instruction::AccountMeta, - native_loader, pubkey::Pubkey, signature::Signature, sysvar::SysvarId, @@ -43,7 +42,7 @@ use { }, std::{ cmp::Ordering, - collections::HashMap, + collections::{HashMap, HashSet}, env, fs::{self, File}, io::Read, @@ -133,49 +132,9 @@ fn create_custom_environment<'a>() -> BuiltinProgram> { BuiltinProgram::new_loader(vm_config, function_registry) } -fn create_executable_environment( - mock_bank: &mut MockBankCallback, -) -> (ProgramCache, Vec) { +fn create_executable_environment(mock_bank: &mut MockBankCallback) -> ProgramCache { let mut program_cache = ProgramCache::::new(0, 20); - // We must register the bpf loader account as a loadable account, otherwise programs - // won't execute. - let account_data = native_loader::create_loadable_account_with_fields( - BPF_LOADER_NAME, - (5000, DEPLOYMENT_EPOCH), - ); - mock_bank - .account_shared_data - .insert(bpf_loader_upgradeable::id(), account_data); - - // The bpf loader needs an executable as well - program_cache.assign_program( - bpf_loader_upgradeable::id(), - Arc::new(LoadedProgram::new_builtin( - DEPLOYMENT_SLOT, - BPF_LOADER_NAME.len(), - solana_bpf_loader_program::Entrypoint::vm, - )), - ); - - // In order to perform a transference of native tokens using the system instruction, - // the system program builtin must be registered. - let account_data = native_loader::create_loadable_account_with_fields( - SYSTEM_PROGRAM_NAME, - (5000, DEPLOYMENT_EPOCH), - ); - mock_bank - .account_shared_data - .insert(solana_system_program::id(), account_data); - program_cache.assign_program( - solana_system_program::id(), - Arc::new(LoadedProgram::new_builtin( - DEPLOYMENT_SLOT, - SYSTEM_PROGRAM_NAME.len(), - solana_system_program::system_processor::Entrypoint::vm, - )), - ); - program_cache.environments = ProgramRuntimeEnvironments { program_runtime_v1: Arc::new(create_custom_environment()), // We are not using program runtime v2 @@ -204,11 +163,10 @@ fn create_executable_environment( account_data.set_data(bincode::serialize(&clock).unwrap()); mock_bank .account_shared_data + .borrow_mut() .insert(Clock::id(), account_data); - // Inform SVM of the registered builins - let registered_built_ins = vec![bpf_loader_upgradeable::id(), solana_system_program::id()]; - (program_cache, registered_built_ins) + program_cache } fn load_program(name: String) -> Vec { @@ -240,6 +198,7 @@ fn deploy_program(name: String, mock_bank: &mut MockBankCallback) -> Pubkey { account_data.set_owner(bpf_loader_upgradeable::id()); mock_bank .account_shared_data + .borrow_mut() .insert(program_account, account_data); let mut account_data = AccountSharedData::default(); @@ -261,11 +220,43 @@ fn deploy_program(name: String, mock_bank: &mut MockBankCallback) -> Pubkey { account_data.set_data(header); mock_bank .account_shared_data + .borrow_mut() .insert(program_data_account, account_data); program_account } +fn register_builtins( + mock_bank: &MockBankCallback, + batch_processor: &TransactionBatchProcessor, +) { + // We must register the bpf loader account as a loadable account, otherwise programs + // won't execute. + batch_processor.add_builtin( + mock_bank, + bpf_loader_upgradeable::id(), + BPF_LOADER_NAME, + LoadedProgram::new_builtin( + DEPLOYMENT_SLOT, + BPF_LOADER_NAME.len(), + solana_bpf_loader_program::Entrypoint::vm, + ), + ); + + // In order to perform a transference of native tokens using the system instruction, + // the system program builtin must be registered. + batch_processor.add_builtin( + mock_bank, + solana_system_program::id(), + SYSTEM_PROGRAM_NAME, + LoadedProgram::new_builtin( + DEPLOYMENT_SLOT, + SYSTEM_PROGRAM_NAME.len(), + solana_system_program::system_processor::Entrypoint::vm, + ), + ); +} + fn prepare_transactions( mock_bank: &mut MockBankCallback, ) -> (Vec, Vec) { @@ -289,6 +280,7 @@ fn prepare_transactions( account_data.set_lamports(80000); mock_bank .account_shared_data + .borrow_mut() .insert(fee_payer, account_data); // A simple funds transfer between accounts @@ -333,18 +325,23 @@ fn prepare_transactions( account_data.set_lamports(80000); mock_bank .account_shared_data + .borrow_mut() .insert(fee_payer, account_data); // sender let mut account_data = AccountSharedData::default(); account_data.set_lamports(900000); - mock_bank.account_shared_data.insert(sender, account_data); + mock_bank + .account_shared_data + .borrow_mut() + .insert(sender, account_data); // recipient let mut account_data = AccountSharedData::default(); account_data.set_lamports(900000); mock_bank .account_shared_data + .borrow_mut() .insert(recipient, account_data); // The system account is set in `create_executable_environment` @@ -364,6 +361,7 @@ fn prepare_transactions( account_data.set_lamports(80000); mock_bank .account_shared_data + .borrow_mut() .insert(fee_payer, account_data); // A transaction that fails @@ -405,18 +403,23 @@ fn prepare_transactions( account_data.set_lamports(80000); mock_bank .account_shared_data + .borrow_mut() .insert(fee_payer, account_data); // Sender without enough funds let mut account_data = AccountSharedData::default(); account_data.set_lamports(900000); - mock_bank.account_shared_data.insert(sender, account_data); + mock_bank + .account_shared_data + .borrow_mut() + .insert(sender, account_data); // recipient let mut account_data = AccountSharedData::default(); account_data.set_lamports(900000); mock_bank .account_shared_data + .borrow_mut() .insert(recipient, account_data); // A transaction whose verification has already failed @@ -430,7 +433,7 @@ fn prepare_transactions( fn svm_integration() { let mut mock_bank = MockBankCallback::default(); let (transactions, mut check_results) = prepare_transactions(&mut mock_bank); - let (program_cache, builtins) = create_executable_environment(&mut mock_bank); + let program_cache = create_executable_environment(&mut mock_bank); let program_cache = Arc::new(RwLock::new(program_cache)); let batch_processor = TransactionBatchProcessor::::new( EXECUTION_SLOT, @@ -439,10 +442,12 @@ fn svm_integration() { FeeStructure::default(), Arc::new(RuntimeConfig::default()), program_cache.clone(), + HashSet::default(), ); // The sysvars must be put in the cache batch_processor.fill_missing_sysvar_cache_entries(&mock_bank); + register_builtins(&mock_bank, &batch_processor); let mut error_counter = TransactionErrorMetrics::default(); let recording_config = ExecutionRecordingConfig { @@ -460,7 +465,6 @@ fn svm_integration() { recording_config, &mut timings, None, - builtins.iter(), None, false, ); diff --git a/svm/tests/mock_bank.rs b/svm/tests/mock_bank.rs index 0c123369e2..ff3fff77d2 100644 --- a/svm/tests/mock_bank.rs +++ b/svm/tests/mock_bank.rs @@ -3,23 +3,24 @@ use { account::{AccountSharedData, ReadableAccount}, feature_set::FeatureSet, hash::Hash, + native_loader, pubkey::Pubkey, rent_collector::RentCollector, }, solana_svm::transaction_processor::TransactionProcessingCallback, - std::{collections::HashMap, sync::Arc}, + std::{cell::RefCell, collections::HashMap, sync::Arc}, }; #[derive(Default)] pub struct MockBankCallback { rent_collector: RentCollector, feature_set: Arc, - pub account_shared_data: HashMap, + pub account_shared_data: RefCell>, } impl TransactionProcessingCallback for MockBankCallback { fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option { - if let Some(data) = self.account_shared_data.get(account) { + if let Some(data) = self.account_shared_data.borrow().get(account) { if data.lamports() == 0 { None } else { @@ -31,7 +32,7 @@ impl TransactionProcessingCallback for MockBankCallback { } fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option { - self.account_shared_data.get(pubkey).cloned() + self.account_shared_data.borrow().get(pubkey).cloned() } fn get_last_blockhash_and_lamports_per_signature(&self) -> (Hash, u64) { @@ -46,4 +47,12 @@ impl TransactionProcessingCallback for MockBankCallback { fn get_feature_set(&self) -> Arc { self.feature_set.clone() } + + fn add_builtin_account(&self, name: &str, program_id: &Pubkey) { + let account_data = native_loader::create_loadable_account_with_fields(name, (5000, 0)); + + self.account_shared_data + .borrow_mut() + .insert(*program_id, account_data); + } }