diff --git a/ledger/src/builtins.rs b/ledger/src/builtins.rs index d8a9ffa37e..2388a650b6 100644 --- a/ledger/src/builtins.rs +++ b/ledger/src/builtins.rs @@ -1,13 +1,20 @@ -use solana_runtime::builtins::{Builtin, BuiltinFeatureTransition, Builtins}; +use { + solana_program_runtime::builtin_program::BuiltinProgram, + solana_runtime::builtins::{BuiltinFeatureTransition, Builtins}, +}; macro_rules! to_builtin { ($b:expr) => { - Builtin::new(&$b.0, $b.1, $b.2) + BuiltinProgram { + name: $b.0.to_string(), + program_id: $b.1, + process_instruction: $b.2, + } }; } /// Builtin programs that are always available -fn genesis_builtins(bpf_jit: bool) -> Vec { +fn genesis_builtins(bpf_jit: bool) -> Vec { // Currently JIT is not supported on the SBF VM: // !x86_64: https://github.com/qmonnet/rbpf/issues/48 // Windows: https://github.com/solana-labs/rbpf/issues/217 diff --git a/program-runtime/src/builtin_program.rs b/program-runtime/src/builtin_program.rs new file mode 100644 index 0000000000..cd876c3e81 --- /dev/null +++ b/program-runtime/src/builtin_program.rs @@ -0,0 +1,64 @@ +#[cfg(RUSTC_WITH_SPECIALIZATION)] +use {crate::declare_process_instruction, solana_frozen_abi::abi_example::AbiExample}; +use { + crate::invoke_context::InvokeContext, solana_rbpf::vm::BuiltInFunction, + solana_sdk::pubkey::Pubkey, +}; + +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, + } + } +} + +#[derive(Debug, Clone, Default)] +pub struct BuiltinPrograms { + pub vec: Vec, +} + +#[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![BuiltinProgram { + name: "mock instruction processor".to_string(), + program_id, + process_instruction, + }], + } + } +} diff --git a/program-runtime/src/invoke_context.rs b/program-runtime/src/invoke_context.rs index 598e90e864..d13104d948 100644 --- a/program-runtime/src/invoke_context.rs +++ b/program-runtime/src/invoke_context.rs @@ -1,6 +1,7 @@ use { crate::{ accounts_data_meter::AccountsDataMeter, + builtin_program::{BuiltinPrograms, ProcessInstructionWithContext}, compute_budget::ComputeBudget, executor_cache::TransactionExecutorCache, ic_logger_msg, ic_msg, @@ -14,7 +15,7 @@ use { solana_rbpf::{ ebpf::MM_HEAP_START, memory_region::MemoryMapping, - vm::{BuiltInFunction, Config, ContextObject, ProgramResult}, + vm::{Config, ContextObject, ProgramResult}, }, solana_sdk::{ account::{AccountSharedData, ReadableAccount}, @@ -82,24 +83,6 @@ macro_rules! declare_process_instruction { }; } -pub type ProcessInstructionWithContext = BuiltInFunction>; - -#[derive(Clone)] -pub struct BuiltinProgram { - 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, - "{}: {:p}", - self.program_id, self.process_instruction as *const ProcessInstructionWithContext, - ) - } -} - impl<'a> ContextObject for InvokeContext<'a> { fn trace(&mut self, state: [u64; 12]) { self.syscall_context @@ -169,7 +152,7 @@ pub struct InvokeContext<'a> { pub transaction_context: &'a mut TransactionContext, rent: Rent, pre_accounts: Vec, - builtin_programs: &'a [BuiltinProgram], + builtin_programs: &'a BuiltinPrograms, sysvar_cache: &'a SysvarCache, log_collector: Option>>, compute_budget: ComputeBudget, @@ -189,7 +172,7 @@ impl<'a> InvokeContext<'a> { pub fn new( transaction_context: &'a mut TransactionContext, rent: Rent, - builtin_programs: &'a [BuiltinProgram], + builtin_programs: &'a BuiltinPrograms, sysvar_cache: &'a SysvarCache, log_collector: Option>>, compute_budget: ComputeBudget, @@ -731,7 +714,7 @@ impl<'a> InvokeContext<'a> { } }; - for entry in self.builtin_programs { + for entry in self.builtin_programs.vec.iter() { if entry.program_id == builtin_id { let program_id = *instruction_context.get_last_program_key(self.transaction_context)?; @@ -894,9 +877,9 @@ macro_rules! with_mock_invoke_context { }, std::{cell::RefCell, rc::Rc, sync::Arc}, $crate::{ - compute_budget::ComputeBudget, executor_cache::TransactionExecutorCache, - invoke_context::InvokeContext, log_collector::LogCollector, - sysvar_cache::SysvarCache, + builtin_program::BuiltinPrograms, compute_budget::ComputeBudget, + executor_cache::TransactionExecutorCache, invoke_context::InvokeContext, + log_collector::LogCollector, sysvar_cache::SysvarCache, }, }; let compute_budget = ComputeBudget::default(); @@ -907,6 +890,7 @@ macro_rules! with_mock_invoke_context { compute_budget.max_instruction_trace_length, ); $transaction_context.enable_cap_accounts_data_allocations_per_transaction(); + let builtin_programs = BuiltinPrograms::default(); let mut sysvar_cache = SysvarCache::default(); sysvar_cache.fill_missing_entries(|pubkey, callback| { for index in 0..$transaction_context.get_number_of_accounts() { @@ -928,7 +912,7 @@ macro_rules! with_mock_invoke_context { let mut $invoke_context = InvokeContext::new( &mut $transaction_context, Rent::default(), - &[], + &builtin_programs, &sysvar_cache, Some(LogCollector::new_ref()), compute_budget, @@ -980,11 +964,8 @@ pub fn mock_process_instruction>(); with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts); - let builtin_programs = &[BuiltinProgram { - program_id: callee_program_id, - process_instruction, - }]; - invoke_context.builtin_programs = builtin_programs; + let builtin_programs = BuiltinPrograms::new_mock(callee_program_id, process_instruction); + invoke_context.builtin_programs = &builtin_programs; // Account modification tests let cases = vec![ @@ -1378,11 +1356,8 @@ mod tests { }, ]; with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts); - let builtin_programs = &[BuiltinProgram { - program_id: program_key, - process_instruction, - }]; - invoke_context.builtin_programs = builtin_programs; + let builtin_programs = BuiltinPrograms::new_mock(program_key, process_instruction); + invoke_context.builtin_programs = &builtin_programs; // 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 cb87b5585c..59d8c3556a 100644 --- a/program-runtime/src/lib.rs +++ b/program-runtime/src/lib.rs @@ -11,6 +11,7 @@ extern crate solana_metrics; pub use solana_rbpf; pub mod accounts_data_meter; +pub mod builtin_program; pub mod compute_budget; pub mod executor_cache; pub mod invoke_context; diff --git a/program-test/src/lib.rs b/program-test/src/lib.rs index ab87bbd6a3..13f6e8b082 100644 --- a/program-test/src/lib.rs +++ b/program-test/src/lib.rs @@ -11,14 +11,15 @@ use { solana_banks_server::banks_server::start_local_server, solana_bpf_loader_program::serialization::serialize_parameters, solana_program_runtime::{ - compute_budget::ComputeBudget, ic_msg, invoke_context::ProcessInstructionWithContext, - stable_log, timings::ExecuteTimings, + builtin_program::{BuiltinProgram, BuiltinPrograms, ProcessInstructionWithContext}, + compute_budget::ComputeBudget, + ic_msg, stable_log, + timings::ExecuteTimings, }, solana_runtime::{ accounts_background_service::{AbsRequestSender, SnapshotRequestType}, bank::Bank, bank_forks::BankForks, - builtins::Builtin, commitment::BlockCommitmentCache, epoch_accounts_hash::EpochAccountsHash, genesis_utils::{create_genesis_config_with_leader_ex, GenesisConfigInfo}, @@ -436,7 +437,7 @@ pub fn read_file>(path: P) -> Vec { pub struct ProgramTest { accounts: Vec<(Pubkey, AccountSharedData)>, - builtins: Vec, + builtin_programs: BuiltinPrograms, compute_max_units: Option, prefer_bpf: bool, use_bpf_jit: bool, @@ -474,7 +475,7 @@ impl Default for ProgramTest { Self { accounts: vec![], - builtins: vec![], + builtin_programs: BuiltinPrograms::default(), compute_max_units: None, prefer_bpf, use_bpf_jit: false, @@ -628,12 +629,6 @@ impl ProgramTest { ); }; - let add_native = |this: &mut ProgramTest, process_fn: ProcessInstructionWithContext| { - info!("\"{}\" program loaded as native code", program_name); - this.builtins - .push(Builtin::new(program_name, program_id, process_fn)); - }; - let warn_invalid_program_name = || { let valid_program_names = default_shared_object_dirs() .iter() @@ -679,7 +674,9 @@ impl ProgramTest { // processor function as is. // // TODO: figure out why tests hang if a processor panics when running native code. - (false, _, Some(process)) => add_native(self, process), + (false, _, Some(process)) => { + self.add_builtin_program(program_name, program_id, process) + } // Invalid: `test-sbf` invocation with no matching SBF shared object. (true, None, _) => { @@ -704,8 +701,11 @@ impl ProgramTest { process_instruction: ProcessInstructionWithContext, ) { info!("\"{}\" builtin program", program_name); - self.builtins - .push(Builtin::new(program_name, program_id, process_instruction)); + self.builtin_programs.vec.push(BuiltinProgram { + name: program_name.to_string(), + program_id, + process_instruction, + }); } /// Deactivate a runtime feature. @@ -816,11 +816,11 @@ impl ProgramTest { } // User-supplied additional builtins - for builtin in self.builtins.iter() { + for builtin in self.builtin_programs.vec.iter() { bank.add_builtin( &builtin.name, - &builtin.id, - builtin.process_instruction_with_context, + &builtin.program_id, + builtin.process_instruction, ); } diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 1a0cb7d426..4b547b846f 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -94,9 +94,9 @@ use { solana_perf::perf_libs, solana_program_runtime::{ accounts_data_meter::MAX_ACCOUNTS_DATA_LEN, + builtin_program::{BuiltinProgram, BuiltinPrograms, ProcessInstructionWithContext}, compute_budget::{self, ComputeBudget}, executor_cache::{BankExecutorCache, TransactionExecutorCache, MAX_CACHED_EXECUTORS}, - invoke_context::{BuiltinProgram, ProcessInstructionWithContext}, loaded_programs::{LoadedProgram, LoadedProgramType, LoadedPrograms, WorkingSlot}, log_collector::LogCollector, sysvar_cache::SysvarCache, @@ -887,18 +887,6 @@ impl AbiExample for OptionalDropCallback { } } -#[derive(Debug, Clone, Default)] -pub struct BuiltinPrograms { - pub vec: Vec, -} - -#[cfg(RUSTC_WITH_SPECIALIZATION)] -impl AbiExample for BuiltinPrograms { - fn example() -> Self { - Self::default() - } -} - /// Manager for the state of all accounts and programs after processing its entries. /// AbiExample is needed even without Serialize/Deserialize; actual (de-)serialization /// are implemented elsewhere for versioning @@ -4282,7 +4270,7 @@ impl Bank { let mut process_message_time = Measure::start("process_message_time"); let process_result = MessageProcessor::process_message( - &self.builtin_programs.vec, + &self.builtin_programs, tx.message(), &loaded_transaction.program_indices, &mut transaction_context, @@ -6631,8 +6619,8 @@ impl Bank { for builtin in builtins.genesis_builtins { self.add_builtin( &builtin.name, - &builtin.id, - builtin.process_instruction_with_context, + &builtin.program_id, + builtin.process_instruction, ); } for precompile in get_precompiles() { @@ -7674,6 +7662,7 @@ impl Bank { entry.process_instruction = process_instruction; } else { self.builtin_programs.vec.push(BuiltinProgram { + name: name.to_string(), program_id: *program_id, process_instruction, }); @@ -7944,8 +7933,8 @@ impl Bank { match builtin_action { BuiltinAction::Add(builtin) => self.add_builtin( &builtin.name, - &builtin.id, - builtin.process_instruction_with_context, + &builtin.program_id, + builtin.process_instruction, ), BuiltinAction::Remove(program_id) => self.remove_builtin(&program_id), } diff --git a/runtime/src/builtins.rs b/runtime/src/builtins.rs index 8cc4a7cb92..04ddf2c895 100644 --- a/runtime/src/builtins.rs +++ b/runtime/src/builtins.rs @@ -1,60 +1,12 @@ -#[cfg(RUSTC_WITH_SPECIALIZATION)] use { - solana_frozen_abi::abi_example::AbiExample, solana_program_runtime::declare_process_instruction, -}; -use { - solana_program_runtime::invoke_context::ProcessInstructionWithContext, + solana_program_runtime::builtin_program::BuiltinProgram, solana_sdk::{feature_set, pubkey::Pubkey, stake}, - std::fmt, }; -#[derive(Clone)] -pub struct Builtin { - pub name: String, - pub id: Pubkey, - pub process_instruction_with_context: ProcessInstructionWithContext, -} - -impl Builtin { - pub fn new( - name: &str, - id: Pubkey, - process_instruction_with_context: ProcessInstructionWithContext, - ) -> Self { - Self { - name: name.to_string(), - id, - process_instruction_with_context, - } - } -} - -impl fmt::Debug for Builtin { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Builtin [name={}, id={}]", self.name, self.id) - } -} - -#[cfg(RUSTC_WITH_SPECIALIZATION)] -impl AbiExample for Builtin { - fn example() -> Self { - declare_process_instruction!(process_instruction, 1, |_invoke_context| { - // Do nothing - Ok(()) - }); - - Self { - name: String::default(), - id: Pubkey::default(), - process_instruction_with_context: process_instruction, - } - } -} - #[derive(Clone, Debug)] pub struct Builtins { /// Builtin programs that are always available - pub genesis_builtins: Vec, + pub genesis_builtins: Vec, /// Dynamic feature transitions for builtin programs pub feature_transitions: Vec, @@ -63,7 +15,7 @@ pub struct Builtins { /// Actions taken by a bank when managing the list of active builtin programs. #[derive(Debug, Clone)] pub enum BuiltinAction { - Add(Builtin), + Add(BuiltinProgram), Remove(Pubkey), } @@ -73,13 +25,13 @@ pub enum BuiltinAction { pub enum BuiltinFeatureTransition { /// Add a builtin program if a feature is activated. Add { - builtin: Builtin, + builtin: BuiltinProgram, feature_id: Pubkey, }, /// Remove a builtin program if a feature is activated or /// retain a previously added builtin. RemoveOrRetain { - previously_added_builtin: Builtin, + previously_added_builtin: BuiltinProgram, addition_feature_id: Pubkey, removal_feature_id: Pubkey, }, @@ -107,7 +59,7 @@ impl BuiltinFeatureTransition { removal_feature_id, } => { if should_apply_action_for_feature(removal_feature_id) { - Some(BuiltinAction::Remove(previously_added_builtin.id)) + Some(BuiltinAction::Remove(previously_added_builtin.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())) @@ -119,29 +71,29 @@ impl BuiltinFeatureTransition { } } -/// Builtin programs that are always available -fn genesis_builtins() -> Vec { +/// Built-in programs that are always available +fn genesis_builtins() -> Vec { vec![ - Builtin::new( - "system_program", - solana_system_program::id(), - solana_system_program::system_processor::process_instruction, - ), - Builtin::new( - "vote_program", - solana_vote_program::id(), - solana_vote_program::vote_processor::process_instruction, - ), - Builtin::new( - "stake_program", - stake::program::id(), - solana_stake_program::stake_instruction::process_instruction, - ), - Builtin::new( - "config_program", - solana_config_program::id(), - solana_config_program::config_processor::process_instruction, - ), + 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: 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, + }, ] } @@ -149,27 +101,28 @@ fn genesis_builtins() -> Vec { fn builtin_feature_transitions() -> Vec { vec![ BuiltinFeatureTransition::Add { - builtin: Builtin::new( - "compute_budget_program", - solana_sdk::compute_budget::id(), - solana_compute_budget_program::process_instruction, - ), + builtin: BuiltinProgram { + name: "compute_budget_program".to_string(), + program_id: solana_sdk::compute_budget::id(), + process_instruction: solana_compute_budget_program::process_instruction, + }, feature_id: feature_set::add_compute_budget_program::id(), }, BuiltinFeatureTransition::Add { - builtin: Builtin::new( - "address_lookup_table_program", - solana_address_lookup_table_program::id(), - solana_address_lookup_table_program::processor::process_instruction, - ), + 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, + }, feature_id: feature_set::versioned_tx_message_enabled::id(), }, BuiltinFeatureTransition::Add { - builtin: Builtin::new( - "zk_token_proof_program", - solana_zk_token_sdk::zk_token_proof_program::id(), - solana_zk_token_proof_program::process_instruction, - ), + 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, + }, feature_id: feature_set::zk_token_sdk_enabled::id(), }, ] @@ -187,9 +140,9 @@ pub fn get_pubkeys() -> Vec { let builtins = get(); let mut pubkeys = Vec::new(); - pubkeys.extend(builtins.genesis_builtins.iter().map(|b| b.id)); + pubkeys.extend(builtins.genesis_builtins.iter().map(|b| b.program_id)); pubkeys.extend(builtins.feature_transitions.iter().filter_map(|f| match f { - BuiltinFeatureTransition::Add { builtin, .. } => Some(builtin.id), + BuiltinFeatureTransition::Add { builtin, .. } => Some(builtin.program_id), BuiltinFeatureTransition::RemoveOrRetain { .. } => None, })); pubkeys diff --git a/runtime/src/message_processor.rs b/runtime/src/message_processor.rs index b671dc7181..044314fdf7 100644 --- a/runtime/src/message_processor.rs +++ b/runtime/src/message_processor.rs @@ -2,9 +2,10 @@ use { serde::{Deserialize, Serialize}, solana_measure::measure::Measure, solana_program_runtime::{ + builtin_program::BuiltinPrograms, compute_budget::ComputeBudget, executor_cache::TransactionExecutorCache, - invoke_context::{BuiltinProgram, InvokeContext}, + invoke_context::InvokeContext, log_collector::LogCollector, sysvar_cache::SysvarCache, timings::{ExecuteDetailsTimings, ExecuteTimings}, @@ -51,7 +52,7 @@ 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: &[BuiltinProgram], + builtin_programs: &BuiltinPrograms, message: &SanitizedMessage, program_indices: &[Vec], transaction_context: &mut TransactionContext, @@ -251,10 +252,8 @@ mod tests { let mock_system_program_id = Pubkey::new_unique(); let rent_collector = RentCollector::default(); - let builtin_programs = &[BuiltinProgram { - program_id: mock_system_program_id, - process_instruction, - }]; + let builtin_programs = + BuiltinPrograms::new_mock(mock_system_program_id, process_instruction); let accounts = vec![ ( @@ -303,7 +302,7 @@ mod tests { ))); let sysvar_cache = SysvarCache::default(); let result = MessageProcessor::process_message( - builtin_programs, + &builtin_programs, &message, &program_indices, &mut transaction_context, @@ -353,7 +352,7 @@ mod tests { ]), ))); let result = MessageProcessor::process_message( - builtin_programs, + &builtin_programs, &message, &program_indices, &mut transaction_context, @@ -393,7 +392,7 @@ mod tests { ]), ))); let result = MessageProcessor::process_message( - builtin_programs, + &builtin_programs, &message, &program_indices, &mut transaction_context, @@ -478,10 +477,7 @@ mod tests { let mock_program_id = Pubkey::from([2u8; 32]); let rent_collector = RentCollector::default(); - let builtin_programs = &[BuiltinProgram { - program_id: mock_program_id, - process_instruction, - }]; + let builtin_programs = BuiltinPrograms::new_mock(mock_program_id, process_instruction); let accounts = vec![ ( @@ -527,7 +523,7 @@ mod tests { ))); let sysvar_cache = SysvarCache::default(); let result = MessageProcessor::process_message( - builtin_programs, + &builtin_programs, &message, &program_indices, &mut transaction_context, @@ -561,7 +557,7 @@ mod tests { Some(transaction_context.get_key_of_account_at_index(0).unwrap()), ))); let result = MessageProcessor::process_message( - builtin_programs, + &builtin_programs, &message, &program_indices, &mut transaction_context, @@ -592,7 +588,7 @@ mod tests { Some(transaction_context.get_key_of_account_at_index(0).unwrap()), ))); let result = MessageProcessor::process_message( - builtin_programs, + &builtin_programs, &message, &program_indices, &mut transaction_context, @@ -641,10 +637,7 @@ mod tests { declare_process_instruction!(process_instruction, 1, |_invoke_context| { Err(InstructionError::Custom(0xbabb1e)) }); - let builtin_programs = &[BuiltinProgram { - program_id: mock_program_id, - process_instruction, - }]; + 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); @@ -669,7 +662,7 @@ mod tests { ))); let sysvar_cache = SysvarCache::default(); let result = MessageProcessor::process_message( - builtin_programs, + &builtin_programs, &message, &[vec![0], vec![1]], &mut transaction_context,