Refactoring - Remove redundant definition of `BuiltinProgram` (#31429)

* Replaces BuiltinProgram in the program-runtime with the one from solana_rbpf.

* Adjusts the runtimes built-ins to use Arc<LoadedProgram>.

* Adjusts the tests and benchmarks.
This commit is contained in:
Alexander Meißner 2023-05-02 23:01:28 +02:00 committed by GitHub
parent 9b547fe9f8
commit ae75c7cb3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 285 additions and 213 deletions

View File

@ -1809,7 +1809,7 @@ pub mod tests {
matches::assert_matches, matches::assert_matches,
rand::{thread_rng, Rng}, rand::{thread_rng, Rng},
solana_entry::entry::{create_ticks, next_entry, next_entry_mut}, 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::{ solana_runtime::{
genesis_utils::{ genesis_utils::{
self, create_genesis_config_with_vote_accounts, ValidatorVoteKeypairs, 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 mock_program_id = solana_sdk::pubkey::new_rand();
let mut bank = Bank::new_for_tests(&genesis_config); 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( let tx = Transaction::new_signed_with_payer(
&[Instruction::new_with_bincode( &[Instruction::new_with_bincode(
@ -3012,7 +3015,10 @@ pub mod tests {
(0..get_instruction_errors().len()).for_each(|err| { (0..get_instruction_errors().len()).for_each(|err| {
let mut bank = Bank::new_for_tests(&genesis_config); 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( let tx = Transaction::new_signed_with_payer(
&[Instruction::new_with_bincode( &[Instruction::new_with_bincode(

View File

@ -1,44 +1,28 @@
#[cfg(RUSTC_WITH_SPECIALIZATION)] #[cfg(RUSTC_WITH_SPECIALIZATION)]
use {crate::declare_process_instruction, solana_frozen_abi::abi_example::AbiExample}; use solana_frozen_abi::abi_example::AbiExample;
use { 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, solana_sdk::pubkey::Pubkey,
std::sync::Arc,
}; };
pub type ProcessInstructionWithContext = BuiltInFunction<InvokeContext<'static>>; pub type ProcessInstructionWithContext = BuiltInFunction<InvokeContext<'static>>;
#[derive(Clone)] pub fn create_builtin(
pub struct BuiltinProgram { name: String,
pub name: String, process_instruction: ProcessInstructionWithContext,
pub program_id: Pubkey, ) -> Arc<LoadedProgram> {
pub process_instruction: ProcessInstructionWithContext, let mut program = BuiltInProgram::default();
} program
.register_function_by_name("entrypoint", process_instruction)
impl std::fmt::Debug for BuiltinProgram { .unwrap();
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { Arc::new(LoadedProgram::new_builtin(name, 0, program))
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)] #[derive(Debug, Clone, Default)]
pub struct BuiltinPrograms { pub struct BuiltinPrograms {
pub vec: Vec<BuiltinProgram>, pub vec: Vec<(Pubkey, Arc<LoadedProgram>)>,
} }
#[cfg(RUSTC_WITH_SPECIALIZATION)] #[cfg(RUSTC_WITH_SPECIALIZATION)]
@ -54,11 +38,10 @@ impl BuiltinPrograms {
process_instruction: ProcessInstructionWithContext, process_instruction: ProcessInstructionWithContext,
) -> Self { ) -> Self {
Self { Self {
vec: vec![BuiltinProgram { vec: vec![(
name: "mock instruction processor".to_string(),
program_id, program_id,
process_instruction, create_builtin("mockup".to_string(), process_instruction),
}], )],
} }
} }
} }

View File

@ -5,6 +5,7 @@ use {
compute_budget::ComputeBudget, compute_budget::ComputeBudget,
executor_cache::TransactionExecutorCache, executor_cache::TransactionExecutorCache,
ic_logger_msg, ic_msg, ic_logger_msg, ic_msg,
loaded_programs::LoadedProgramType,
log_collector::LogCollector, log_collector::LogCollector,
pre_account::PreAccount, pre_account::PreAccount,
stable_log, stable_log,
@ -40,7 +41,7 @@ use {
cell::RefCell, cell::RefCell,
fmt::{self, Debug}, fmt::{self, Debug},
rc::Rc, rc::Rc,
sync::Arc, sync::{atomic::Ordering, Arc},
}, },
}; };
@ -719,19 +720,29 @@ impl<'a> InvokeContext<'a> {
}; };
for entry in self.builtin_programs.vec.iter() { 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 = let program_id =
*instruction_context.get_last_program_key(self.transaction_context)?; *instruction_context.get_last_program_key(self.transaction_context)?;
self.transaction_context self.transaction_context
.set_return_data(program_id, Vec::new())?; .set_return_data(program_id, Vec::new())?;
let logger = self.get_log_collector(); let logger = self.get_log_collector();
stable_log::program_invoke(&logger, &program_id, self.get_stack_height()); stable_log::program_invoke(&logger, &program_id, self.get_stack_height());
let pre_remaining_units = self.get_remaining(); let pre_remaining_units = self.get_remaining();
let mock_config = Config::default(); let mock_config = Config::default();
let mut mock_memory_mapping = MemoryMapping::new(Vec::new(), &mock_config).unwrap(); let mut mock_memory_mapping = MemoryMapping::new(Vec::new(), &mock_config).unwrap();
let mut result = ProgramResult::Ok(0); let mut result = ProgramResult::Ok(0);
(entry.process_instruction)( process_instruction(
// Removes lifetime tracking // Removes lifetime tracking
unsafe { std::mem::transmute::<&mut InvokeContext, &mut InvokeContext>(self) }, unsafe { std::mem::transmute::<&mut InvokeContext, &mut InvokeContext>(self) },
0, 0,

View File

@ -72,7 +72,7 @@ pub enum LoadedProgramType {
Typed(VerifiedExecutable<RequisiteVerifier, InvokeContext<'static>>), Typed(VerifiedExecutable<RequisiteVerifier, InvokeContext<'static>>),
#[cfg(test)] #[cfg(test)]
TestLoaded, TestLoaded,
BuiltIn(BuiltInProgram<InvokeContext<'static>>), Builtin(String, BuiltInProgram<InvokeContext<'static>>),
} }
impl Debug for LoadedProgramType { impl Debug for LoadedProgramType {
@ -89,7 +89,9 @@ impl Debug for LoadedProgramType {
LoadedProgramType::Typed(_) => write!(f, "LoadedProgramType::Typed"), LoadedProgramType::Typed(_) => write!(f, "LoadedProgramType::Typed"),
#[cfg(test)] #[cfg(test)]
LoadedProgramType::TestLoaded => write!(f, "LoadedProgramType::TestLoaded"), 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 /// Creates a new built-in program
pub fn new_built_in( pub fn new_builtin(
name: String,
deployment_slot: Slot, deployment_slot: Slot,
program: BuiltInProgram<InvokeContext<'static>>, program: BuiltInProgram<InvokeContext<'static>>,
) -> Self { ) -> Self {
@ -225,7 +228,7 @@ impl LoadedProgram {
effective_slot: deployment_slot.saturating_add(1), effective_slot: deployment_slot.saturating_add(1),
maybe_expiration_slot: None, maybe_expiration_slot: None,
usage_counter: AtomicU64::new(0), 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<dyn Executor> due to rust's coherence issue
// This is safe because LoadedPrograms isn't serializable by definition.
Self::default()
}
}
pub enum LoadedProgramMatchCriteria { pub enum LoadedProgramMatchCriteria {
DeployedOnOrAfterSlot(Slot), DeployedOnOrAfterSlot(Slot),
Closed, Closed,
@ -474,7 +467,7 @@ impl LoadedPrograms {
LoadedProgramType::FailedVerification LoadedProgramType::FailedVerification
| LoadedProgramType::Closed | LoadedProgramType::Closed
| LoadedProgramType::DelayVisibility | LoadedProgramType::DelayVisibility
| LoadedProgramType::BuiltIn(_) => None, | LoadedProgramType::Builtin(_, _) => None,
}) })
}) })
.sorted_by_cached_key(|(order, (_id, program))| { .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)] #[cfg(test)]
mod tests { mod tests {
use { use {
@ -582,7 +591,7 @@ mod tests {
fn new_test_builtin_program(deployment_slot: Slot, effective_slot: Slot) -> Arc<LoadedProgram> { fn new_test_builtin_program(deployment_slot: Slot, effective_slot: Slot) -> Arc<LoadedProgram> {
Arc::new(LoadedProgram { Arc::new(LoadedProgram {
program: LoadedProgramType::BuiltIn(BuiltInProgram::default()), program: LoadedProgramType::Builtin("mockup".to_string(), BuiltInProgram::default()),
account_size: 0, account_size: 0,
deployment_slot, deployment_slot,
effective_slot, effective_slot,

View File

@ -11,7 +11,7 @@ use {
solana_banks_server::banks_server::start_local_server, solana_banks_server::banks_server::start_local_server,
solana_bpf_loader_program::serialization::serialize_parameters, solana_bpf_loader_program::serialization::serialize_parameters,
solana_program_runtime::{ solana_program_runtime::{
builtin_program::{BuiltinProgram, BuiltinPrograms, ProcessInstructionWithContext}, builtin_program::{create_builtin, BuiltinPrograms, ProcessInstructionWithContext},
compute_budget::ComputeBudget, compute_budget::ComputeBudget,
ic_msg, stable_log, ic_msg, stable_log,
timings::ExecuteTimings, timings::ExecuteTimings,
@ -692,11 +692,10 @@ impl ProgramTest {
process_instruction: ProcessInstructionWithContext, process_instruction: ProcessInstructionWithContext,
) { ) {
info!("\"{}\" builtin program", program_name); info!("\"{}\" builtin program", program_name);
self.builtin_programs.vec.push(BuiltinProgram { self.builtin_programs.vec.push((
name: program_name.to_string(),
program_id, program_id,
process_instruction, create_builtin(program_name.to_string(), process_instruction),
}); ));
} }
/// Deactivate a runtime feature. /// Deactivate a runtime feature.
@ -791,12 +790,8 @@ impl ProgramTest {
} }
// User-supplied additional builtins // User-supplied additional builtins
for builtin in self.builtin_programs.vec.iter() { for (program_id, builtin) in self.builtin_programs.vec.iter() {
bank.add_builtin( bank.add_builtin(*program_id, builtin.clone());
&builtin.name,
&builtin.program_id,
builtin.process_instruction,
);
} }
for (address, account) in self.accounts.iter() { for (address, account) in self.accounts.iter() {

View File

@ -5,7 +5,7 @@ extern crate test;
use { use {
log::*, log::*,
solana_program_runtime::declare_process_instruction, solana_program_runtime::{builtin_program::create_builtin, declare_process_instruction},
solana_runtime::{ solana_runtime::{
bank::{test_utils::goto_end_of_slot, *}, bank::{test_utils::goto_end_of_slot, *},
bank_client::BankClient, bank_client::BankClient,
@ -132,9 +132,8 @@ fn do_bench_transactions(
let mut bank = Bank::new_from_parent(&Arc::new(bank), &Pubkey::default(), 1); let mut bank = Bank::new_from_parent(&Arc::new(bank), &Pubkey::default(), 1);
bank.add_builtin( bank.add_builtin(
"builtin_program", Pubkey::from(BUILTIN_PROGRAM_ID),
&Pubkey::from(BUILTIN_PROGRAM_ID), create_builtin("mockup".to_string(), process_instruction),
process_instruction,
); );
bank.add_builtin_account("solana_noop_program", &Pubkey::from(NOOP_PROGRAM_ID), false); bank.add_builtin_account("solana_noop_program", &Pubkey::from(NOOP_PROGRAM_ID), false);
let bank = Arc::new(bank); let bank = Arc::new(bank);

View File

@ -94,7 +94,7 @@ use {
solana_perf::perf_libs, solana_perf::perf_libs,
solana_program_runtime::{ solana_program_runtime::{
accounts_data_meter::MAX_ACCOUNTS_DATA_LEN, accounts_data_meter::MAX_ACCOUNTS_DATA_LEN,
builtin_program::{BuiltinProgram, BuiltinPrograms, ProcessInstructionWithContext}, builtin_program::BuiltinPrograms,
compute_budget::{self, ComputeBudget}, compute_budget::{self, ComputeBudget},
executor_cache::{BankExecutorCache, TransactionExecutorCache, MAX_CACHED_EXECUTORS}, executor_cache::{BankExecutorCache, TransactionExecutorCache, MAX_CACHED_EXECUTORS},
loaded_programs::{ loaded_programs::{
@ -6350,12 +6350,8 @@ impl Bank {
.extend_from_slice(&additional_builtins.feature_transitions); .extend_from_slice(&additional_builtins.feature_transitions);
} }
if !debug_do_not_add_builtins { if !debug_do_not_add_builtins {
for builtin in builtins.genesis_builtins { for (program_id, builtin) in builtins.genesis_builtins {
self.add_builtin( self.add_builtin(program_id, builtin);
&builtin.name,
&builtin.program_id,
builtin.process_instruction,
);
} }
for precompile in get_precompiles() { for precompile in get_precompiles() {
if precompile.feature.is_none() { if precompile.feature.is_none() {
@ -7379,27 +7375,24 @@ impl Bank {
} }
/// Add an instruction processor to intercept instructions before the dynamic loader. /// Add an instruction processor to intercept instructions before the dynamic loader.
pub fn add_builtin( pub fn add_builtin(&mut self, program_id: Pubkey, builtin: Arc<LoadedProgram>) {
&mut self, let name = match &builtin.program {
name: &str, LoadedProgramType::Builtin(name, _) => name,
program_id: &Pubkey, _ => unreachable!(),
process_instruction: ProcessInstructionWithContext, };
) {
debug!("Adding program {} under {:?}", name, program_id); 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 if let Some(entry) = self
.builtin_programs .builtin_programs
.vec .vec
.iter_mut() .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 { } else {
self.builtin_programs.vec.push(BuiltinProgram { self.builtin_programs
name: name.to_string(), .vec
program_id: *program_id, .push((program_id, builtin.clone()));
process_instruction,
});
} }
debug!("Added program {} under {:?}", name, program_id); debug!("Added program {} under {:?}", name, program_id);
} }
@ -7413,7 +7406,7 @@ impl Bank {
.builtin_programs .builtin_programs
.vec .vec
.iter() .iter()
.position(|entry| entry.program_id == *program_id) .position(|entry| entry.0 == *program_id)
{ {
self.builtin_programs.vec.remove(position); self.builtin_programs.vec.remove(position);
} }
@ -7665,11 +7658,9 @@ impl Bank {
transition.to_action(&should_apply_action_for_feature_transition) transition.to_action(&should_apply_action_for_feature_transition)
{ {
match builtin_action { match builtin_action {
BuiltinAction::Add(builtin) => self.add_builtin( BuiltinAction::Add(program_id, builtin) => {
&builtin.name, self.add_builtin(program_id, builtin)
&builtin.program_id, }
builtin.process_instruction,
),
BuiltinAction::Remove(program_id) => self.remove_builtin(&program_id), BuiltinAction::Remove(program_id) => self.remove_builtin(&program_id),
} }
} }

View File

@ -33,6 +33,7 @@ use {
serde::{Deserialize, Serialize}, serde::{Deserialize, Serialize},
solana_logger, solana_logger,
solana_program_runtime::{ solana_program_runtime::{
builtin_program::create_builtin,
compute_budget::{self, ComputeBudget, MAX_COMPUTE_UNIT_LIMIT}, compute_budget::{self, ComputeBudget, MAX_COMPUTE_UNIT_LIMIT},
declare_process_instruction, declare_process_instruction,
executor_cache::TransactionExecutorCache, executor_cache::TransactionExecutorCache,
@ -1380,7 +1381,10 @@ fn test_rent_complex() {
root_bank.restore_old_behavior_for_fragile_tests(); root_bank.restore_old_behavior_for_fragile_tests();
let root_bank = Arc::new(root_bank); let root_bank = Arc::new(root_bank);
let mut bank = create_child_bank_for_rent_test(&root_bank, &genesis_config); 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()); 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()); assert!(bank.get_account(&mock_vote_program_id()).is_none());
bank.add_builtin( bank.add_builtin(
"mock_vote_program", mock_vote_program_id(),
&mock_vote_program_id(), create_builtin("mockup".to_string(), process_instruction),
process_instruction,
); );
assert!(bank.get_account(&mock_vote_program_id()).is_some()); 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(); let vote_loader_account = bank.get_account(&solana_vote_program::id()).unwrap();
bank.add_builtin( bank.add_builtin(
"solana_vote_program", solana_vote_program::id(),
&solana_vote_program::id(), create_builtin("mockup".to_string(), process_instruction),
process_instruction,
); );
let new_vote_loader_account = bank.get_account(&solana_vote_program::id()).unwrap(); 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. // 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; continue;
} }
bank.add_builtin("mock_program1", &vote_id, process_instruction); bank.add_builtin(
bank.add_builtin("mock_program2", &stake_id, process_instruction); 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(); let stakes = bank.stakes_cache.stakes();
assert!(stakes.vote_accounts().as_ref().is_empty()); 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 // Re-adding builtin programs should be no-op
bank.update_accounts_hash_for_tests(); bank.update_accounts_hash_for_tests();
let old_hash = bank.get_accounts_hash().unwrap(); let old_hash = bank.get_accounts_hash().unwrap();
bank.add_builtin("mock_program1", &vote_id, process_instruction); bank.add_builtin(
bank.add_builtin("mock_program2", &stake_id, process_instruction); 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); add_root_and_flush_write_cache(&bank);
bank.update_accounts_hash_for_tests(); bank.update_accounts_hash_for_tests();
let new_hash = bank.get_accounts_hash().unwrap(); 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]); 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 from_pubkey = solana_sdk::pubkey::new_rand();
let to_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 mut bank = Bank::new_for_tests(&genesis_config);
let mock_program_id = Pubkey::from([2u8; 32]); 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 from_pubkey = solana_sdk::pubkey::new_rand();
let to_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()); 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); let result = bank.process_transaction(&tx);
assert_eq!(result, Ok(())); assert_eq!(result, Ok(()));
let account = bank.get_account(&solana_vote_program::id()).unwrap(); let account = bank.get_account(&solana_vote_program::id()).unwrap();
@ -6362,7 +6385,10 @@ fn test_duplicate_account_key() {
AccountMeta::new(to_pubkey, false), 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 instruction = Instruction::new_with_bincode(solana_vote_program::id(), &10, account_metas);
let mut tx = Transaction::new_signed_with_payer( 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), 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 instruction = Instruction::new_with_bincode(solana_vote_program::id(), &10, account_metas);
let mut tx = Transaction::new_signed_with_payer( let mut tx = Transaction::new_signed_with_payer(
@ -6424,7 +6453,10 @@ fn test_program_id_as_payer() {
AccountMeta::new(to_pubkey, false), 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 instruction = Instruction::new_with_bincode(solana_vote_program::id(), &10, account_metas);
let mut tx = Transaction::new_signed_with_payer( 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), 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 instruction = Instruction::new_with_bincode(solana_vote_program::id(), &10, account_metas);
let mut tx = Transaction::new_signed_with_payer( let mut tx = Transaction::new_signed_with_payer(
@ -6498,7 +6533,7 @@ fn test_fuzz_instructions() {
.map(|i| { .map(|i| {
let key = solana_sdk::pubkey::new_rand(); let key = solana_sdk::pubkey::new_rand();
let name = format!("program{i:?}"); 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()) (key, name.as_bytes().to_vec())
}) })
.collect(); .collect();
@ -6704,7 +6739,10 @@ fn test_same_program_id_uses_unqiue_executable_accounts() {
// Add a new program // Add a new program
let program1_pubkey = solana_sdk::pubkey::new_rand(); 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 // Add a new program owned by the first
let program2_pubkey = solana_sdk::pubkey::new_rand(); 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); assert_eq!(bank.get_account_modified_slot(&program_id), None);
Arc::get_mut(&mut bank) Arc::get_mut(&mut bank).unwrap().add_builtin(
.unwrap() program_id,
.add_builtin("mock_program", &program_id, process_instruction); create_builtin("mockup".to_string(), process_instruction),
);
assert_eq!(bank.get_account_modified_slot(&program_id).unwrap().1, slot); assert_eq!(bank.get_account_modified_slot(&program_id).unwrap().1, slot);
let mut bank = Arc::new(new_from_parent(&bank)); let mut bank = Arc::new(new_from_parent(&bank));
Arc::get_mut(&mut bank) Arc::get_mut(&mut bank).unwrap().add_builtin(
.unwrap() program_id,
.add_builtin("mock_program", &program_id, process_instruction); create_builtin("mockup".to_string(), process_instruction),
);
assert_eq!(bank.get_account_modified_slot(&program_id).unwrap().1, slot); 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); assert_eq!(bank.get_account_modified_slot(&loader_id), None);
Arc::get_mut(&mut bank) Arc::get_mut(&mut bank).unwrap().add_builtin(
.unwrap() loader_id,
.add_builtin("mock_program", &loader_id, process_instruction); create_builtin("mockup".to_string(), process_instruction),
);
assert_eq!(bank.get_account_modified_slot(&loader_id).unwrap().1, slot); assert_eq!(bank.get_account_modified_slot(&loader_id).unwrap().1, slot);
let mut bank = Arc::new(new_from_parent(&bank)); let mut bank = Arc::new(new_from_parent(&bank));
Arc::get_mut(&mut bank) Arc::get_mut(&mut bank).unwrap().add_builtin(
.unwrap() loader_id,
.add_builtin("mock_program", &loader_id, process_instruction); create_builtin("mockup".to_string(), process_instruction),
);
assert_eq!(bank.get_account_modified_slot(&loader_id).unwrap().1, slot); 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 mock_program_id = Pubkey::from([2u8; 32]);
let blockhash = bank.last_blockhash(); 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 [ for index in [
None, None,
@ -9883,7 +9928,10 @@ fn test_transfer_sysvar() {
}); });
let program_id = solana_sdk::pubkey::new_rand(); 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(); let blockhash = bank.last_blockhash();
#[allow(deprecated)] #[allow(deprecated)]
@ -10095,7 +10143,10 @@ fn test_compute_budget_program_noop() {
Ok(()) Ok(())
}); });
let program_id = solana_sdk::pubkey::new_rand(); 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( let message = Message::new(
&[ &[
@ -10138,7 +10189,10 @@ fn test_compute_request_instruction() {
Ok(()) Ok(())
}); });
let program_id = solana_sdk::pubkey::new_rand(); 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( let message = Message::new(
&[ &[
@ -10188,7 +10242,10 @@ fn test_failed_compute_request_instruction() {
Ok(()) Ok(())
}); });
let program_id = solana_sdk::pubkey::new_rand(); 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 // This message will not be executed because the compute budget request is invalid
let message0 = Message::new( 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); let mut bank = Bank::new_for_tests(&genesis_config);
bank.add_builtin( bank.add_builtin(
"mock_program", mock_program_id,
&mock_program_id, create_builtin("mockup".to_string(), mock_transfer_process_instruction),
mock_transfer_process_instruction,
); );
let recent_blockhash = bank.last_blockhash(); 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); let mut bank = Bank::new_for_tests(&genesis_config);
bank.add_builtin( bank.add_builtin(
"mock_program", mock_program_id,
&mock_program_id, create_builtin("mockup".to_string(), mock_transfer_process_instruction),
mock_transfer_process_instruction,
); );
let recent_blockhash = bank.last_blockhash(); let recent_blockhash = bank.last_blockhash();
@ -11013,9 +11068,8 @@ fn test_drained_created_account() {
let mut bank = Bank::new_for_tests(&genesis_config); let mut bank = Bank::new_for_tests(&genesis_config);
bank.add_builtin( bank.add_builtin(
"mock_program", mock_program_id,
&mock_program_id, create_builtin("mockup".to_string(), mock_transfer_process_instruction),
mock_transfer_process_instruction,
); );
let recent_blockhash = bank.last_blockhash(); let recent_blockhash = bank.last_blockhash();
@ -11668,9 +11722,8 @@ fn test_resize_and_rent() {
let mock_program_id = Pubkey::new_unique(); let mock_program_id = Pubkey::new_unique();
bank.add_builtin( bank.add_builtin(
"mock_realloc_program", mock_program_id,
&mock_program_id, create_builtin("mockup".to_string(), mock_realloc_process_instruction),
mock_realloc_process_instruction,
); );
let recent_blockhash = bank.last_blockhash(); 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 mut bank = Bank::new_for_tests(&genesis_config);
let mock_program_id = Pubkey::new_unique(); let mock_program_id = Pubkey::new_unique();
bank.add_builtin( bank.add_builtin(
"mock_realloc_program", mock_program_id,
&mock_program_id, create_builtin("mockup".to_string(), mock_realloc_process_instruction),
mock_realloc_process_instruction,
); );
let recent_blockhash = bank.last_blockhash(); let recent_blockhash = bank.last_blockhash();

View File

@ -1,14 +1,15 @@
use { use {
solana_program_runtime::builtin_program::BuiltinProgram, solana_program_runtime::{builtin_program::create_builtin, loaded_programs::LoadedProgram},
solana_sdk::{ solana_sdk::{
bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, feature_set, pubkey::Pubkey, bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, feature_set, pubkey::Pubkey,
}, },
std::sync::Arc,
}; };
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Builtins { pub struct Builtins {
/// Builtin programs that are always available /// Builtin programs that are always available
pub genesis_builtins: Vec<BuiltinProgram>, pub genesis_builtins: Vec<(Pubkey, Arc<LoadedProgram>)>,
/// Dynamic feature transitions for builtin programs /// Dynamic feature transitions for builtin programs
pub feature_transitions: Vec<BuiltinFeatureTransition>, pub feature_transitions: Vec<BuiltinFeatureTransition>,
@ -17,7 +18,7 @@ pub struct Builtins {
/// Actions taken by a bank when managing the list of active builtin programs. /// Actions taken by a bank when managing the list of active builtin programs.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum BuiltinAction { pub enum BuiltinAction {
Add(BuiltinProgram), Add(Pubkey, Arc<LoadedProgram>),
Remove(Pubkey), Remove(Pubkey),
} }
@ -27,13 +28,15 @@ pub enum BuiltinAction {
pub enum BuiltinFeatureTransition { pub enum BuiltinFeatureTransition {
/// Add a builtin program if a feature is activated. /// Add a builtin program if a feature is activated.
Add { Add {
builtin: BuiltinProgram, program_id: Pubkey,
builtin: Arc<LoadedProgram>,
feature_id: Pubkey, feature_id: Pubkey,
}, },
/// Remove a builtin program if a feature is activated or /// Remove a builtin program if a feature is activated or
/// retain a previously added builtin. /// retain a previously added builtin.
RemoveOrRetain { RemoveOrRetain {
previously_added_builtin: BuiltinProgram, program_id: Pubkey,
previously_added_builtin: Arc<LoadedProgram>,
addition_feature_id: Pubkey, addition_feature_id: Pubkey,
removal_feature_id: Pubkey, removal_feature_id: Pubkey,
}, },
@ -46,25 +49,30 @@ impl BuiltinFeatureTransition {
) -> Option<BuiltinAction> { ) -> Option<BuiltinAction> {
match self { match self {
Self::Add { Self::Add {
program_id,
builtin, builtin,
feature_id, feature_id,
} => { } => {
if should_apply_action_for_feature(feature_id) { if should_apply_action_for_feature(feature_id) {
Some(BuiltinAction::Add(builtin.clone())) Some(BuiltinAction::Add(*program_id, builtin.clone()))
} else { } else {
None None
} }
} }
Self::RemoveOrRetain { Self::RemoveOrRetain {
program_id,
previously_added_builtin, previously_added_builtin,
addition_feature_id, addition_feature_id,
removal_feature_id, removal_feature_id,
} => { } => {
if should_apply_action_for_feature(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) { } else if should_apply_action_for_feature(addition_feature_id) {
// Retaining is no different from adding a new builtin. // 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 { } else {
None None
} }
@ -74,43 +82,57 @@ impl BuiltinFeatureTransition {
} }
/// Built-in programs that are always available /// Built-in programs that are always available
fn genesis_builtins() -> Vec<BuiltinProgram> { fn genesis_builtins() -> Vec<(Pubkey, Arc<LoadedProgram>)> {
vec![ vec![
BuiltinProgram { (
name: "system_program".to_string(), solana_system_program::id(),
program_id: solana_system_program::id(), create_builtin(
process_instruction: solana_system_program::system_processor::process_instruction, "system_program".to_string(),
}, 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, solana_vote_program::id(),
}, create_builtin(
BuiltinProgram { "vote_program".to_string(),
name: "stake_program".to_string(), solana_vote_program::vote_processor::process_instruction,
program_id: solana_stake_program::id(), ),
process_instruction: solana_stake_program::stake_instruction::process_instruction, ),
}, (
BuiltinProgram { solana_stake_program::id(),
name: "config_program".to_string(), create_builtin(
program_id: solana_config_program::id(), "stake_program".to_string(),
process_instruction: solana_config_program::config_processor::process_instruction, solana_stake_program::stake_instruction::process_instruction,
}, ),
BuiltinProgram { ),
name: "solana_bpf_loader_deprecated_program".to_string(), (
program_id: bpf_loader_deprecated::id(), solana_config_program::id(),
process_instruction: solana_bpf_loader_program::process_instruction, create_builtin(
}, "config_program".to_string(),
BuiltinProgram { solana_config_program::config_processor::process_instruction,
name: "solana_bpf_loader_program".to_string(), ),
program_id: bpf_loader::id(), ),
process_instruction: solana_bpf_loader_program::process_instruction, (
}, bpf_loader_deprecated::id(),
BuiltinProgram { create_builtin(
name: "solana_bpf_loader_upgradeable_program".to_string(), "solana_bpf_loader_deprecated_program".to_string(),
program_id: bpf_loader_upgradeable::id(), solana_bpf_loader_program::process_instruction,
process_instruction: 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<BuiltinProgram> {
fn builtin_feature_transitions() -> Vec<BuiltinFeatureTransition> { fn builtin_feature_transitions() -> Vec<BuiltinFeatureTransition> {
vec![ vec![
BuiltinFeatureTransition::Add { BuiltinFeatureTransition::Add {
builtin: BuiltinProgram { program_id: solana_sdk::compute_budget::id(),
name: "compute_budget_program".to_string(), builtin: create_builtin(
program_id: solana_sdk::compute_budget::id(), "compute_budget_program".to_string(),
process_instruction: solana_compute_budget_program::process_instruction, solana_compute_budget_program::process_instruction,
}, ),
feature_id: feature_set::add_compute_budget_program::id(), feature_id: feature_set::add_compute_budget_program::id(),
}, },
BuiltinFeatureTransition::Add { BuiltinFeatureTransition::Add {
builtin: BuiltinProgram { program_id: solana_address_lookup_table_program::id(),
name: "address_lookup_table_program".to_string(), builtin: create_builtin(
program_id: solana_address_lookup_table_program::id(), "address_lookup_table_program".to_string(),
process_instruction: solana_address_lookup_table_program::processor::process_instruction,
solana_address_lookup_table_program::processor::process_instruction, ),
},
feature_id: feature_set::versioned_tx_message_enabled::id(), feature_id: feature_set::versioned_tx_message_enabled::id(),
}, },
BuiltinFeatureTransition::Add { BuiltinFeatureTransition::Add {
builtin: BuiltinProgram { program_id: solana_zk_token_sdk::zk_token_proof_program::id(),
name: "zk_token_proof_program".to_string(), builtin: create_builtin(
program_id: solana_zk_token_sdk::zk_token_proof_program::id(), "zk_token_proof_program".to_string(),
process_instruction: solana_zk_token_proof_program::process_instruction, solana_zk_token_proof_program::process_instruction,
}, ),
feature_id: feature_set::zk_token_sdk_enabled::id(), feature_id: feature_set::zk_token_sdk_enabled::id(),
}, },
] ]
@ -157,9 +178,14 @@ pub fn get_pubkeys() -> Vec<Pubkey> {
let builtins = get(); let builtins = get();
let mut pubkeys = Vec::new(); 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 { 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, BuiltinFeatureTransition::RemoveOrRetain { .. } => None,
})); }));
pubkeys pubkeys