diff --git a/ledger-tool/src/program.rs b/ledger-tool/src/program.rs index f8937f9927734e..8488b52d2425db 100644 --- a/ledger-tool/src/program.rs +++ b/ledger-tool/src/program.rs @@ -531,12 +531,7 @@ pub fn program(ledger_path: &Path, matches: &ArgMatches<'_>) { AccountSharedData::new(0, 0, &loader_id), )); let interpreted = matches.value_of("mode").unwrap() != "jit"; - with_mock_invoke_context!( - invoke_context, - transaction_context, - transaction_accounts, - bank.get_builtin_programs() - ); + with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts); // Adding `DELAY_VISIBILITY_SLOT_OFFSET` to slots to accommodate for delay visibility of the program let mut loaded_programs = diff --git a/ledger/src/blockstore_processor.rs b/ledger/src/blockstore_processor.rs index 71eabe62b64226..b81212be870272 100644 --- a/ledger/src/blockstore_processor.rs +++ b/ledger/src/blockstore_processor.rs @@ -1840,7 +1840,7 @@ pub mod tests { matches::assert_matches, rand::{thread_rng, Rng}, solana_entry::entry::{create_ticks, next_entry, next_entry_mut}, - solana_program_runtime::{builtin_program::create_builtin, declare_process_instruction}, + solana_program_runtime::declare_process_instruction, solana_runtime::{ genesis_utils::{ self, create_genesis_config_with_vote_accounts, ValidatorVoteKeypairs, @@ -3002,10 +3002,7 @@ 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_program_id, - create_builtin("mockup".to_string(), mock_processor_ok), - ); + bank.add_mockup_builtin(mock_program_id, mock_processor_ok); let tx = Transaction::new_signed_with_payer( &[Instruction::new_with_bincode( @@ -3046,10 +3043,7 @@ pub mod tests { (0..get_instruction_errors().len()).for_each(|err| { let mut bank = Bank::new_for_tests(&genesis_config); - bank.add_builtin( - mock_program_id, - create_builtin("mockup".to_string(), mock_processor_err), - ); + bank.add_mockup_builtin(mock_program_id, 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 deleted file mode 100644 index 9fd93f20911cc9..00000000000000 --- a/program-runtime/src/builtin_program.rs +++ /dev/null @@ -1,47 +0,0 @@ -#[cfg(RUSTC_WITH_SPECIALIZATION)] -use solana_frozen_abi::abi_example::AbiExample; -use { - crate::{invoke_context::InvokeContext, loaded_programs::LoadedProgram}, - solana_rbpf::vm::{BuiltInFunction, BuiltInProgram}, - solana_sdk::pubkey::Pubkey, - std::sync::Arc, -}; - -pub type ProcessInstructionWithContext = BuiltInFunction>; - -pub fn create_builtin( - name: String, - process_instruction: ProcessInstructionWithContext, -) -> Arc { - let mut program = BuiltInProgram::default(); - program - .register_function(b"entrypoint", process_instruction) - .unwrap(); - Arc::new(LoadedProgram::new_builtin(name, 0, program)) -} - -#[derive(Debug, Clone, Default)] -pub struct BuiltinPrograms { - pub vec: Vec<(Pubkey, Arc)>, -} - -#[cfg(RUSTC_WITH_SPECIALIZATION)] -impl AbiExample for BuiltinPrograms { - fn example() -> Self { - Self::default() - } -} - -impl BuiltinPrograms { - pub fn new_mock( - program_id: Pubkey, - process_instruction: ProcessInstructionWithContext, - ) -> Self { - Self { - vec: vec![( - program_id, - create_builtin("mockup".to_string(), process_instruction), - )], - } - } -} diff --git a/program-runtime/src/invoke_context.rs b/program-runtime/src/invoke_context.rs index 53c1804e3c97f8..ecd29d88bd0e1c 100644 --- a/program-runtime/src/invoke_context.rs +++ b/program-runtime/src/invoke_context.rs @@ -1,10 +1,9 @@ use { crate::{ accounts_data_meter::AccountsDataMeter, - builtin_program::{BuiltinPrograms, ProcessInstructionWithContext}, compute_budget::ComputeBudget, ic_logger_msg, ic_msg, - loaded_programs::{LoadedProgramType, LoadedProgramsForTxBatch}, + loaded_programs::{LoadedProgram, LoadedProgramType, LoadedProgramsForTxBatch}, log_collector::LogCollector, pre_account::PreAccount, stable_log, @@ -15,7 +14,7 @@ use { solana_rbpf::{ ebpf::MM_HEAP_START, memory_region::MemoryMapping, - vm::{Config, ContextObject, ProgramResult}, + vm::{BuiltInFunction, Config, ContextObject, ProgramResult}, }, solana_sdk::{ account::{AccountSharedData, ReadableAccount}, @@ -44,6 +43,8 @@ use { }, }; +pub type ProcessInstructionWithContext = BuiltInFunction>; + /// Adapter so we can unify the interfaces of built-in programs and syscalls #[macro_export] macro_rules! declare_process_instruction { @@ -152,7 +153,6 @@ pub struct InvokeContext<'a> { pub transaction_context: &'a mut TransactionContext, rent: Rent, pre_accounts: Vec, - builtin_programs: &'a BuiltinPrograms, sysvar_cache: &'a SysvarCache, log_collector: Option>>, compute_budget: ComputeBudget, @@ -175,7 +175,6 @@ impl<'a> InvokeContext<'a> { pub fn new( transaction_context: &'a mut TransactionContext, rent: Rent, - builtin_programs: &'a BuiltinPrograms, sysvar_cache: &'a SysvarCache, log_collector: Option>>, compute_budget: ComputeBudget, @@ -191,7 +190,6 @@ impl<'a> InvokeContext<'a> { transaction_context, rent, pre_accounts: Vec::new(), - builtin_programs, sysvar_cache, log_collector, current_compute_budget: compute_budget, @@ -724,80 +722,77 @@ impl<'a> InvokeContext<'a> { } }; - for entry in self.builtin_programs.vec.iter() { - 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); - process_instruction( - // Removes lifetime tracking - unsafe { std::mem::transmute::<&mut InvokeContext, &mut InvokeContext>(self) }, - 0, - 0, - 0, - 0, - 0, - &mut mock_memory_mapping, - &mut result, - ); - let result = match result { - ProgramResult::Ok(_) => { - stable_log::program_success(&logger, &program_id); - Ok(()) - } - ProgramResult::Err(err) => { - stable_log::program_failure(&logger, &program_id, err.as_ref()); - if let Some(err) = err.downcast_ref::() { - Err(err.clone()) - } else { - Err(InstructionError::ProgramFailedToComplete) - } - } - }; - let post_remaining_units = self.get_remaining(); - *compute_units_consumed = pre_remaining_units.saturating_sub(post_remaining_units); - - if builtin_id == program_id - && result.is_ok() - && *compute_units_consumed == 0 - && self - .feature_set - .is_active(&native_programs_consume_cu::id()) - { - return Err(InstructionError::BuiltinProgramsMustConsumeComputeUnits); - } + // The Murmur3 hash value (used by RBPF) of the string "entrypoint" + const ENTRYPOINT_KEY: u32 = 0x71E3CF81; + let entry = self + .programs_loaded_for_tx_batch + .find(&builtin_id) + .ok_or(InstructionError::UnsupportedProgramId)?; + let process_instruction = match &entry.program { + LoadedProgramType::Builtin(program) => program + .lookup_function(ENTRYPOINT_KEY) + .map(|(_name, process_instruction)| process_instruction), + _ => None, + } + .ok_or(InstructionError::UnsupportedProgramId)?; + entry.usage_counter.fetch_add(1, Ordering::Relaxed); - process_executable_chain_time.stop(); - saturating_add_assign!( - timings - .execute_accessories - .process_instructions - .process_executable_chain_us, - process_executable_chain_time.as_us() - ); - return result; + 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); + process_instruction( + // Removes lifetime tracking + unsafe { std::mem::transmute::<&mut InvokeContext, &mut InvokeContext>(self) }, + 0, + 0, + 0, + 0, + 0, + &mut mock_memory_mapping, + &mut result, + ); + let result = match result { + ProgramResult::Ok(_) => { + stable_log::program_success(&logger, &program_id); + Ok(()) + } + ProgramResult::Err(err) => { + stable_log::program_failure(&logger, &program_id, err.as_ref()); + if let Some(err) = err.downcast_ref::() { + Err(err.clone()) + } else { + Err(InstructionError::ProgramFailedToComplete) + } } + }; + let post_remaining_units = self.get_remaining(); + *compute_units_consumed = pre_remaining_units.saturating_sub(post_remaining_units); + + if builtin_id == program_id + && result.is_ok() + && *compute_units_consumed == 0 + && self + .feature_set + .is_active(&native_programs_consume_cu::id()) + { + return Err(InstructionError::BuiltinProgramsMustConsumeComputeUnits); } - Err(InstructionError::UnsupportedProgramId) + process_executable_chain_time.stop(); + saturating_add_assign!( + timings + .execute_accessories + .process_instructions + .process_executable_chain_us, + process_executable_chain_time.as_us() + ); + result } /// Get this invocation's LogCollector @@ -893,12 +888,11 @@ impl<'a> InvokeContext<'a> { } #[macro_export] -macro_rules! with_mock_invoke_context_and_builtin_programs { +macro_rules! with_mock_invoke_context { ( $invoke_context:ident, $transaction_context:ident, - $transaction_accounts:expr, - $builtin_programs:expr + $transaction_accounts:expr $(,)? ) => { use { solana_sdk::{ @@ -944,7 +938,6 @@ macro_rules! with_mock_invoke_context_and_builtin_programs { let mut $invoke_context = InvokeContext::new( &mut $transaction_context, Rent::default(), - $builtin_programs, &sysvar_cache, Some(LogCollector::new_ref()), compute_budget, @@ -959,37 +952,6 @@ macro_rules! with_mock_invoke_context_and_builtin_programs { }; } -#[macro_export] -macro_rules! with_mock_invoke_context { - ( - $invoke_context:ident, - $transaction_context:ident, - $transaction_accounts:expr, - $builtin_programs:expr - ) => { - use $crate::with_mock_invoke_context_and_builtin_programs; - with_mock_invoke_context_and_builtin_programs!( - $invoke_context, - $transaction_context, - $transaction_accounts, - $builtin_programs - ); - }; - - ($invoke_context:ident, $transaction_context:ident, $transaction_accounts:expr) => { - use $crate::{ - builtin_program::BuiltinPrograms, with_mock_invoke_context_and_builtin_programs, - }; - let builtin_programs = BuiltinPrograms::default(); - with_mock_invoke_context_and_builtin_programs!( - $invoke_context, - $transaction_context, - $transaction_accounts, - &builtin_programs - ); - }; -} - pub fn mock_process_instruction( loader_id: &Pubkey, mut program_indices: Vec, @@ -1028,13 +990,13 @@ pub fn mock_process_instruction>(); - let builtin_programs = BuiltinPrograms::new_mock(callee_program_id, process_instruction); - with_mock_invoke_context!( - invoke_context, - transaction_context, - transaction_accounts, - &builtin_programs + with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts); + let mut programs_loaded_for_tx_batch = LoadedProgramsForTxBatch::default(); + programs_loaded_for_tx_batch.replenish( + callee_program_id, + Arc::new(LoadedProgram::new_builtin(0, 0, process_instruction)), ); + invoke_context.programs_loaded_for_tx_batch = &programs_loaded_for_tx_batch; // Account modification tests let cases = vec![ @@ -1428,13 +1390,13 @@ mod tests { is_writable: false, }, ]; - let builtin_programs = BuiltinPrograms::new_mock(program_key, process_instruction); - with_mock_invoke_context!( - invoke_context, - transaction_context, - transaction_accounts, - &builtin_programs + with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts); + let mut programs_loaded_for_tx_batch = LoadedProgramsForTxBatch::default(); + programs_loaded_for_tx_batch.replenish( + program_key, + Arc::new(LoadedProgram::new_builtin(0, 0, process_instruction)), ); + invoke_context.programs_loaded_for_tx_batch = &programs_loaded_for_tx_batch; // Test: Resize the account to *the same size*, so not consuming any additional size; this must succeed { diff --git a/program-runtime/src/lib.rs b/program-runtime/src/lib.rs index 4e06f1c111e297..7c7e91d0592105 100644 --- a/program-runtime/src/lib.rs +++ b/program-runtime/src/lib.rs @@ -11,7 +11,6 @@ extern crate solana_metrics; pub use solana_rbpf; pub mod accounts_data_meter; -pub mod builtin_program; pub mod compute_budget; pub mod invoke_context; pub mod loaded_programs; diff --git a/program-runtime/src/loaded_programs.rs b/program-runtime/src/loaded_programs.rs index 75a72ee2edd222..0d277852c1a6bf 100644 --- a/program-runtime/src/loaded_programs.rs +++ b/program-runtime/src/loaded_programs.rs @@ -1,7 +1,10 @@ #[cfg(all(not(target_os = "windows"), target_arch = "x86_64"))] use solana_rbpf::error::EbpfError; use { - crate::{invoke_context::InvokeContext, timings::ExecuteDetailsTimings}, + crate::{ + invoke_context::{InvokeContext, ProcessInstructionWithContext}, + timings::ExecuteDetailsTimings, + }, itertools::Itertools, log::{debug, log_enabled, trace}, percentage::PercentageInteger, @@ -68,7 +71,7 @@ pub enum LoadedProgramType { Typed(Executable>), #[cfg(test)] TestLoaded, - Builtin(String, BuiltInProgram>), + Builtin(BuiltInProgram>), } impl Debug for LoadedProgramType { @@ -85,9 +88,7 @@ impl Debug for LoadedProgramType { LoadedProgramType::Typed(_) => write!(f, "LoadedProgramType::Typed"), #[cfg(test)] LoadedProgramType::TestLoaded => write!(f, "LoadedProgramType::TestLoaded"), - LoadedProgramType::Builtin(name, _) => { - write!(f, "LoadedProgramType::Builtin({name})") - } + LoadedProgramType::Builtin(_) => write!(f, "LoadedProgramType::Builtin"), } } } @@ -268,17 +269,21 @@ impl LoadedProgram { /// Creates a new built-in program pub fn new_builtin( - name: String, deployment_slot: Slot, - program: BuiltInProgram>, + account_size: usize, + entrypoint: ProcessInstructionWithContext, ) -> Self { + let mut program = BuiltInProgram::default(); + program + .register_function(b"entrypoint", entrypoint) + .unwrap(); Self { deployment_slot, - account_size: 0, - effective_slot: deployment_slot.saturating_add(1), + account_size, + effective_slot: deployment_slot, maybe_expiration_slot: None, usage_counter: AtomicU64::new(0), - program: LoadedProgramType::Builtin(name, program), + program: LoadedProgramType::Builtin(program), } } @@ -318,7 +323,9 @@ impl LoadedProgram { } fn is_implicit_delay_visibility_tombstone(&self, slot: Slot) -> bool { - self.effective_slot.saturating_sub(self.deployment_slot) == DELAY_VISIBILITY_SLOT_OFFSET + !matches!(self.program, LoadedProgramType::Builtin(_)) + && self.effective_slot.saturating_sub(self.deployment_slot) + == DELAY_VISIBILITY_SLOT_OFFSET && slot >= self.deployment_slot && slot < self.effective_slot } @@ -420,7 +427,10 @@ impl LoadedPrograms { if existing.deployment_slot == entry.deployment_slot && existing.effective_slot == entry.effective_slot { - if matches!(existing.program, LoadedProgramType::Unloaded) { + if matches!(existing.program, LoadedProgramType::Builtin(_)) { + // Allow built-ins to be overwritten + second_level.swap_remove(entry_index); + } else if matches!(existing.program, LoadedProgramType::Unloaded) { // The unloaded program is getting reloaded // Copy over the usage counter to the new entry entry.usage_counter.store( @@ -463,7 +473,9 @@ impl LoadedPrograms { .rev() .filter(|entry| { let relation = fork_graph.relationship(entry.deployment_slot, new_root); - if entry.deployment_slot >= new_root { + if matches!(entry.program, LoadedProgramType::Builtin(_)) { + true + } else if entry.deployment_slot >= new_root { matches!(relation, BlockRelation::Equal | BlockRelation::Descendant) } else if !first_ancestor_found && (matches!(relation, BlockRelation::Ancestor) @@ -603,7 +615,7 @@ impl LoadedPrograms { | LoadedProgramType::FailedVerification | LoadedProgramType::Closed | LoadedProgramType::DelayVisibility - | LoadedProgramType::Builtin(_, _) => None, + | LoadedProgramType::Builtin(_) => None, }) }) .sorted_by_cached_key(|(_id, program)| program.usage_counter.load(Ordering::Relaxed)) @@ -712,7 +724,7 @@ mod tests { fn new_test_builtin_program(deployment_slot: Slot, effective_slot: Slot) -> Arc { Arc::new(LoadedProgram { - program: LoadedProgramType::Builtin("mockup".to_string(), BuiltInProgram::default()), + program: LoadedProgramType::Builtin(BuiltInProgram::default()), account_size: 0, deployment_slot, effective_slot, diff --git a/program-test/src/lib.rs b/program-test/src/lib.rs index 9a610a11b442dd..ec5de690b4ab3b 100644 --- a/program-test/src/lib.rs +++ b/program-test/src/lib.rs @@ -12,10 +12,8 @@ use { solana_banks_server::banks_server::start_local_server, solana_bpf_loader_program::serialization::serialize_parameters, solana_program_runtime::{ - builtin_program::{create_builtin, BuiltinPrograms, ProcessInstructionWithContext}, - compute_budget::ComputeBudget, - ic_msg, stable_log, - timings::ExecuteTimings, + compute_budget::ComputeBudget, ic_msg, invoke_context::ProcessInstructionWithContext, + loaded_programs::LoadedProgram, stable_log, timings::ExecuteTimings, }, solana_runtime::{ accounts_background_service::{AbsRequestSender, SnapshotRequestType}, @@ -436,7 +434,7 @@ pub fn read_file>(path: P) -> Vec { pub struct ProgramTest { accounts: Vec<(Pubkey, AccountSharedData)>, - builtin_programs: BuiltinPrograms, + builtin_programs: Vec<(Pubkey, String, LoadedProgram)>, compute_max_units: Option, prefer_bpf: bool, deactivate_feature_set: HashSet, @@ -473,7 +471,7 @@ impl Default for ProgramTest { Self { accounts: vec![], - builtin_programs: BuiltinPrograms::default(), + builtin_programs: vec![], compute_max_units: None, prefer_bpf, deactivate_feature_set, @@ -694,9 +692,10 @@ impl ProgramTest { process_instruction: ProcessInstructionWithContext, ) { info!("\"{}\" builtin program", program_name); - self.builtin_programs.vec.push(( + self.builtin_programs.push(( program_id, - create_builtin(program_name.to_string(), process_instruction), + program_name.to_string(), + LoadedProgram::new_builtin(0, program_name.len(), process_instruction), )); } @@ -708,7 +707,7 @@ impl ProgramTest { } fn setup_bank( - &self, + &mut self, ) -> ( Arc>, Arc>, @@ -792,8 +791,10 @@ impl ProgramTest { } // User-supplied additional builtins - for (program_id, builtin) in self.builtin_programs.vec.iter() { - bank.add_builtin(*program_id, builtin.clone()); + 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); } for (address, account) in self.accounts.iter() { @@ -831,7 +832,7 @@ impl ProgramTest { ) } - pub async fn start(self) -> (BanksClient, Keypair, Hash) { + pub async fn start(mut self) -> (BanksClient, Keypair, Hash) { let (bank_forks, block_commitment_cache, last_blockhash, gci) = self.setup_bank(); let target_tick_duration = gci.genesis_config.poh_config.target_tick_duration; let target_slot_duration = target_tick_duration * gci.genesis_config.ticks_per_slot as u32; @@ -866,7 +867,7 @@ impl ProgramTest { /// /// Returns a `BanksClient` interface into the test environment as well as a payer `Keypair` /// with SOL for sending transactions - pub async fn start_with_context(self) -> ProgramTestContext { + pub async fn start_with_context(mut self) -> ProgramTestContext { let (bank_forks, block_commitment_cache, last_blockhash, gci) = self.setup_bank(); let target_tick_duration = gci.genesis_config.poh_config.target_tick_duration; let transport = start_local_server( diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index 7eb96f0eecab1a..3da64cc224cbb3 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -3884,7 +3884,7 @@ mod tests { programdata_account = accounts.first().unwrap().clone(); program_account = accounts.get(3).unwrap().clone(); process_instruction( - &program_address, + &loader_id, &[0, 1], &[], vec![ diff --git a/runtime/benches/bank.rs b/runtime/benches/bank.rs index add7b2ede26e1f..ed43f1dae818e0 100644 --- a/runtime/benches/bank.rs +++ b/runtime/benches/bank.rs @@ -5,7 +5,7 @@ extern crate test; use { log::*, - solana_program_runtime::{builtin_program::create_builtin, declare_process_instruction}, + solana_program_runtime::declare_process_instruction, solana_runtime::{ bank::{test_utils::goto_end_of_slot, *}, bank_client::BankClient, @@ -131,10 +131,7 @@ fn do_bench_transactions( }); let mut bank = Bank::new_from_parent(&Arc::new(bank), &Pubkey::default(), 1); - bank.add_builtin( - Pubkey::from(BUILTIN_PROGRAM_ID), - create_builtin("mockup".to_string(), process_instruction), - ); + bank.add_mockup_builtin(Pubkey::from(BUILTIN_PROGRAM_ID), process_instruction); bank.add_builtin_account("solana_noop_program", &Pubkey::from(NOOP_PROGRAM_ID), false); let bank = Arc::new(bank); let bank_client = BankClient::new_shared(&bank); diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index b192b1fa3326fe..fda195e2f3d31c 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -56,7 +56,7 @@ use { ancestors::{Ancestors, AncestorsForSerialization}, bank::metrics::*, blockhash_queue::BlockhashQueue, - builtins::{self, BuiltinFeatureTransition, Builtins}, + builtins::{BuiltinPrototype, BUILTINS}, cost_tracker::CostTracker, epoch_accounts_hash::{self, EpochAccountsHash}, epoch_stakes::{EpochStakes, NodeVoteAccounts}, @@ -93,8 +93,8 @@ use { solana_perf::perf_libs, solana_program_runtime::{ accounts_data_meter::MAX_ACCOUNTS_DATA_LEN, - builtin_program::BuiltinPrograms, compute_budget::{self, ComputeBudget}, + invoke_context::ProcessInstructionWithContext, loaded_programs::{ LoadedProgram, LoadedProgramMatchCriteria, LoadedProgramType, LoadedPrograms, LoadedProgramsForTxBatch, WorkingSlot, @@ -777,7 +777,6 @@ impl PartialEq for Bank { // TODO: Confirm if all these fields are intentionally ignored! builtin_programs: _, runtime_config: _, - builtin_feature_transitions: _, rewards: _, cluster_type: _, lazy_rent_collection: _, @@ -1016,16 +1015,11 @@ pub struct Bank { /// stream for the slot == self.slot is_delta: AtomicBool, - /// The builtin programs - builtin_programs: BuiltinPrograms, + builtin_programs: HashSet, /// Optional config parameters that can override runtime behavior runtime_config: Arc, - /// Dynamic feature transitions for builtin programs - #[allow(clippy::rc_buffer)] - builtin_feature_transitions: Arc>, - /// Protocol-level rewards that were distributed by this bank pub rewards: RwLock>, @@ -1281,9 +1275,8 @@ impl Bank { stakes_cache: StakesCache::default(), epoch_stakes: HashMap::::default(), is_delta: AtomicBool::default(), - builtin_programs: BuiltinPrograms::default(), + builtin_programs: HashSet::::default(), runtime_config: Arc::::default(), - builtin_feature_transitions: Arc::>::default(), rewards: RwLock::>::default(), cluster_type: Option::::default(), lazy_rent_collection: AtomicBool::default(), @@ -1358,7 +1351,7 @@ impl Bank { runtime_config: Arc, paths: Vec, debug_keys: Option>>, - additional_builtins: Option<&Builtins>, + additional_builtins: Option<&[BuiltinPrototype]>, account_indexes: AccountSecondaryIndexes, shrink_ratio: AccountShrinkThreshold, debug_do_not_add_builtins: bool, @@ -1555,11 +1548,10 @@ impl Bank { ancestors: Ancestors::default(), hash: RwLock::new(Hash::default()), is_delta: AtomicBool::new(false), + builtin_programs, tick_height: AtomicU64::new(parent.tick_height.load(Relaxed)), signature_count: AtomicU64::new(0), - builtin_programs, runtime_config: parent.runtime_config.clone(), - builtin_feature_transitions: parent.builtin_feature_transitions.clone(), hard_forks: parent.hard_forks.clone(), rewards: RwLock::new(vec![]), cluster_type: parent.cluster_type, @@ -1834,7 +1826,7 @@ impl Bank { runtime_config: Arc, fields: BankFieldsToDeserialize, debug_keys: Option>>, - additional_builtins: Option<&Builtins>, + additional_builtins: Option<&[BuiltinPrototype]>, debug_do_not_add_builtins: bool, accounts_data_size_initial: u64, ) -> Self { @@ -1902,7 +1894,6 @@ impl Bank { is_delta: AtomicBool::new(fields.is_delta), builtin_programs: new(), runtime_config, - builtin_feature_transitions: new(), rewards: new(), cluster_type: Some(genesis_config.cluster_type), lazy_rent_collection: new(), @@ -4275,7 +4266,6 @@ impl Bank { let mut programs_updated_only_for_global_cache = LoadedProgramsForTxBatch::new(self.slot); let mut process_message_time = Measure::start("process_message_time"); let process_result = MessageProcessor::process_message( - &self.builtin_programs, tx.message(), &loaded_transaction.program_indices, &mut transaction_context, @@ -4528,13 +4518,20 @@ impl Bank { bpf_loader_deprecated::id(), ]; let program_owners_refs: Vec<&Pubkey> = program_owners.iter().collect(); - let program_accounts_map = self.rc.accounts.filter_executable_program_accounts( + let mut program_accounts_map = self.rc.accounts.filter_executable_program_accounts( &self.ancestors, sanitized_txs, &mut check_results, &program_owners_refs, &self.blockhash_queue.read().unwrap(), ); + for precompile in get_precompiles() { + program_accounts_map.remove(&precompile.program_id); + } + let native_loader = native_loader::id(); + for builtin_program in self.builtin_programs.iter() { + program_accounts_map.insert(*builtin_program, &native_loader); + } let programs_loaded_for_tx_batch = Rc::new(RefCell::new( self.replenish_program_cache(&program_accounts_map), @@ -6262,24 +6259,24 @@ impl Bank { fn finish_init( &mut self, genesis_config: &GenesisConfig, - additional_builtins: Option<&Builtins>, + additional_builtins: Option<&[BuiltinPrototype]>, debug_do_not_add_builtins: bool, ) { self.rewards_pool_pubkeys = Arc::new(genesis_config.rewards_pools.keys().cloned().collect()); - let mut builtins = builtins::get(); - if let Some(additional_builtins) = additional_builtins { - builtins - .genesis_builtins - .extend_from_slice(&additional_builtins.genesis_builtins); - builtins - .feature_transitions - .extend_from_slice(&additional_builtins.feature_transitions); - } if !debug_do_not_add_builtins { - for (program_id, builtin) in builtins.genesis_builtins { - self.add_builtin(program_id, builtin); + for builtin in BUILTINS + .iter() + .chain(additional_builtins.unwrap_or(&[]).iter()) + { + if builtin.feature_id.is_none() { + self.add_builtin( + builtin.program_id, + builtin.name.to_string(), + LoadedProgram::new_builtin(0, builtin.name.len(), builtin.entrypoint), + ); + } } for precompile in get_precompiles() { if precompile.feature.is_none() { @@ -6287,7 +6284,6 @@ impl Bank { } } } - self.builtin_feature_transitions = Arc::new(builtins.feature_transitions); self.apply_feature_activations( ApplyFeatureActivationsCaller::FinishInit, @@ -7311,40 +7307,39 @@ impl Bank { !self.is_delta.load(Relaxed) } + pub fn add_mockup_builtin( + &mut self, + program_id: Pubkey, + entrypoint: ProcessInstructionWithContext, + ) { + self.add_builtin( + program_id, + "mockup".to_string(), + LoadedProgram::new_builtin(0, 0, entrypoint), + ); + } + /// Add a built-in program - pub fn add_builtin(&mut self, program_id: Pubkey, builtin: Arc) { - let name = match &builtin.program { - LoadedProgramType::Builtin(name, _) => name, - _ => unreachable!(), - }; + pub fn add_builtin(&mut self, program_id: Pubkey, name: String, builtin: LoadedProgram) { debug!("Adding program {} under {:?}", name, program_id); self.add_builtin_account(name.as_str(), &program_id, false); - if let Some(entry) = self - .builtin_programs - .vec - .iter_mut() - .find(|entry| entry.0 == program_id) - { - entry.1 = builtin.clone(); - } else { - self.builtin_programs - .vec - .push((program_id, builtin.clone())); - } + self.builtin_programs.insert(program_id); + self.loaded_programs_cache + .write() + .unwrap() + .replenish(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) { + pub fn remove_builtin(&mut self, program_id: Pubkey, name: String) { debug!("Removing program {}", program_id); // Don't remove the account since the bank expects the account state to // be idempotent self.add_builtin( program_id, - Arc::new(LoadedProgram::new_tombstone( - self.slot, - LoadedProgramType::Closed, - )), + name, + LoadedProgram::new_tombstone(self.slot, LoadedProgramType::Closed), ); debug!("Removed program {}", program_id); } @@ -7573,18 +7568,25 @@ impl Bank { only_apply_transitions_for_new_features: bool, new_feature_activations: &HashSet, ) { - let feature_set = self.feature_set.clone(); - - let builtin_feature_transitions = self.builtin_feature_transitions.clone(); - for transition in builtin_feature_transitions.iter() { - let should_apply_action_for_feature_transition = - if only_apply_transitions_for_new_features { - new_feature_activations.contains(&transition.feature_id) - } else { - feature_set.is_active(&transition.feature_id) - }; - if should_apply_action_for_feature_transition { - self.add_builtin(transition.program_id, transition.builtin.clone()); + for builtin in BUILTINS.iter() { + if let Some(feature_id) = builtin.feature_id { + let should_apply_action_for_feature_transition = + if only_apply_transitions_for_new_features { + new_feature_activations.contains(&feature_id) + } else { + self.feature_set.is_active(&feature_id) + }; + if should_apply_action_for_feature_transition { + self.add_builtin( + builtin.program_id, + builtin.name.to_string(), + LoadedProgram::new_builtin( + self.feature_set.activated_slot(&feature_id).unwrap_or(0), + builtin.name.len(), + builtin.entrypoint, + ), + ); + } } } @@ -7775,11 +7777,6 @@ impl Bank { &mut error_counters, ) } - - /// Return reference to builtin_progams - pub fn get_builtin_programs(&self) -> &BuiltinPrograms { - &self.builtin_programs - } } /// Compute how much an account has changed size. This function is useful when the data size delta diff --git a/runtime/src/bank/tests.rs b/runtime/src/bank/tests.rs index c0c273b500942f..0f581b46dee39b 100644 --- a/runtime/src/bank/tests.rs +++ b/runtime/src/bank/tests.rs @@ -33,11 +33,10 @@ use { serde::{Deserialize, Serialize}, solana_logger, solana_program_runtime::{ - builtin_program::create_builtin, compute_budget::{self, ComputeBudget, MAX_COMPUTE_UNIT_LIMIT}, declare_process_instruction, invoke_context::mock_process_instruction, - loaded_programs::{LoadedProgramType, DELAY_VISIBILITY_SLOT_OFFSET}, + loaded_programs::{LoadedProgram, LoadedProgramType, DELAY_VISIBILITY_SLOT_OFFSET}, prioritization_fee::{PrioritizationFeeDetails, PrioritizationFeeType}, timings::ExecuteTimings, }, @@ -1379,10 +1378,7 @@ 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_id, - create_builtin("mockup".to_string(), process_instruction), - ); + bank.add_mockup_builtin(mock_program_id, process_instruction); assert_eq!(bank.last_blockhash(), genesis_config.hash()); @@ -4818,10 +4814,7 @@ fn test_add_builtin() { }); assert!(bank.get_account(&mock_vote_program_id()).is_none()); - bank.add_builtin( - mock_vote_program_id(), - create_builtin("mockup".to_string(), process_instruction), - ); + bank.add_mockup_builtin(mock_vote_program_id(), process_instruction); assert!(bank.get_account(&mock_vote_program_id()).is_some()); let mock_account = Keypair::new(); @@ -4894,10 +4887,7 @@ fn test_add_duplicate_static_program() { ); let vote_loader_account = bank.get_account(&solana_vote_program::id()).unwrap(); - bank.add_builtin( - solana_vote_program::id(), - create_builtin("mockup".to_string(), process_instruction), - ); + bank.add_mockup_builtin(solana_vote_program::id(), 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. assert_eq!(vote_loader_account.data(), new_vote_loader_account.data()); @@ -4950,11 +4940,13 @@ fn test_add_instruction_processor_for_existing_unrelated_accounts() { bank.add_builtin( vote_id, - create_builtin("mock_program1".to_string(), process_instruction), + "mock_program1".to_string(), + LoadedProgram::new_builtin(0, 0, process_instruction), ); bank.add_builtin( stake_id, - create_builtin("mock_program2".to_string(), process_instruction), + "mock_program2".to_string(), + LoadedProgram::new_builtin(0, 0, process_instruction), ); { let stakes = bank.stakes_cache.stakes(); @@ -4978,14 +4970,8 @@ 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( - vote_id, - create_builtin("mockup".to_string(), process_instruction), - ); - bank.add_builtin( - stake_id, - create_builtin("mockup".to_string(), process_instruction), - ); + bank.add_mockup_builtin(vote_id, process_instruction); + bank.add_mockup_builtin(stake_id, process_instruction); add_root_and_flush_write_cache(&bank); bank.update_accounts_hash_for_tests(); let new_hash = bank.get_accounts_hash().unwrap(); @@ -6236,10 +6222,7 @@ fn test_transaction_with_duplicate_accounts_in_instruction() { }); let mock_program_id = Pubkey::from([2u8; 32]); - bank.add_builtin( - mock_program_id, - create_builtin("mockup".to_string(), process_instruction), - ); + bank.add_mockup_builtin(mock_program_id, process_instruction); let from_pubkey = solana_sdk::pubkey::new_rand(); let to_pubkey = solana_sdk::pubkey::new_rand(); @@ -6275,10 +6258,7 @@ 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_id, - create_builtin("mockup".to_string(), process_instruction), - ); + bank.add_mockup_builtin(mock_program_id, process_instruction); let from_pubkey = solana_sdk::pubkey::new_rand(); let to_pubkey = solana_sdk::pubkey::new_rand(); @@ -6330,10 +6310,7 @@ fn test_account_ids_after_program_ids() { tx.message.account_keys.push(solana_sdk::pubkey::new_rand()); - bank.add_builtin( - solana_vote_program::id(), - create_builtin("mockup".to_string(), process_instruction), - ); + bank.add_mockup_builtin(solana_vote_program::id(), process_instruction); let result = bank.process_transaction(&tx); assert_eq!(result, Ok(())); let account = bank.get_account(&solana_vote_program::id()).unwrap(); @@ -6383,10 +6360,7 @@ fn test_duplicate_account_key() { AccountMeta::new(to_pubkey, false), ]; - bank.add_builtin( - solana_vote_program::id(), - create_builtin("mockup".to_string(), process_instruction), - ); + bank.add_mockup_builtin(solana_vote_program::id(), process_instruction); let instruction = Instruction::new_with_bincode(solana_vote_program::id(), &10, account_metas); let mut tx = Transaction::new_signed_with_payer( @@ -6415,10 +6389,7 @@ fn test_process_transaction_with_too_many_account_locks() { AccountMeta::new(to_pubkey, false), ]; - bank.add_builtin( - solana_vote_program::id(), - create_builtin("mockup".to_string(), process_instruction), - ); + bank.add_mockup_builtin(solana_vote_program::id(), process_instruction); let instruction = Instruction::new_with_bincode(solana_vote_program::id(), &10, account_metas); let mut tx = Transaction::new_signed_with_payer( @@ -6451,10 +6422,7 @@ fn test_program_id_as_payer() { AccountMeta::new(to_pubkey, false), ]; - bank.add_builtin( - solana_vote_program::id(), - create_builtin("mockup".to_string(), process_instruction), - ); + bank.add_mockup_builtin(solana_vote_program::id(), process_instruction); let instruction = Instruction::new_with_bincode(solana_vote_program::id(), &10, account_metas); let mut tx = Transaction::new_signed_with_payer( @@ -6497,10 +6465,7 @@ fn test_ref_account_key_after_program_id() { AccountMeta::new(to_pubkey, false), ]; - bank.add_builtin( - solana_vote_program::id(), - create_builtin("mockup".to_string(), process_instruction), - ); + bank.add_mockup_builtin(solana_vote_program::id(), process_instruction); let instruction = Instruction::new_with_bincode(solana_vote_program::id(), &10, account_metas); let mut tx = Transaction::new_signed_with_payer( @@ -6531,7 +6496,11 @@ fn test_fuzz_instructions() { .map(|i| { let key = solana_sdk::pubkey::new_rand(); let name = format!("program{i:?}"); - bank.add_builtin(key, create_builtin(name.clone(), process_instruction)); + bank.add_builtin( + key, + name.clone(), + LoadedProgram::new_builtin(0, 0, process_instruction), + ); (key, name.as_bytes().to_vec()) }) .collect(); @@ -6737,10 +6706,7 @@ fn test_same_program_id_uses_unqiue_executable_accounts() { // Add a new program let program1_pubkey = solana_sdk::pubkey::new_rand(); - bank.add_builtin( - program1_pubkey, - create_builtin("mockup".to_string(), process_instruction), - ); + bank.add_mockup_builtin(program1_pubkey, process_instruction); // Add a new program owned by the first let program2_pubkey = solana_sdk::pubkey::new_rand(); @@ -6955,17 +6921,15 @@ fn test_add_builtin_no_overwrite() { )); assert_eq!(bank.get_account_modified_slot(&program_id), None); - Arc::get_mut(&mut bank).unwrap().add_builtin( - program_id, - create_builtin("mockup".to_string(), process_instruction), - ); + Arc::get_mut(&mut bank) + .unwrap() + .add_mockup_builtin(program_id, 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( - program_id, - create_builtin("mockup".to_string(), process_instruction), - ); + Arc::get_mut(&mut bank) + .unwrap() + .add_mockup_builtin(program_id, process_instruction); assert_eq!(bank.get_account_modified_slot(&program_id).unwrap().1, slot); } @@ -6981,17 +6945,15 @@ 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( - loader_id, - create_builtin("mockup".to_string(), process_instruction), - ); + Arc::get_mut(&mut bank) + .unwrap() + .add_mockup_builtin(loader_id, 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( - loader_id, - create_builtin("mockup".to_string(), process_instruction), - ); + Arc::get_mut(&mut bank) + .unwrap() + .add_mockup_builtin(loader_id, process_instruction); assert_eq!(bank.get_account_modified_slot(&loader_id).unwrap().1, slot); } @@ -9576,10 +9538,7 @@ fn test_tx_return_data() { let mock_program_id = Pubkey::from([2u8; 32]); let blockhash = bank.last_blockhash(); - bank.add_builtin( - mock_program_id, - create_builtin("mockup".to_string(), process_instruction), - ); + bank.add_mockup_builtin(mock_program_id, process_instruction); for index in [ None, @@ -9768,10 +9727,7 @@ fn test_transfer_sysvar() { }); let program_id = solana_sdk::pubkey::new_rand(); - bank.add_builtin( - program_id, - create_builtin("mockup".to_string(), process_instruction), - ); + bank.add_mockup_builtin(program_id, process_instruction); let blockhash = bank.last_blockhash(); #[allow(deprecated)] @@ -9983,10 +9939,7 @@ fn test_compute_budget_program_noop() { Ok(()) }); let program_id = solana_sdk::pubkey::new_rand(); - bank.add_builtin( - program_id, - create_builtin("mockup".to_string(), process_instruction), - ); + bank.add_mockup_builtin(program_id, process_instruction); let message = Message::new( &[ @@ -10029,10 +9982,7 @@ fn test_compute_request_instruction() { Ok(()) }); let program_id = solana_sdk::pubkey::new_rand(); - bank.add_builtin( - program_id, - create_builtin("mockup".to_string(), process_instruction), - ); + bank.add_mockup_builtin(program_id, process_instruction); let message = Message::new( &[ @@ -10082,10 +10032,7 @@ fn test_failed_compute_request_instruction() { Ok(()) }); let program_id = solana_sdk::pubkey::new_rand(); - bank.add_builtin( - program_id, - create_builtin("mockup".to_string(), process_instruction), - ); + bank.add_mockup_builtin(program_id, process_instruction); // This message will not be executed because the compute budget request is invalid let message0 = Message::new( @@ -10759,10 +10706,7 @@ fn test_invalid_rent_state_changes_existing_accounts() { ); let mut bank = Bank::new_for_tests(&genesis_config); - bank.add_builtin( - mock_program_id, - create_builtin("mockup".to_string(), mock_transfer_process_instruction), - ); + bank.add_mockup_builtin(mock_program_id, mock_transfer_process_instruction); let recent_blockhash = bank.last_blockhash(); let check_account_is_rent_exempt = |pubkey: &Pubkey| -> bool { @@ -10845,10 +10789,7 @@ fn test_invalid_rent_state_changes_new_accounts() { let rent_exempt_minimum = genesis_config.rent.minimum_balance(account_data_size); let mut bank = Bank::new_for_tests(&genesis_config); - bank.add_builtin( - mock_program_id, - create_builtin("mockup".to_string(), mock_transfer_process_instruction), - ); + bank.add_mockup_builtin(mock_program_id, mock_transfer_process_instruction); let recent_blockhash = bank.last_blockhash(); let check_account_is_rent_exempt = |pubkey: &Pubkey| -> bool { @@ -10907,10 +10848,7 @@ fn test_drained_created_account() { let created_keypair = Keypair::new(); let mut bank = Bank::new_for_tests(&genesis_config); - bank.add_builtin( - mock_program_id, - create_builtin("mockup".to_string(), mock_transfer_process_instruction), - ); + bank.add_mockup_builtin(mock_program_id, mock_transfer_process_instruction); let recent_blockhash = bank.last_blockhash(); // Create and drain a small data size account @@ -11562,10 +11500,7 @@ fn test_resize_and_rent() { let mut bank = Bank::new_for_tests(&genesis_config); let mock_program_id = Pubkey::new_unique(); - bank.add_builtin( - mock_program_id, - create_builtin("mockup".to_string(), mock_realloc_process_instruction), - ); + bank.add_mockup_builtin(mock_program_id, mock_realloc_process_instruction); let recent_blockhash = bank.last_blockhash(); let account_data_size_small = 1024; @@ -11836,10 +11771,7 @@ fn test_accounts_data_size_and_resize_transactions() { } = genesis_utils::create_genesis_config(100 * LAMPORTS_PER_SOL); let mut bank = Bank::new_for_tests(&genesis_config); let mock_program_id = Pubkey::new_unique(); - bank.add_builtin( - mock_program_id, - create_builtin("mockup".to_string(), mock_realloc_process_instruction), - ); + bank.add_mockup_builtin(mock_program_id, mock_realloc_process_instruction); let recent_blockhash = bank.last_blockhash(); diff --git a/runtime/src/builtins.rs b/runtime/src/builtins.rs index 9bef21f3235124..d692101aaab42f 100644 --- a/runtime/src/builtins.rs +++ b/runtime/src/builtins.rs @@ -1,120 +1,104 @@ use { - solana_program_runtime::{builtin_program::create_builtin, loaded_programs::LoadedProgram}, + solana_program_runtime::invoke_context::ProcessInstructionWithContext, 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<(Pubkey, Arc)>, - - /// Dynamic feature transitions for builtin programs - pub feature_transitions: Vec, -} - /// Transitions of built-in programs at epoch bondaries when features are activated. -#[derive(Debug, Default, Clone)] -pub struct BuiltinFeatureTransition { - pub feature_id: Pubkey, +pub struct BuiltinPrototype { + pub feature_id: Option, pub program_id: Pubkey, - pub builtin: Arc, + pub name: &'static str, + pub entrypoint: ProcessInstructionWithContext, } -#[cfg(RUSTC_WITH_SPECIALIZATION)] -impl solana_frozen_abi::abi_example::AbiExample for BuiltinFeatureTransition { - fn example() -> Self { - // BuiltinFeatureTransition isn't serializable by definition. - Self::default() +impl std::fmt::Debug for BuiltinPrototype { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + let mut builder = f.debug_struct("BuiltinPrototype"); + builder.field("program_id", &self.program_id); + builder.field("name", &self.name); + builder.field("feature_id", &self.feature_id); + builder.finish() } } -/// Built-in programs that are always available -fn genesis_builtins() -> Vec<(Pubkey, Arc)> { - vec![ - ( - 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, - ), - ), - ( - solana_sdk::compute_budget::id(), - create_builtin( - "compute_budget_program".to_string(), - solana_compute_budget_program::process_instruction, - ), - ), - ( - solana_address_lookup_table_program::id(), - create_builtin( - "address_lookup_table_program".to_string(), - solana_address_lookup_table_program::processor::process_instruction, - ), - ), - ] +#[cfg(RUSTC_WITH_SPECIALIZATION)] +impl solana_frozen_abi::abi_example::AbiExample for BuiltinPrototype { + fn example() -> Self { + // BuiltinPrototype isn't serializable by definition. + solana_program_runtime::declare_process_instruction!(entrypoint, 0, |_invoke_context| { + // Do nothing + Ok(()) + }); + Self { + feature_id: None, + program_id: Pubkey::default(), + name: "", + entrypoint, + } + } } -/// Dynamic feature transitions for builtin programs -fn builtin_feature_transitions() -> Vec { - vec![BuiltinFeatureTransition { - feature_id: feature_set::zk_token_sdk_enabled::id(), +pub static BUILTINS: &[BuiltinPrototype] = &[ + BuiltinPrototype { + feature_id: None, + program_id: solana_system_program::id(), + name: "system_program", + entrypoint: solana_system_program::system_processor::process_instruction, + }, + BuiltinPrototype { + feature_id: None, + program_id: solana_vote_program::id(), + name: "vote_program", + entrypoint: solana_vote_program::vote_processor::process_instruction, + }, + BuiltinPrototype { + feature_id: None, + program_id: solana_stake_program::id(), + name: "stake_program", + entrypoint: solana_stake_program::stake_instruction::process_instruction, + }, + BuiltinPrototype { + feature_id: None, + program_id: solana_config_program::id(), + name: "config_program", + entrypoint: solana_config_program::config_processor::process_instruction, + }, + BuiltinPrototype { + feature_id: None, + program_id: bpf_loader_deprecated::id(), + name: "solana_bpf_loader_deprecated_program", + entrypoint: solana_bpf_loader_program::process_instruction, + }, + BuiltinPrototype { + feature_id: None, + program_id: bpf_loader::id(), + name: "solana_bpf_loader_program", + entrypoint: solana_bpf_loader_program::process_instruction, + }, + BuiltinPrototype { + feature_id: None, + program_id: bpf_loader_upgradeable::id(), + name: "solana_bpf_loader_upgradeable_program", + entrypoint: solana_bpf_loader_program::process_instruction, + }, + BuiltinPrototype { + feature_id: None, + program_id: solana_sdk::compute_budget::id(), + name: "compute_budget_program", + entrypoint: solana_compute_budget_program::process_instruction, + }, + BuiltinPrototype { + feature_id: None, + program_id: solana_address_lookup_table_program::id(), + name: "address_lookup_table_program", + entrypoint: solana_address_lookup_table_program::processor::process_instruction, + }, + BuiltinPrototype { + feature_id: Some(feature_set::zk_token_sdk_enabled::id()), 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, - ), - }] -} - -pub(crate) fn get() -> Builtins { - Builtins { - genesis_builtins: genesis_builtins(), - feature_transitions: builtin_feature_transitions(), - } -} + name: "zk_token_proof_program", + entrypoint: solana_zk_token_proof_program::process_instruction, + }, +]; diff --git a/runtime/src/message_processor.rs b/runtime/src/message_processor.rs index 3871d44761cbcb..bad5b615f936c9 100644 --- a/runtime/src/message_processor.rs +++ b/runtime/src/message_processor.rs @@ -2,7 +2,6 @@ use { serde::{Deserialize, Serialize}, solana_measure::measure::Measure, solana_program_runtime::{ - builtin_program::BuiltinPrograms, compute_budget::ComputeBudget, invoke_context::InvokeContext, loaded_programs::LoadedProgramsForTxBatch, @@ -52,7 +51,6 @@ impl MessageProcessor { /// The accounts are committed back to the bank only if every instruction succeeds. #[allow(clippy::too_many_arguments)] pub fn process_message( - builtin_programs: &BuiltinPrograms, message: &SanitizedMessage, program_indices: &[Vec], transaction_context: &mut TransactionContext, @@ -73,7 +71,6 @@ impl MessageProcessor { let mut invoke_context = InvokeContext::new( transaction_context, rent, - builtin_programs, sysvar_cache, log_collector, compute_budget, @@ -193,7 +190,7 @@ mod tests { use { super::*, crate::rent_collector::RentCollector, - solana_program_runtime::declare_process_instruction, + solana_program_runtime::{declare_process_instruction, loaded_programs::LoadedProgram}, solana_sdk::{ account::{AccountSharedData, ReadableAccount}, instruction::{AccountMeta, Instruction, InstructionError}, @@ -254,10 +251,7 @@ mod tests { let writable_pubkey = Pubkey::new_unique(); let readonly_pubkey = Pubkey::new_unique(); let mock_system_program_id = Pubkey::new_unique(); - let rent_collector = RentCollector::default(); - let builtin_programs = - BuiltinPrograms::new_mock(mock_system_program_id, process_instruction); let accounts = vec![ ( @@ -276,7 +270,11 @@ mod tests { let mut transaction_context = TransactionContext::new(accounts, Some(Rent::default()), 1, 3); let program_indices = vec![vec![2]]; - let programs_loaded_for_tx_batch = LoadedProgramsForTxBatch::default(); + let mut programs_loaded_for_tx_batch = LoadedProgramsForTxBatch::default(); + programs_loaded_for_tx_batch.replenish( + mock_system_program_id, + Arc::new(LoadedProgram::new_builtin(0, 0, process_instruction)), + ); let account_keys = (0..transaction_context.get_number_of_accounts()) .map(|index| { *transaction_context @@ -308,7 +306,6 @@ mod tests { let mut programs_modified_by_tx = LoadedProgramsForTxBatch::default(); let mut programs_updated_only_for_global_cache = LoadedProgramsForTxBatch::default(); let result = MessageProcessor::process_message( - &builtin_programs, &message, &program_indices, &mut transaction_context, @@ -362,7 +359,6 @@ mod tests { let mut programs_modified_by_tx = LoadedProgramsForTxBatch::default(); let mut programs_updated_only_for_global_cache = LoadedProgramsForTxBatch::default(); let result = MessageProcessor::process_message( - &builtin_programs, &message, &program_indices, &mut transaction_context, @@ -406,7 +402,6 @@ mod tests { let mut programs_modified_by_tx = LoadedProgramsForTxBatch::default(); let mut programs_updated_only_for_global_cache = LoadedProgramsForTxBatch::default(); let result = MessageProcessor::process_message( - &builtin_programs, &message, &program_indices, &mut transaction_context, @@ -490,11 +485,8 @@ mod tests { Err(InstructionError::InvalidInstructionData) } }); - let mock_program_id = Pubkey::from([2u8; 32]); let rent_collector = RentCollector::default(); - let builtin_programs = BuiltinPrograms::new_mock(mock_program_id, process_instruction); - let accounts = vec![ ( solana_sdk::pubkey::new_rand(), @@ -512,7 +504,11 @@ mod tests { let mut transaction_context = TransactionContext::new(accounts, Some(Rent::default()), 1, 3); let program_indices = vec![vec![2]]; - let programs_loaded_for_tx_batch = LoadedProgramsForTxBatch::default(); + let mut programs_loaded_for_tx_batch = LoadedProgramsForTxBatch::default(); + programs_loaded_for_tx_batch.replenish( + mock_program_id, + Arc::new(LoadedProgram::new_builtin(0, 0, process_instruction)), + ); let account_metas = vec![ AccountMeta::new( *transaction_context.get_key_of_account_at_index(0).unwrap(), @@ -541,7 +537,6 @@ mod tests { let mut programs_modified_by_tx = LoadedProgramsForTxBatch::default(); let mut programs_updated_only_for_global_cache = LoadedProgramsForTxBatch::default(); let result = MessageProcessor::process_message( - &builtin_programs, &message, &program_indices, &mut transaction_context, @@ -579,7 +574,6 @@ mod tests { let mut programs_modified_by_tx = LoadedProgramsForTxBatch::default(); let mut programs_updated_only_for_global_cache = LoadedProgramsForTxBatch::default(); let result = MessageProcessor::process_message( - &builtin_programs, &message, &program_indices, &mut transaction_context, @@ -614,7 +608,6 @@ mod tests { let mut programs_modified_by_tx = LoadedProgramsForTxBatch::default(); let mut programs_updated_only_for_global_cache = LoadedProgramsForTxBatch::default(); let result = MessageProcessor::process_message( - &builtin_programs, &message, &program_indices, &mut transaction_context, @@ -665,7 +658,6 @@ mod tests { declare_process_instruction!(process_instruction, 1, |_invoke_context| { Err(InstructionError::Custom(0xbabb1e)) }); - let builtin_programs = BuiltinPrograms::new_mock(mock_program_id, process_instruction); let mut secp256k1_account = AccountSharedData::new(1, 0, &native_loader::id()); secp256k1_account.set_executable(true); @@ -689,11 +681,14 @@ mod tests { None, ))); let sysvar_cache = SysvarCache::default(); - let programs_loaded_for_tx_batch = LoadedProgramsForTxBatch::default(); + let mut programs_loaded_for_tx_batch = LoadedProgramsForTxBatch::default(); + programs_loaded_for_tx_batch.replenish( + mock_program_id, + Arc::new(LoadedProgram::new_builtin(0, 0, process_instruction)), + ); let mut programs_modified_by_tx = LoadedProgramsForTxBatch::default(); let mut programs_updated_only_for_global_cache = LoadedProgramsForTxBatch::default(); let result = MessageProcessor::process_message( - &builtin_programs, &message, &[vec![0], vec![1]], &mut transaction_context, diff --git a/runtime/src/serde_snapshot.rs b/runtime/src/serde_snapshot.rs index 59e603d6d80d98..145f57d975b0c5 100644 --- a/runtime/src/serde_snapshot.rs +++ b/runtime/src/serde_snapshot.rs @@ -12,7 +12,7 @@ use { accounts_update_notifier_interface::AccountsUpdateNotifier, bank::{Bank, BankFieldsToDeserialize, BankRc}, blockhash_queue::BlockhashQueue, - builtins::Builtins, + builtins::BuiltinPrototype, epoch_accounts_hash::EpochAccountsHash, epoch_stakes::EpochStakes, rent_collector::RentCollector, @@ -353,7 +353,7 @@ pub(crate) fn bank_from_streams( genesis_config: &GenesisConfig, runtime_config: &RuntimeConfig, debug_keys: Option>>, - additional_builtins: Option<&Builtins>, + additional_builtins: Option<&[BuiltinPrototype]>, account_secondary_indexes: AccountSecondaryIndexes, limit_load_slot_count_from_snapshot: Option, shrink_ratio: AccountShrinkThreshold, @@ -563,7 +563,7 @@ fn reconstruct_bank_from_fields( account_paths: &[PathBuf], storage_and_next_append_vec_id: StorageAndNextAppendVecId, debug_keys: Option>>, - additional_builtins: Option<&Builtins>, + additional_builtins: Option<&[BuiltinPrototype]>, account_secondary_indexes: AccountSecondaryIndexes, limit_load_slot_count_from_snapshot: Option, shrink_ratio: AccountShrinkThreshold, diff --git a/runtime/src/snapshot_minimizer.rs b/runtime/src/snapshot_minimizer.rs index 54398dd400ef92..b8d5a09754873b 100644 --- a/runtime/src/snapshot_minimizer.rs +++ b/runtime/src/snapshot_minimizer.rs @@ -7,6 +7,7 @@ use { }, accounts_partition, bank::Bank, + builtins::BUILTINS, static_ids, }, dashmap::DashSet, @@ -114,13 +115,9 @@ impl<'a> SnapshotMinimizer<'a> { /// Used to get builtin accounts in `minimize` fn get_builtins(&self) { - self.bank - .get_builtin_programs() - .vec - .iter() - .for_each(|(pubkey, _builtin)| { - self.minimized_account_set.insert(*pubkey); - }); + BUILTINS.iter().for_each(|e| { + self.minimized_account_set.insert(e.program_id); + }); } /// Used to get static runtime accounts in `minimize` diff --git a/runtime/src/snapshot_utils.rs b/runtime/src/snapshot_utils.rs index faa480b4f77178..4886ef25f81241 100644 --- a/runtime/src/snapshot_utils.rs +++ b/runtime/src/snapshot_utils.rs @@ -10,7 +10,7 @@ use { accounts_update_notifier_interface::AccountsUpdateNotifier, append_vec::AppendVec, bank::{Bank, BankFieldsToDeserialize, BankSlotDelta}, - builtins::Builtins, + builtins::BuiltinPrototype, hardened_unpack::{ streaming_unpack_snapshot, unpack_snapshot, ParallelSelector, UnpackError, UnpackedAppendVecMap, @@ -1475,7 +1475,7 @@ pub fn bank_from_snapshot_archives( genesis_config: &GenesisConfig, runtime_config: &RuntimeConfig, debug_keys: Option>>, - additional_builtins: Option<&Builtins>, + additional_builtins: Option<&[BuiltinPrototype]>, account_secondary_indexes: AccountSecondaryIndexes, limit_load_slot_count_from_snapshot: Option, shrink_ratio: AccountShrinkThreshold, @@ -1594,7 +1594,7 @@ pub fn bank_from_latest_snapshot_archives( genesis_config: &GenesisConfig, runtime_config: &RuntimeConfig, debug_keys: Option>>, - additional_builtins: Option<&Builtins>, + additional_builtins: Option<&[BuiltinPrototype]>, account_secondary_indexes: AccountSecondaryIndexes, limit_load_slot_count_from_snapshot: Option, shrink_ratio: AccountShrinkThreshold, @@ -1689,7 +1689,7 @@ pub fn bank_from_snapshot_dir( genesis_config: &GenesisConfig, runtime_config: &RuntimeConfig, debug_keys: Option>>, - additional_builtins: Option<&Builtins>, + additional_builtins: Option<&[BuiltinPrototype]>, account_secondary_indexes: AccountSecondaryIndexes, limit_load_slot_count_from_snapshot: Option, shrink_ratio: AccountShrinkThreshold, @@ -1757,7 +1757,7 @@ pub fn bank_from_latest_snapshot_dir( runtime_config: &RuntimeConfig, account_paths: &[PathBuf], debug_keys: Option>>, - additional_builtins: Option<&Builtins>, + additional_builtins: Option<&[BuiltinPrototype]>, account_secondary_indexes: AccountSecondaryIndexes, limit_load_slot_count_from_snapshot: Option, shrink_ratio: AccountShrinkThreshold, @@ -2599,7 +2599,7 @@ fn rebuild_bank_from_unarchived_snapshots( genesis_config: &GenesisConfig, runtime_config: &RuntimeConfig, debug_keys: Option>>, - additional_builtins: Option<&Builtins>, + additional_builtins: Option<&[BuiltinPrototype]>, account_secondary_indexes: AccountSecondaryIndexes, limit_load_slot_count_from_snapshot: Option, shrink_ratio: AccountShrinkThreshold, @@ -2695,7 +2695,7 @@ fn rebuild_bank_from_snapshot( genesis_config: &GenesisConfig, runtime_config: &RuntimeConfig, debug_keys: Option>>, - additional_builtins: Option<&Builtins>, + additional_builtins: Option<&[BuiltinPrototype]>, account_secondary_indexes: AccountSecondaryIndexes, limit_load_slot_count_from_snapshot: Option, shrink_ratio: AccountShrinkThreshold,