diff --git a/ledger/src/blockstore_processor.rs b/ledger/src/blockstore_processor.rs index 76c72e65eb..b3b7966170 100644 --- a/ledger/src/blockstore_processor.rs +++ b/ledger/src/blockstore_processor.rs @@ -1809,7 +1809,7 @@ pub mod tests { matches::assert_matches, rand::{thread_rng, Rng}, solana_entry::entry::{create_ticks, next_entry, next_entry_mut}, - solana_program_runtime::declare_process_instruction, + solana_program_runtime::{builtin_program::create_builtin, declare_process_instruction}, solana_runtime::{ genesis_utils::{ self, create_genesis_config_with_vote_accounts, ValidatorVoteKeypairs, @@ -2971,7 +2971,10 @@ pub mod tests { let mock_program_id = solana_sdk::pubkey::new_rand(); let mut bank = Bank::new_for_tests(&genesis_config); - bank.add_builtin("mock_processor", &mock_program_id, mock_processor_ok); + bank.add_builtin( + mock_program_id, + create_builtin("mockup".to_string(), mock_processor_ok), + ); let tx = Transaction::new_signed_with_payer( &[Instruction::new_with_bincode( @@ -3012,7 +3015,10 @@ pub mod tests { (0..get_instruction_errors().len()).for_each(|err| { let mut bank = Bank::new_for_tests(&genesis_config); - bank.add_builtin("mock_processor", &mock_program_id, mock_processor_err); + bank.add_builtin( + mock_program_id, + create_builtin("mockup".to_string(), mock_processor_err), + ); let tx = Transaction::new_signed_with_payer( &[Instruction::new_with_bincode( diff --git a/program-runtime/src/builtin_program.rs b/program-runtime/src/builtin_program.rs index cd876c3e81..b768436c96 100644 --- a/program-runtime/src/builtin_program.rs +++ b/program-runtime/src/builtin_program.rs @@ -1,44 +1,28 @@ #[cfg(RUSTC_WITH_SPECIALIZATION)] -use {crate::declare_process_instruction, solana_frozen_abi::abi_example::AbiExample}; +use solana_frozen_abi::abi_example::AbiExample; use { - crate::invoke_context::InvokeContext, solana_rbpf::vm::BuiltInFunction, + crate::{invoke_context::InvokeContext, loaded_programs::LoadedProgram}, + solana_rbpf::vm::{BuiltInFunction, BuiltInProgram}, solana_sdk::pubkey::Pubkey, + std::sync::Arc, }; pub type ProcessInstructionWithContext = BuiltInFunction>; -#[derive(Clone)] -pub struct BuiltinProgram { - pub name: String, - pub program_id: Pubkey, - pub process_instruction: ProcessInstructionWithContext, -} - -impl std::fmt::Debug for BuiltinProgram { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "Builtin [name={}, id={}]", self.name, self.program_id) - } -} - -#[cfg(RUSTC_WITH_SPECIALIZATION)] -impl AbiExample for BuiltinProgram { - fn example() -> Self { - declare_process_instruction!(empty_mock_process_instruction, 1, |_invoke_context| { - // Do nothing - Ok(()) - }); - - Self { - name: String::default(), - program_id: Pubkey::default(), - process_instruction: empty_mock_process_instruction, - } - } +pub fn create_builtin( + name: String, + process_instruction: ProcessInstructionWithContext, +) -> Arc { + let mut program = BuiltInProgram::default(); + program + .register_function_by_name("entrypoint", process_instruction) + .unwrap(); + Arc::new(LoadedProgram::new_builtin(name, 0, program)) } #[derive(Debug, Clone, Default)] pub struct BuiltinPrograms { - pub vec: Vec, + pub vec: Vec<(Pubkey, Arc)>, } #[cfg(RUSTC_WITH_SPECIALIZATION)] @@ -54,11 +38,10 @@ impl BuiltinPrograms { process_instruction: ProcessInstructionWithContext, ) -> Self { Self { - vec: vec![BuiltinProgram { - name: "mock instruction processor".to_string(), + vec: vec![( program_id, - process_instruction, - }], + create_builtin("mockup".to_string(), process_instruction), + )], } } } diff --git a/program-runtime/src/invoke_context.rs b/program-runtime/src/invoke_context.rs index f006c90d69..b7ddd14879 100644 --- a/program-runtime/src/invoke_context.rs +++ b/program-runtime/src/invoke_context.rs @@ -5,6 +5,7 @@ use { compute_budget::ComputeBudget, executor_cache::TransactionExecutorCache, ic_logger_msg, ic_msg, + loaded_programs::LoadedProgramType, log_collector::LogCollector, pre_account::PreAccount, stable_log, @@ -40,7 +41,7 @@ use { cell::RefCell, fmt::{self, Debug}, rc::Rc, - sync::Arc, + sync::{atomic::Ordering, Arc}, }, }; @@ -719,19 +720,29 @@ impl<'a> InvokeContext<'a> { }; for entry in self.builtin_programs.vec.iter() { - if entry.program_id == builtin_id { + if entry.0 == builtin_id { + // The Murmur3 hash value (used by RBPF) of the string "entrypoint" + const ENTRYPOINT_KEY: u32 = 0x71E3CF81; + let process_instruction = match &entry.1.program { + LoadedProgramType::Builtin(_name, program) => program + .lookup_function(ENTRYPOINT_KEY) + .map(|(_name, process_instruction)| process_instruction), + _ => None, + } + .ok_or(InstructionError::GenericError)?; + entry.1.usage_counter.fetch_add(1, Ordering::Relaxed); + let program_id = *instruction_context.get_last_program_key(self.transaction_context)?; self.transaction_context .set_return_data(program_id, Vec::new())?; - let logger = self.get_log_collector(); stable_log::program_invoke(&logger, &program_id, self.get_stack_height()); let pre_remaining_units = self.get_remaining(); let mock_config = Config::default(); let mut mock_memory_mapping = MemoryMapping::new(Vec::new(), &mock_config).unwrap(); let mut result = ProgramResult::Ok(0); - (entry.process_instruction)( + process_instruction( // Removes lifetime tracking unsafe { std::mem::transmute::<&mut InvokeContext, &mut InvokeContext>(self) }, 0, diff --git a/program-runtime/src/loaded_programs.rs b/program-runtime/src/loaded_programs.rs index 11e1996506..02f1d52bd1 100644 --- a/program-runtime/src/loaded_programs.rs +++ b/program-runtime/src/loaded_programs.rs @@ -72,7 +72,7 @@ pub enum LoadedProgramType { Typed(VerifiedExecutable>), #[cfg(test)] TestLoaded, - BuiltIn(BuiltInProgram>), + Builtin(String, BuiltInProgram>), } impl Debug for LoadedProgramType { @@ -89,7 +89,9 @@ impl Debug for LoadedProgramType { LoadedProgramType::Typed(_) => write!(f, "LoadedProgramType::Typed"), #[cfg(test)] LoadedProgramType::TestLoaded => write!(f, "LoadedProgramType::TestLoaded"), - LoadedProgramType::BuiltIn(_) => write!(f, "LoadedProgramType::BuiltIn"), + LoadedProgramType::Builtin(name, _) => { + write!(f, "LoadedProgramType::Builtin({name})") + } } } } @@ -215,7 +217,8 @@ impl LoadedProgram { } /// Creates a new built-in program - pub fn new_built_in( + pub fn new_builtin( + name: String, deployment_slot: Slot, program: BuiltInProgram>, ) -> Self { @@ -225,7 +228,7 @@ impl LoadedProgram { effective_slot: deployment_slot.saturating_add(1), maybe_expiration_slot: None, usage_counter: AtomicU64::new(0), - program: LoadedProgramType::BuiltIn(program), + program: LoadedProgramType::Builtin(name, program), } } @@ -294,16 +297,6 @@ impl LoadedProgramsForTxBatch { } } -#[cfg(RUSTC_WITH_SPECIALIZATION)] -impl solana_frozen_abi::abi_example::AbiExample for LoadedPrograms { - fn example() -> Self { - // Delegate AbiExample impl to Default before going deep and stuck with - // not easily impl-able Arc due to rust's coherence issue - // This is safe because LoadedPrograms isn't serializable by definition. - Self::default() - } -} - pub enum LoadedProgramMatchCriteria { DeployedOnOrAfterSlot(Slot), Closed, @@ -474,7 +467,7 @@ impl LoadedPrograms { LoadedProgramType::FailedVerification | LoadedProgramType::Closed | LoadedProgramType::DelayVisibility - | LoadedProgramType::BuiltIn(_) => None, + | LoadedProgramType::Builtin(_, _) => None, }) }) .sorted_by_cached_key(|(order, (_id, program))| { @@ -561,6 +554,22 @@ impl LoadedPrograms { } } +#[cfg(RUSTC_WITH_SPECIALIZATION)] +impl solana_frozen_abi::abi_example::AbiExample for LoadedProgram { + fn example() -> Self { + // LoadedProgram isn't serializable by definition. + Self::default() + } +} + +#[cfg(RUSTC_WITH_SPECIALIZATION)] +impl solana_frozen_abi::abi_example::AbiExample for LoadedPrograms { + fn example() -> Self { + // LoadedPrograms isn't serializable by definition. + Self::default() + } +} + #[cfg(test)] mod tests { use { @@ -582,7 +591,7 @@ mod tests { fn new_test_builtin_program(deployment_slot: Slot, effective_slot: Slot) -> Arc { Arc::new(LoadedProgram { - program: LoadedProgramType::BuiltIn(BuiltInProgram::default()), + program: LoadedProgramType::Builtin("mockup".to_string(), BuiltInProgram::default()), account_size: 0, deployment_slot, effective_slot, diff --git a/program-test/src/lib.rs b/program-test/src/lib.rs index f93088f0d8..43d8dc7e71 100644 --- a/program-test/src/lib.rs +++ b/program-test/src/lib.rs @@ -11,7 +11,7 @@ use { solana_banks_server::banks_server::start_local_server, solana_bpf_loader_program::serialization::serialize_parameters, solana_program_runtime::{ - builtin_program::{BuiltinProgram, BuiltinPrograms, ProcessInstructionWithContext}, + builtin_program::{create_builtin, BuiltinPrograms, ProcessInstructionWithContext}, compute_budget::ComputeBudget, ic_msg, stable_log, timings::ExecuteTimings, @@ -692,11 +692,10 @@ impl ProgramTest { process_instruction: ProcessInstructionWithContext, ) { info!("\"{}\" builtin program", program_name); - self.builtin_programs.vec.push(BuiltinProgram { - name: program_name.to_string(), + self.builtin_programs.vec.push(( program_id, - process_instruction, - }); + create_builtin(program_name.to_string(), process_instruction), + )); } /// Deactivate a runtime feature. @@ -791,12 +790,8 @@ impl ProgramTest { } // User-supplied additional builtins - for builtin in self.builtin_programs.vec.iter() { - bank.add_builtin( - &builtin.name, - &builtin.program_id, - builtin.process_instruction, - ); + for (program_id, builtin) in self.builtin_programs.vec.iter() { + bank.add_builtin(*program_id, builtin.clone()); } for (address, account) in self.accounts.iter() { diff --git a/runtime/benches/bank.rs b/runtime/benches/bank.rs index b30f2309a5..add7b2ede2 100644 --- a/runtime/benches/bank.rs +++ b/runtime/benches/bank.rs @@ -5,7 +5,7 @@ extern crate test; use { log::*, - solana_program_runtime::declare_process_instruction, + solana_program_runtime::{builtin_program::create_builtin, declare_process_instruction}, solana_runtime::{ bank::{test_utils::goto_end_of_slot, *}, bank_client::BankClient, @@ -132,9 +132,8 @@ fn do_bench_transactions( let mut bank = Bank::new_from_parent(&Arc::new(bank), &Pubkey::default(), 1); bank.add_builtin( - "builtin_program", - &Pubkey::from(BUILTIN_PROGRAM_ID), - process_instruction, + Pubkey::from(BUILTIN_PROGRAM_ID), + create_builtin("mockup".to_string(), process_instruction), ); bank.add_builtin_account("solana_noop_program", &Pubkey::from(NOOP_PROGRAM_ID), false); let bank = Arc::new(bank); diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index ecad2a62bd..8b6f30f79c 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -94,7 +94,7 @@ use { solana_perf::perf_libs, solana_program_runtime::{ accounts_data_meter::MAX_ACCOUNTS_DATA_LEN, - builtin_program::{BuiltinProgram, BuiltinPrograms, ProcessInstructionWithContext}, + builtin_program::BuiltinPrograms, compute_budget::{self, ComputeBudget}, executor_cache::{BankExecutorCache, TransactionExecutorCache, MAX_CACHED_EXECUTORS}, loaded_programs::{ @@ -6350,12 +6350,8 @@ impl Bank { .extend_from_slice(&additional_builtins.feature_transitions); } if !debug_do_not_add_builtins { - for builtin in builtins.genesis_builtins { - self.add_builtin( - &builtin.name, - &builtin.program_id, - builtin.process_instruction, - ); + for (program_id, builtin) in builtins.genesis_builtins { + self.add_builtin(program_id, builtin); } for precompile in get_precompiles() { if precompile.feature.is_none() { @@ -7379,27 +7375,24 @@ impl Bank { } /// Add an instruction processor to intercept instructions before the dynamic loader. - pub fn add_builtin( - &mut self, - name: &str, - program_id: &Pubkey, - process_instruction: ProcessInstructionWithContext, - ) { + pub fn add_builtin(&mut self, program_id: Pubkey, builtin: Arc) { + let name = match &builtin.program { + LoadedProgramType::Builtin(name, _) => name, + _ => unreachable!(), + }; debug!("Adding program {} under {:?}", name, program_id); - self.add_builtin_account(name, program_id, false); + self.add_builtin_account(name.as_str(), &program_id, false); if let Some(entry) = self .builtin_programs .vec .iter_mut() - .find(|entry| entry.program_id == *program_id) + .find(|entry| entry.0 == program_id) { - entry.process_instruction = process_instruction; + entry.1 = builtin.clone(); } else { - self.builtin_programs.vec.push(BuiltinProgram { - name: name.to_string(), - program_id: *program_id, - process_instruction, - }); + self.builtin_programs + .vec + .push((program_id, builtin.clone())); } debug!("Added program {} under {:?}", name, program_id); } @@ -7413,7 +7406,7 @@ impl Bank { .builtin_programs .vec .iter() - .position(|entry| entry.program_id == *program_id) + .position(|entry| entry.0 == *program_id) { self.builtin_programs.vec.remove(position); } @@ -7665,11 +7658,9 @@ impl Bank { transition.to_action(&should_apply_action_for_feature_transition) { match builtin_action { - BuiltinAction::Add(builtin) => self.add_builtin( - &builtin.name, - &builtin.program_id, - builtin.process_instruction, - ), + BuiltinAction::Add(program_id, builtin) => { + self.add_builtin(program_id, builtin) + } BuiltinAction::Remove(program_id) => self.remove_builtin(&program_id), } } diff --git a/runtime/src/bank/tests.rs b/runtime/src/bank/tests.rs index d3a53214af..86e202a0cb 100644 --- a/runtime/src/bank/tests.rs +++ b/runtime/src/bank/tests.rs @@ -33,6 +33,7 @@ use { serde::{Deserialize, Serialize}, solana_logger, solana_program_runtime::{ + builtin_program::create_builtin, compute_budget::{self, ComputeBudget, MAX_COMPUTE_UNIT_LIMIT}, declare_process_instruction, executor_cache::TransactionExecutorCache, @@ -1380,7 +1381,10 @@ fn test_rent_complex() { root_bank.restore_old_behavior_for_fragile_tests(); let root_bank = Arc::new(root_bank); let mut bank = create_child_bank_for_rent_test(&root_bank, &genesis_config); - bank.add_builtin("mock_program", &mock_program_id, process_instruction); + bank.add_builtin( + mock_program_id, + create_builtin("mockup".to_string(), process_instruction), + ); assert_eq!(bank.last_blockhash(), genesis_config.hash()); @@ -4817,9 +4821,8 @@ fn test_add_builtin() { assert!(bank.get_account(&mock_vote_program_id()).is_none()); bank.add_builtin( - "mock_vote_program", - &mock_vote_program_id(), - process_instruction, + mock_vote_program_id(), + create_builtin("mockup".to_string(), process_instruction), ); assert!(bank.get_account(&mock_vote_program_id()).is_some()); @@ -4894,9 +4897,8 @@ fn test_add_duplicate_static_program() { let vote_loader_account = bank.get_account(&solana_vote_program::id()).unwrap(); bank.add_builtin( - "solana_vote_program", - &solana_vote_program::id(), - process_instruction, + solana_vote_program::id(), + create_builtin("mockup".to_string(), process_instruction), ); let new_vote_loader_account = bank.get_account(&solana_vote_program::id()).unwrap(); // Vote loader account should not be updated since it was included in the genesis config. @@ -4948,8 +4950,14 @@ fn test_add_instruction_processor_for_existing_unrelated_accounts() { continue; } - bank.add_builtin("mock_program1", &vote_id, process_instruction); - bank.add_builtin("mock_program2", &stake_id, process_instruction); + bank.add_builtin( + vote_id, + create_builtin("mock_program1".to_string(), process_instruction), + ); + bank.add_builtin( + stake_id, + create_builtin("mock_program2".to_string(), process_instruction), + ); { let stakes = bank.stakes_cache.stakes(); assert!(stakes.vote_accounts().as_ref().is_empty()); @@ -4972,8 +4980,14 @@ fn test_add_instruction_processor_for_existing_unrelated_accounts() { // Re-adding builtin programs should be no-op bank.update_accounts_hash_for_tests(); let old_hash = bank.get_accounts_hash().unwrap(); - bank.add_builtin("mock_program1", &vote_id, process_instruction); - bank.add_builtin("mock_program2", &stake_id, process_instruction); + bank.add_builtin( + vote_id, + create_builtin("mockup".to_string(), process_instruction), + ); + bank.add_builtin( + stake_id, + create_builtin("mockup".to_string(), process_instruction), + ); add_root_and_flush_write_cache(&bank); bank.update_accounts_hash_for_tests(); let new_hash = bank.get_accounts_hash().unwrap(); @@ -6224,7 +6238,10 @@ fn test_transaction_with_duplicate_accounts_in_instruction() { }); let mock_program_id = Pubkey::from([2u8; 32]); - bank.add_builtin("mock_program", &mock_program_id, process_instruction); + bank.add_builtin( + mock_program_id, + create_builtin("mockup".to_string(), process_instruction), + ); let from_pubkey = solana_sdk::pubkey::new_rand(); let to_pubkey = solana_sdk::pubkey::new_rand(); @@ -6260,7 +6277,10 @@ fn test_transaction_with_program_ids_passed_to_programs() { let mut bank = Bank::new_for_tests(&genesis_config); let mock_program_id = Pubkey::from([2u8; 32]); - bank.add_builtin("mock_program", &mock_program_id, process_instruction); + bank.add_builtin( + mock_program_id, + create_builtin("mockup".to_string(), process_instruction), + ); let from_pubkey = solana_sdk::pubkey::new_rand(); let to_pubkey = solana_sdk::pubkey::new_rand(); @@ -6312,7 +6332,10 @@ fn test_account_ids_after_program_ids() { tx.message.account_keys.push(solana_sdk::pubkey::new_rand()); - bank.add_builtin("mock_vote", &solana_vote_program::id(), process_instruction); + bank.add_builtin( + solana_vote_program::id(), + create_builtin("mockup".to_string(), process_instruction), + ); let result = bank.process_transaction(&tx); assert_eq!(result, Ok(())); let account = bank.get_account(&solana_vote_program::id()).unwrap(); @@ -6362,7 +6385,10 @@ fn test_duplicate_account_key() { AccountMeta::new(to_pubkey, false), ]; - bank.add_builtin("mock_vote", &solana_vote_program::id(), process_instruction); + bank.add_builtin( + solana_vote_program::id(), + create_builtin("mockup".to_string(), process_instruction), + ); let instruction = Instruction::new_with_bincode(solana_vote_program::id(), &10, account_metas); let mut tx = Transaction::new_signed_with_payer( @@ -6391,7 +6417,10 @@ fn test_process_transaction_with_too_many_account_locks() { AccountMeta::new(to_pubkey, false), ]; - bank.add_builtin("mock_vote", &solana_vote_program::id(), process_instruction); + bank.add_builtin( + solana_vote_program::id(), + create_builtin("mockup".to_string(), process_instruction), + ); let instruction = Instruction::new_with_bincode(solana_vote_program::id(), &10, account_metas); let mut tx = Transaction::new_signed_with_payer( @@ -6424,7 +6453,10 @@ fn test_program_id_as_payer() { AccountMeta::new(to_pubkey, false), ]; - bank.add_builtin("mock_vote", &solana_vote_program::id(), process_instruction); + bank.add_builtin( + solana_vote_program::id(), + create_builtin("mockup".to_string(), process_instruction), + ); let instruction = Instruction::new_with_bincode(solana_vote_program::id(), &10, account_metas); let mut tx = Transaction::new_signed_with_payer( @@ -6467,7 +6499,10 @@ fn test_ref_account_key_after_program_id() { AccountMeta::new(to_pubkey, false), ]; - bank.add_builtin("mock_vote", &solana_vote_program::id(), process_instruction); + bank.add_builtin( + solana_vote_program::id(), + create_builtin("mockup".to_string(), process_instruction), + ); let instruction = Instruction::new_with_bincode(solana_vote_program::id(), &10, account_metas); let mut tx = Transaction::new_signed_with_payer( @@ -6498,7 +6533,7 @@ fn test_fuzz_instructions() { .map(|i| { let key = solana_sdk::pubkey::new_rand(); let name = format!("program{i:?}"); - bank.add_builtin(&name, &key, process_instruction); + bank.add_builtin(key, create_builtin(name.clone(), process_instruction)); (key, name.as_bytes().to_vec()) }) .collect(); @@ -6704,7 +6739,10 @@ fn test_same_program_id_uses_unqiue_executable_accounts() { // Add a new program let program1_pubkey = solana_sdk::pubkey::new_rand(); - bank.add_builtin("program", &program1_pubkey, process_instruction); + bank.add_builtin( + program1_pubkey, + create_builtin("mockup".to_string(), process_instruction), + ); // Add a new program owned by the first let program2_pubkey = solana_sdk::pubkey::new_rand(); @@ -6919,15 +6957,17 @@ fn test_add_builtin_no_overwrite() { )); assert_eq!(bank.get_account_modified_slot(&program_id), None); - Arc::get_mut(&mut bank) - .unwrap() - .add_builtin("mock_program", &program_id, process_instruction); + Arc::get_mut(&mut bank).unwrap().add_builtin( + program_id, + create_builtin("mockup".to_string(), process_instruction), + ); assert_eq!(bank.get_account_modified_slot(&program_id).unwrap().1, slot); let mut bank = Arc::new(new_from_parent(&bank)); - Arc::get_mut(&mut bank) - .unwrap() - .add_builtin("mock_program", &program_id, process_instruction); + Arc::get_mut(&mut bank).unwrap().add_builtin( + program_id, + create_builtin("mockup".to_string(), process_instruction), + ); assert_eq!(bank.get_account_modified_slot(&program_id).unwrap().1, slot); } @@ -6943,15 +6983,17 @@ fn test_add_builtin_loader_no_overwrite() { )); assert_eq!(bank.get_account_modified_slot(&loader_id), None); - Arc::get_mut(&mut bank) - .unwrap() - .add_builtin("mock_program", &loader_id, process_instruction); + Arc::get_mut(&mut bank).unwrap().add_builtin( + loader_id, + create_builtin("mockup".to_string(), process_instruction), + ); assert_eq!(bank.get_account_modified_slot(&loader_id).unwrap().1, slot); let mut bank = Arc::new(new_from_parent(&bank)); - Arc::get_mut(&mut bank) - .unwrap() - .add_builtin("mock_program", &loader_id, process_instruction); + Arc::get_mut(&mut bank).unwrap().add_builtin( + loader_id, + create_builtin("mockup".to_string(), process_instruction), + ); assert_eq!(bank.get_account_modified_slot(&loader_id).unwrap().1, slot); } @@ -9694,7 +9736,10 @@ fn test_tx_return_data() { let mock_program_id = Pubkey::from([2u8; 32]); let blockhash = bank.last_blockhash(); - bank.add_builtin("mock_program", &mock_program_id, process_instruction); + bank.add_builtin( + mock_program_id, + create_builtin("mockup".to_string(), process_instruction), + ); for index in [ None, @@ -9883,7 +9928,10 @@ fn test_transfer_sysvar() { }); let program_id = solana_sdk::pubkey::new_rand(); - bank.add_builtin("mock_program1", &program_id, process_instruction); + bank.add_builtin( + program_id, + create_builtin("mockup".to_string(), process_instruction), + ); let blockhash = bank.last_blockhash(); #[allow(deprecated)] @@ -10095,7 +10143,10 @@ fn test_compute_budget_program_noop() { Ok(()) }); let program_id = solana_sdk::pubkey::new_rand(); - bank.add_builtin("mock_program", &program_id, process_instruction); + bank.add_builtin( + program_id, + create_builtin("mockup".to_string(), process_instruction), + ); let message = Message::new( &[ @@ -10138,7 +10189,10 @@ fn test_compute_request_instruction() { Ok(()) }); let program_id = solana_sdk::pubkey::new_rand(); - bank.add_builtin("mock_program", &program_id, process_instruction); + bank.add_builtin( + program_id, + create_builtin("mockup".to_string(), process_instruction), + ); let message = Message::new( &[ @@ -10188,7 +10242,10 @@ fn test_failed_compute_request_instruction() { Ok(()) }); let program_id = solana_sdk::pubkey::new_rand(); - bank.add_builtin("mock_program", &program_id, process_instruction); + bank.add_builtin( + program_id, + create_builtin("mockup".to_string(), process_instruction), + ); // This message will not be executed because the compute budget request is invalid let message0 = Message::new( @@ -10863,9 +10920,8 @@ fn test_invalid_rent_state_changes_existing_accounts() { let mut bank = Bank::new_for_tests(&genesis_config); bank.add_builtin( - "mock_program", - &mock_program_id, - mock_transfer_process_instruction, + mock_program_id, + create_builtin("mockup".to_string(), mock_transfer_process_instruction), ); let recent_blockhash = bank.last_blockhash(); @@ -10950,9 +11006,8 @@ fn test_invalid_rent_state_changes_new_accounts() { let mut bank = Bank::new_for_tests(&genesis_config); bank.add_builtin( - "mock_program", - &mock_program_id, - mock_transfer_process_instruction, + mock_program_id, + create_builtin("mockup".to_string(), mock_transfer_process_instruction), ); let recent_blockhash = bank.last_blockhash(); @@ -11013,9 +11068,8 @@ fn test_drained_created_account() { let mut bank = Bank::new_for_tests(&genesis_config); bank.add_builtin( - "mock_program", - &mock_program_id, - mock_transfer_process_instruction, + mock_program_id, + create_builtin("mockup".to_string(), mock_transfer_process_instruction), ); let recent_blockhash = bank.last_blockhash(); @@ -11668,9 +11722,8 @@ fn test_resize_and_rent() { let mock_program_id = Pubkey::new_unique(); bank.add_builtin( - "mock_realloc_program", - &mock_program_id, - mock_realloc_process_instruction, + mock_program_id, + create_builtin("mockup".to_string(), mock_realloc_process_instruction), ); let recent_blockhash = bank.last_blockhash(); @@ -11943,9 +11996,8 @@ fn test_accounts_data_size_and_resize_transactions() { let mut bank = Bank::new_for_tests(&genesis_config); let mock_program_id = Pubkey::new_unique(); bank.add_builtin( - "mock_realloc_program", - &mock_program_id, - mock_realloc_process_instruction, + mock_program_id, + create_builtin("mockup".to_string(), mock_realloc_process_instruction), ); let recent_blockhash = bank.last_blockhash(); diff --git a/runtime/src/builtins.rs b/runtime/src/builtins.rs index 12164fc67a..e396705cfe 100644 --- a/runtime/src/builtins.rs +++ b/runtime/src/builtins.rs @@ -1,14 +1,15 @@ use { - solana_program_runtime::builtin_program::BuiltinProgram, + solana_program_runtime::{builtin_program::create_builtin, loaded_programs::LoadedProgram}, solana_sdk::{ bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, feature_set, pubkey::Pubkey, }, + std::sync::Arc, }; #[derive(Clone, Debug)] pub struct Builtins { /// Builtin programs that are always available - pub genesis_builtins: Vec, + pub genesis_builtins: Vec<(Pubkey, Arc)>, /// Dynamic feature transitions for builtin programs pub feature_transitions: Vec, @@ -17,7 +18,7 @@ pub struct Builtins { /// Actions taken by a bank when managing the list of active builtin programs. #[derive(Debug, Clone)] pub enum BuiltinAction { - Add(BuiltinProgram), + Add(Pubkey, Arc), Remove(Pubkey), } @@ -27,13 +28,15 @@ pub enum BuiltinAction { pub enum BuiltinFeatureTransition { /// Add a builtin program if a feature is activated. Add { - builtin: BuiltinProgram, + program_id: Pubkey, + builtin: Arc, feature_id: Pubkey, }, /// Remove a builtin program if a feature is activated or /// retain a previously added builtin. RemoveOrRetain { - previously_added_builtin: BuiltinProgram, + program_id: Pubkey, + previously_added_builtin: Arc, addition_feature_id: Pubkey, removal_feature_id: Pubkey, }, @@ -46,25 +49,30 @@ impl BuiltinFeatureTransition { ) -> Option { match self { Self::Add { + program_id, builtin, feature_id, } => { if should_apply_action_for_feature(feature_id) { - Some(BuiltinAction::Add(builtin.clone())) + Some(BuiltinAction::Add(*program_id, builtin.clone())) } else { None } } Self::RemoveOrRetain { + program_id, previously_added_builtin, addition_feature_id, removal_feature_id, } => { if should_apply_action_for_feature(removal_feature_id) { - Some(BuiltinAction::Remove(previously_added_builtin.program_id)) + Some(BuiltinAction::Remove(*program_id)) } else if should_apply_action_for_feature(addition_feature_id) { // Retaining is no different from adding a new builtin. - Some(BuiltinAction::Add(previously_added_builtin.clone())) + Some(BuiltinAction::Add( + *program_id, + previously_added_builtin.clone(), + )) } else { None } @@ -74,43 +82,57 @@ impl BuiltinFeatureTransition { } /// Built-in programs that are always available -fn genesis_builtins() -> Vec { +fn genesis_builtins() -> Vec<(Pubkey, Arc)> { vec![ - BuiltinProgram { - name: "system_program".to_string(), - program_id: solana_system_program::id(), - process_instruction: solana_system_program::system_processor::process_instruction, - }, - BuiltinProgram { - name: "vote_program".to_string(), - program_id: solana_vote_program::id(), - process_instruction: solana_vote_program::vote_processor::process_instruction, - }, - BuiltinProgram { - name: "stake_program".to_string(), - program_id: solana_stake_program::id(), - process_instruction: solana_stake_program::stake_instruction::process_instruction, - }, - BuiltinProgram { - name: "config_program".to_string(), - program_id: solana_config_program::id(), - process_instruction: solana_config_program::config_processor::process_instruction, - }, - BuiltinProgram { - name: "solana_bpf_loader_deprecated_program".to_string(), - program_id: bpf_loader_deprecated::id(), - process_instruction: solana_bpf_loader_program::process_instruction, - }, - BuiltinProgram { - name: "solana_bpf_loader_program".to_string(), - program_id: bpf_loader::id(), - process_instruction: solana_bpf_loader_program::process_instruction, - }, - BuiltinProgram { - name: "solana_bpf_loader_upgradeable_program".to_string(), - program_id: bpf_loader_upgradeable::id(), - process_instruction: solana_bpf_loader_program::process_instruction, - }, + ( + solana_system_program::id(), + create_builtin( + "system_program".to_string(), + solana_system_program::system_processor::process_instruction, + ), + ), + ( + solana_vote_program::id(), + create_builtin( + "vote_program".to_string(), + solana_vote_program::vote_processor::process_instruction, + ), + ), + ( + solana_stake_program::id(), + create_builtin( + "stake_program".to_string(), + solana_stake_program::stake_instruction::process_instruction, + ), + ), + ( + solana_config_program::id(), + create_builtin( + "config_program".to_string(), + solana_config_program::config_processor::process_instruction, + ), + ), + ( + bpf_loader_deprecated::id(), + create_builtin( + "solana_bpf_loader_deprecated_program".to_string(), + solana_bpf_loader_program::process_instruction, + ), + ), + ( + bpf_loader::id(), + create_builtin( + "solana_bpf_loader_program".to_string(), + solana_bpf_loader_program::process_instruction, + ), + ), + ( + bpf_loader_upgradeable::id(), + create_builtin( + "solana_bpf_loader_upgradeable_program".to_string(), + solana_bpf_loader_program::process_instruction, + ), + ), ] } @@ -118,28 +140,27 @@ fn genesis_builtins() -> Vec { fn builtin_feature_transitions() -> Vec { vec![ BuiltinFeatureTransition::Add { - builtin: BuiltinProgram { - name: "compute_budget_program".to_string(), - program_id: solana_sdk::compute_budget::id(), - process_instruction: solana_compute_budget_program::process_instruction, - }, + program_id: solana_sdk::compute_budget::id(), + builtin: create_builtin( + "compute_budget_program".to_string(), + solana_compute_budget_program::process_instruction, + ), feature_id: feature_set::add_compute_budget_program::id(), }, BuiltinFeatureTransition::Add { - builtin: BuiltinProgram { - name: "address_lookup_table_program".to_string(), - program_id: solana_address_lookup_table_program::id(), - process_instruction: - solana_address_lookup_table_program::processor::process_instruction, - }, + program_id: solana_address_lookup_table_program::id(), + builtin: create_builtin( + "address_lookup_table_program".to_string(), + solana_address_lookup_table_program::processor::process_instruction, + ), feature_id: feature_set::versioned_tx_message_enabled::id(), }, BuiltinFeatureTransition::Add { - builtin: BuiltinProgram { - name: "zk_token_proof_program".to_string(), - program_id: solana_zk_token_sdk::zk_token_proof_program::id(), - process_instruction: solana_zk_token_proof_program::process_instruction, - }, + program_id: solana_zk_token_sdk::zk_token_proof_program::id(), + builtin: create_builtin( + "zk_token_proof_program".to_string(), + solana_zk_token_proof_program::process_instruction, + ), feature_id: feature_set::zk_token_sdk_enabled::id(), }, ] @@ -157,9 +178,14 @@ pub fn get_pubkeys() -> Vec { let builtins = get(); let mut pubkeys = Vec::new(); - pubkeys.extend(builtins.genesis_builtins.iter().map(|b| b.program_id)); + pubkeys.extend( + builtins + .genesis_builtins + .iter() + .map(|(program_id, _builtin)| program_id), + ); pubkeys.extend(builtins.feature_transitions.iter().filter_map(|f| match f { - BuiltinFeatureTransition::Add { builtin, .. } => Some(builtin.program_id), + BuiltinFeatureTransition::Add { program_id, .. } => Some(program_id), BuiltinFeatureTransition::RemoveOrRetain { .. } => None, })); pubkeys