From ac36dbbc8793455c52df77f37af9ea9286c51fa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Wed, 17 Apr 2024 17:48:14 +0200 Subject: [PATCH] Refactor - `LoadedProgramType::Loaded` and `LoadedProgramOwner` (#606) * Adds LoadedProgram::account_owner() and LoadedProgramOwner. Merges LoadedProgramTypes LegacyV0, LegacyV1, Typed and TestLoaded into Loaded. * Review feedback. --- ledger-tool/src/program.rs | 2 +- program-runtime/src/loaded_programs.rs | 229 +++++++++++------- programs/bpf_loader/src/lib.rs | 9 +- programs/loader-v4/src/lib.rs | 2 +- runtime/src/bank.rs | 9 +- .../bank/builtins/core_bpf_migration/mod.rs | 2 +- runtime/src/bank/tests.rs | 4 +- svm/src/transaction_processor.rs | 80 ++++-- 8 files changed, 214 insertions(+), 123 deletions(-) diff --git a/ledger-tool/src/program.rs b/ledger-tool/src/program.rs index 7a5f5cc64..1dd64118c 100644 --- a/ledger-tool/src/program.rs +++ b/ledger-tool/src/program.rs @@ -344,7 +344,7 @@ fn load_program<'a>( ); match result { Ok(loaded_program) => match loaded_program.program { - LoadedProgramType::LegacyV1(program) => Ok(program), + LoadedProgramType::Loaded(program) => Ok(program), _ => unreachable!(), }, Err(err) => Err(format!("Loading executable failed: {err:?}")), diff --git a/program-runtime/src/loaded_programs.rs b/program-runtime/src/loaded_programs.rs index 7807ef447..75bce1840 100644 --- a/program-runtime/src/loaded_programs.rs +++ b/program-runtime/src/loaded_programs.rs @@ -16,7 +16,7 @@ use { solana_sdk::{ bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, clock::{Epoch, Slot}, - loader_v4, + loader_v4, native_loader, pubkey::Pubkey, saturating_add_assign, }, @@ -60,6 +60,48 @@ pub trait ForkGraph { } } +/// The owner of a programs accounts, thus the loader of a program +#[derive(Default, Clone, Copy, PartialEq, Eq, Debug)] +pub enum LoadedProgramOwner { + #[default] + NativeLoader, + LoaderV1, + LoaderV2, + LoaderV3, + LoaderV4, +} + +impl TryFrom<&Pubkey> for LoadedProgramOwner { + type Error = (); + fn try_from(loader_key: &Pubkey) -> Result { + if native_loader::check_id(loader_key) { + Ok(LoadedProgramOwner::NativeLoader) + } else if bpf_loader_deprecated::check_id(loader_key) { + Ok(LoadedProgramOwner::LoaderV1) + } else if bpf_loader::check_id(loader_key) { + Ok(LoadedProgramOwner::LoaderV2) + } else if bpf_loader_upgradeable::check_id(loader_key) { + Ok(LoadedProgramOwner::LoaderV3) + } else if loader_v4::check_id(loader_key) { + Ok(LoadedProgramOwner::LoaderV4) + } else { + Err(()) + } + } +} + +impl From for Pubkey { + fn from(loaded_program_owner: LoadedProgramOwner) -> Self { + match loaded_program_owner { + LoadedProgramOwner::NativeLoader => native_loader::id(), + LoadedProgramOwner::LoaderV1 => bpf_loader_deprecated::id(), + LoadedProgramOwner::LoaderV2 => bpf_loader::id(), + LoadedProgramOwner::LoaderV3 => bpf_loader_upgradeable::id(), + LoadedProgramOwner::LoaderV4 => loader_v4::id(), + } + } +} + /// Actual payload of [LoadedProgram]. #[derive(Default)] pub enum LoadedProgramType { @@ -76,14 +118,8 @@ pub enum LoadedProgramType { /// /// It continues to track usage statistics even when the compiled executable of the program is evicted from memory. Unloaded(ProgramRuntimeEnvironment), - /// Verified and compiled program of loader-v1 or loader-v2 - LegacyV0(Executable>), - /// Verified and compiled program of loader-v3 (aka upgradable loader) - LegacyV1(Executable>), - /// Verified and compiled program of loader-v4 - Typed(Executable>), - #[cfg(test)] - TestLoaded(ProgramRuntimeEnvironment), + /// Verified and compiled program + Loaded(Executable>), /// A built-in program which is not stored on-chain but backed into and distributed with the validator Builtin(BuiltinProgram>), } @@ -97,11 +133,7 @@ impl Debug for LoadedProgramType { LoadedProgramType::Closed => write!(f, "LoadedProgramType::Closed"), LoadedProgramType::DelayVisibility => write!(f, "LoadedProgramType::DelayVisibility"), LoadedProgramType::Unloaded(_) => write!(f, "LoadedProgramType::Unloaded"), - LoadedProgramType::LegacyV0(_) => write!(f, "LoadedProgramType::LegacyV0"), - LoadedProgramType::LegacyV1(_) => write!(f, "LoadedProgramType::LegacyV1"), - LoadedProgramType::Typed(_) => write!(f, "LoadedProgramType::Typed"), - #[cfg(test)] - LoadedProgramType::TestLoaded(_) => write!(f, "LoadedProgramType::TestLoaded"), + LoadedProgramType::Loaded(_) => write!(f, "LoadedProgramType::Loaded"), LoadedProgramType::Builtin(_) => write!(f, "LoadedProgramType::Builtin"), } } @@ -111,14 +143,10 @@ impl LoadedProgramType { /// Returns a reference to its environment if it has one pub fn get_environment(&self) -> Option<&ProgramRuntimeEnvironment> { match self { - LoadedProgramType::LegacyV0(program) - | LoadedProgramType::LegacyV1(program) - | LoadedProgramType::Typed(program) => Some(program.get_loader()), + LoadedProgramType::Loaded(program) => Some(program.get_loader()), LoadedProgramType::FailedVerification(env) | LoadedProgramType::Unloaded(env) => { Some(env) } - #[cfg(test)] - LoadedProgramType::TestLoaded(environment) => Some(environment), _ => None, } } @@ -131,6 +159,8 @@ impl LoadedProgramType { pub struct LoadedProgram { /// The program of this entry pub program: LoadedProgramType, + /// The loader of this entry + pub account_owner: LoadedProgramOwner, /// Size of account that stores the program and program data pub account_size: usize, /// Slot in which the program was (re)deployed @@ -355,22 +385,13 @@ impl LoadedProgram { metrics.jit_compile_us = jit_compile_time.end_as_us(); } - let program = if bpf_loader_deprecated::check_id(loader_key) { - LoadedProgramType::LegacyV0(executable) - } else if bpf_loader::check_id(loader_key) || bpf_loader_upgradeable::check_id(loader_key) { - LoadedProgramType::LegacyV1(executable) - } else if loader_v4::check_id(loader_key) { - LoadedProgramType::Typed(executable) - } else { - panic!(); - }; - Ok(Self { deployment_slot, + account_owner: LoadedProgramOwner::try_from(loader_key).unwrap(), account_size, effective_slot, tx_usage_counter: AtomicU64::new(0), - program, + program: LoadedProgramType::Loaded(executable), ix_usage_counter: AtomicU64::new(0), latest_access_slot: AtomicU64::new(0), }) @@ -378,11 +399,7 @@ impl LoadedProgram { pub fn to_unloaded(&self) -> Option { match &self.program { - LoadedProgramType::LegacyV0(_) - | LoadedProgramType::LegacyV1(_) - | LoadedProgramType::Typed(_) => {} - #[cfg(test)] - LoadedProgramType::TestLoaded(_) => {} + LoadedProgramType::Loaded(_) => {} LoadedProgramType::FailedVerification(_) | LoadedProgramType::Closed | LoadedProgramType::DelayVisibility @@ -393,6 +410,7 @@ impl LoadedProgram { } Some(Self { program: LoadedProgramType::Unloaded(self.program.get_environment()?.clone()), + account_owner: self.account_owner, account_size: self.account_size, deployment_slot: self.deployment_slot, effective_slot: self.effective_slot, @@ -414,6 +432,7 @@ impl LoadedProgram { .unwrap(); Self { deployment_slot, + account_owner: LoadedProgramOwner::NativeLoader, account_size, effective_slot: deployment_slot, tx_usage_counter: AtomicU64::new(0), @@ -423,9 +442,14 @@ impl LoadedProgram { } } - pub fn new_tombstone(slot: Slot, reason: LoadedProgramType) -> Self { + pub fn new_tombstone( + slot: Slot, + account_owner: LoadedProgramOwner, + reason: LoadedProgramType, + ) -> Self { let tombstone = Self { program: reason, + account_owner, account_size: 0, deployment_slot: slot, effective_slot: slot, @@ -464,6 +488,10 @@ impl LoadedProgram { let decaying_for = std::cmp::min(63, now.saturating_sub(last_access)); self.tx_usage_counter.load(Ordering::Relaxed) >> decaying_for } + + pub fn account_owner(&self) -> Pubkey { + self.account_owner.into() + } } /// Globally shared RBPF config and syscall registry @@ -691,6 +719,7 @@ impl LoadedProgramsForTxBatch { // the tombstone to reflect that. Arc::new(LoadedProgram::new_tombstone( entry.deployment_slot, + entry.account_owner, LoadedProgramType::DelayVisibility, )) } else { @@ -777,11 +806,7 @@ impl ProgramCache { let existing = slot_versions.get_mut(index).unwrap(); match (&existing.program, &entry.program) { (LoadedProgramType::Builtin(_), LoadedProgramType::Builtin(_)) - | (LoadedProgramType::Unloaded(_), LoadedProgramType::LegacyV0(_)) - | (LoadedProgramType::Unloaded(_), LoadedProgramType::LegacyV1(_)) - | (LoadedProgramType::Unloaded(_), LoadedProgramType::Typed(_)) => {} - #[cfg(test)] - (LoadedProgramType::Unloaded(_), LoadedProgramType::TestLoaded(_)) => {} + | (LoadedProgramType::Unloaded(_), LoadedProgramType::Loaded(_)) => {} _ => { // Something is wrong, I can feel it ... error!("ProgramCache::assign_program() failed key={:?} existing={:?} entry={:?}", key, slot_versions, entry); @@ -967,6 +992,7 @@ impl ProgramCache { // the tombstone to reflect that. Arc::new(LoadedProgram::new_tombstone( entry.deployment_slot, + entry.account_owner, LoadedProgramType::DelayVisibility, )) } else { @@ -1061,16 +1087,17 @@ impl ProgramCache { .slot_versions .iter() .filter_map(move |program| match program.program { - LoadedProgramType::LegacyV0(_) | LoadedProgramType::LegacyV1(_) - if include_program_runtime_v1 => - { - Some((*id, program.clone())) + LoadedProgramType::Loaded(_) => { + if (program.account_owner != LoadedProgramOwner::LoaderV4 + && include_program_runtime_v1) + || (program.account_owner == LoadedProgramOwner::LoaderV4 + && include_program_runtime_v2) + { + Some((*id, program.clone())) + } else { + None + } } - LoadedProgramType::Typed(_) if include_program_runtime_v2 => { - Some((*id, program.clone())) - } - #[cfg(test)] - LoadedProgramType::TestLoaded(_) => Some((*id, program.clone())), _ => None, }) }) @@ -1207,15 +1234,17 @@ impl solana_frozen_abi::abi_example::AbiExample for ProgramCache< mod tests { use { crate::loaded_programs::{ - BlockRelation, ForkGraph, LoadedProgram, LoadedProgramMatchCriteria, LoadedProgramType, - LoadedProgramsForTxBatch, ProgramCache, ProgramRuntimeEnvironment, - ProgramRuntimeEnvironments, DELAY_VISIBILITY_SLOT_OFFSET, + BlockRelation, ForkGraph, LoadedProgram, LoadedProgramMatchCriteria, + LoadedProgramOwner, LoadedProgramType, LoadedProgramsForTxBatch, ProgramCache, + ProgramRuntimeEnvironment, ProgramRuntimeEnvironments, DELAY_VISIBILITY_SLOT_OFFSET, }, assert_matches::assert_matches, percentage::Percentage, - solana_rbpf::program::BuiltinProgram, + solana_rbpf::{elf::Executable, program::BuiltinProgram}, solana_sdk::{clock::Slot, pubkey::Pubkey}, std::{ + fs::File, + io::Read, ops::ControlFlow, sync::{ atomic::{AtomicU64, Ordering}, @@ -1228,12 +1257,15 @@ mod tests { static MOCK_ENVIRONMENT: std::sync::OnceLock = std::sync::OnceLock::::new(); + fn get_mock_env() -> ProgramRuntimeEnvironment { + MOCK_ENVIRONMENT + .get_or_init(|| Arc::new(BuiltinProgram::new_mock())) + .clone() + } + fn new_mock_cache() -> ProgramCache { let mut cache = ProgramCache::new(0, 0); - - cache.environments.program_runtime_v1 = MOCK_ENVIRONMENT - .get_or_init(|| Arc::new(BuiltinProgram::new_mock())) - .clone(); + cache.environments.program_runtime_v1 = get_mock_env(); cache } @@ -1241,13 +1273,24 @@ mod tests { new_test_loaded_program_with_usage(deployment_slot, effective_slot, AtomicU64::default()) } + fn new_loaded_program(env: ProgramRuntimeEnvironment) -> LoadedProgramType { + let mut elf = Vec::new(); + File::open("../programs/bpf_loader/test_elfs/out/noop_aligned.so") + .unwrap() + .read_to_end(&mut elf) + .unwrap(); + let executable = Executable::load(&elf, env).unwrap(); + LoadedProgramType::Loaded(executable) + } + fn new_test_loaded_program_with_usage( deployment_slot: Slot, effective_slot: Slot, usage_counter: AtomicU64, ) -> Arc { Arc::new(LoadedProgram { - program: LoadedProgramType::TestLoaded(MOCK_ENVIRONMENT.get().unwrap().clone()), + program: new_loaded_program(get_mock_env()), + account_owner: LoadedProgramOwner::LoaderV2, account_size: 0, deployment_slot, effective_slot, @@ -1260,6 +1303,7 @@ mod tests { fn new_test_builtin_program(deployment_slot: Slot, effective_slot: Slot) -> Arc { Arc::new(LoadedProgram { program: LoadedProgramType::Builtin(BuiltinProgram::new_mock()), + account_owner: LoadedProgramOwner::NativeLoader, account_size: 0, deployment_slot, effective_slot, @@ -1275,7 +1319,11 @@ mod tests { slot: Slot, reason: LoadedProgramType, ) -> Arc { - let program = Arc::new(LoadedProgram::new_tombstone(slot, reason)); + let program = Arc::new(LoadedProgram::new_tombstone( + slot, + LoadedProgramOwner::LoaderV2, + reason, + )); cache.assign_program(key, program.clone()); program } @@ -1285,21 +1333,9 @@ mod tests { key: Pubkey, slot: Slot, ) -> Arc { - let unloaded = Arc::new( - LoadedProgram { - program: LoadedProgramType::TestLoaded( - cache.environments.program_runtime_v1.clone(), - ), - account_size: 0, - deployment_slot: slot, - effective_slot: slot.saturating_add(1), - tx_usage_counter: AtomicU64::default(), - ix_usage_counter: AtomicU64::default(), - latest_access_slot: AtomicU64::default(), - } - .to_unloaded() - .expect("Failed to unload the program"), - ); + let loaded = + new_test_loaded_program_with_usage(slot, slot.saturating_add(1), AtomicU64::default()); + let unloaded = Arc::new(loaded.to_unloaded().expect("Failed to unload the program")); cache.assign_program(key, unloaded.clone()); unloaded } @@ -1439,7 +1475,7 @@ mod tests { // Count the number of loaded, unloaded and tombstone entries. programs.sort_by_key(|(_id, _slot, usage_count)| *usage_count); let num_loaded = num_matching_entries(&cache, |program_type| { - matches!(program_type, LoadedProgramType::TestLoaded(_)) + matches!(program_type, LoadedProgramType::Loaded(_)) }); let num_unloaded = num_matching_entries(&cache, |program_type| { matches!(program_type, LoadedProgramType::Unloaded(_)) @@ -1468,7 +1504,7 @@ mod tests { // Count the number of loaded, unloaded and tombstone entries. let num_loaded = num_matching_entries(&cache, |program_type| { - matches!(program_type, LoadedProgramType::TestLoaded(_)) + matches!(program_type, LoadedProgramType::Loaded(_)) }); let num_unloaded = num_matching_entries(&cache, |program_type| { matches!(program_type, LoadedProgramType::Unloaded(_)) @@ -1527,7 +1563,7 @@ mod tests { // Count the number of loaded, unloaded and tombstone entries. programs.sort_by_key(|(_id, _slot, usage_count)| *usage_count); let num_loaded = num_matching_entries(&cache, |program_type| { - matches!(program_type, LoadedProgramType::TestLoaded(_)) + matches!(program_type, LoadedProgramType::Loaded(_)) }); let num_unloaded = num_matching_entries(&cache, |program_type| { matches!(program_type, LoadedProgramType::Unloaded(_)) @@ -1573,7 +1609,7 @@ mod tests { // Count the number of loaded, unloaded and tombstone entries. let num_loaded = num_matching_entries(&cache, |program_type| { - matches!(program_type, LoadedProgramType::TestLoaded(_)) + matches!(program_type, LoadedProgramType::Loaded(_)) }); let num_unloaded = num_matching_entries(&cache, |program_type| { matches!(program_type, LoadedProgramType::Unloaded(_)) @@ -1681,13 +1717,13 @@ mod tests { ( LoadedProgramType::Closed, LoadedProgramType::FailedVerification(Arc::new(BuiltinProgram::new_mock())), - LoadedProgramType::TestLoaded(Arc::new(BuiltinProgram::new_mock())), + new_loaded_program(get_mock_env()), ), ( LoadedProgramType::FailedVerification(Arc::new(BuiltinProgram::new_mock())), LoadedProgramType::Closed, LoadedProgramType::Unloaded(Arc::new(BuiltinProgram::new_mock())), - LoadedProgramType::TestLoaded(Arc::new(BuiltinProgram::new_mock())), + new_loaded_program(get_mock_env()), LoadedProgramType::Builtin(BuiltinProgram::new_mock()), ) )] @@ -1708,7 +1744,7 @@ mod tests { LoadedProgramType::FailedVerification(Arc::new(BuiltinProgram::new_mock())), LoadedProgramType::Closed, LoadedProgramType::Unloaded(Arc::new(BuiltinProgram::new_mock())), - LoadedProgramType::TestLoaded(Arc::new(BuiltinProgram::new_mock())), + new_loaded_program(get_mock_env()), ) )] #[should_panic(expected = "Unexpected replacement of an entry")] @@ -1719,6 +1755,7 @@ mod tests { program_id, Arc::new(LoadedProgram { program: old, + account_owner: LoadedProgramOwner::LoaderV2, account_size: 0, deployment_slot: 10, effective_slot: 11, @@ -1731,6 +1768,7 @@ mod tests { program_id, Arc::new(LoadedProgram { program: new, + account_owner: LoadedProgramOwner::LoaderV2, account_size: 0, deployment_slot: 10, effective_slot: 11, @@ -1743,7 +1781,7 @@ mod tests { #[test_case( LoadedProgramType::Unloaded(Arc::new(BuiltinProgram::new_mock())), - LoadedProgramType::TestLoaded(Arc::new(BuiltinProgram::new_mock())) + new_loaded_program(get_mock_env()) )] #[test_case( LoadedProgramType::Builtin(BuiltinProgram::new_mock()), @@ -1756,6 +1794,7 @@ mod tests { program_id, Arc::new(LoadedProgram { program: old, + account_owner: LoadedProgramOwner::LoaderV2, account_size: 0, deployment_slot: 10, effective_slot: 11, @@ -1768,6 +1807,7 @@ mod tests { program_id, Arc::new(LoadedProgram { program: new, + account_owner: LoadedProgramOwner::LoaderV2, account_size: 0, deployment_slot: 10, effective_slot: 11, @@ -1781,14 +1821,21 @@ mod tests { #[test] fn test_tombstone() { let env = Arc::new(BuiltinProgram::new_mock()); - let tombstone = - LoadedProgram::new_tombstone(0, LoadedProgramType::FailedVerification(env.clone())); + let tombstone = LoadedProgram::new_tombstone( + 0, + LoadedProgramOwner::LoaderV2, + LoadedProgramType::FailedVerification(env.clone()), + ); assert_matches!(tombstone.program, LoadedProgramType::FailedVerification(_)); assert!(tombstone.is_tombstone()); assert_eq!(tombstone.deployment_slot, 0); assert_eq!(tombstone.effective_slot, 0); - let tombstone = LoadedProgram::new_tombstone(100, LoadedProgramType::Closed); + let tombstone = LoadedProgram::new_tombstone( + 100, + LoadedProgramOwner::LoaderV2, + LoadedProgramType::Closed, + ); assert_matches!(tombstone.program, LoadedProgramType::Closed); assert!(tombstone.is_tombstone()); assert_eq!(tombstone.deployment_slot, 100); @@ -1921,7 +1968,8 @@ mod tests { program_runtime_v2: new_env.clone(), }); let updated_program = Arc::new(LoadedProgram { - program: LoadedProgramType::TestLoaded(new_env.clone()), + program: new_loaded_program(new_env.clone()), + account_owner: LoadedProgramOwner::LoaderV2, account_size: 0, deployment_slot: 20, effective_slot: 20, @@ -2460,6 +2508,7 @@ mod tests { ] { let entry = Arc::new(LoadedProgram { program: loaded_program_type, + account_owner: LoadedProgramOwner::LoaderV2, account_size: 0, deployment_slot: 0, effective_slot: 0, @@ -2623,7 +2672,11 @@ mod tests { #[test] fn test_usable_entries_for_slot() { new_mock_cache::(); - let tombstone = Arc::new(LoadedProgram::new_tombstone(0, LoadedProgramType::Closed)); + let tombstone = Arc::new(LoadedProgram::new_tombstone( + 0, + LoadedProgramOwner::LoaderV2, + LoadedProgramType::Closed, + )); assert!( ProgramCache::::matches_loaded_program_criteria( diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index dfb27ec2e..1ebb2df67 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -10,7 +10,8 @@ use { ic_logger_msg, ic_msg, invoke_context::{BpfAllocator, InvokeContext, SerializedAccountMetadata, SyscallContext}, loaded_programs::{ - LoadProgramMetrics, LoadedProgram, LoadedProgramType, DELAY_VISIBILITY_SLOT_OFFSET, + LoadProgramMetrics, LoadedProgram, LoadedProgramOwner, LoadedProgramType, + DELAY_VISIBILITY_SLOT_OFFSET, }, log_collector::LogCollector, stable_log, @@ -456,8 +457,7 @@ pub fn process_instruction_inner( ic_logger_msg!(log_collector, "Program is not deployed"); Err(Box::new(InstructionError::InvalidAccountData) as Box) } - LoadedProgramType::LegacyV0(executable) => execute(executable, invoke_context), - LoadedProgramType::LegacyV1(executable) => execute(executable, invoke_context), + LoadedProgramType::Loaded(executable) => execute(executable, invoke_context), _ => Err(Box::new(InstructionError::IncorrectProgramId) as Box), } .map(|_| 0) @@ -1113,6 +1113,7 @@ fn process_loader_upgradeable_instruction( program_key, Arc::new(LoadedProgram::new_tombstone( clock.slot, + LoadedProgramOwner::LoaderV3, LoadedProgramType::Closed, )), ); @@ -3761,6 +3762,7 @@ mod tests { let env = Arc::new(BuiltinProgram::new_mock()); let program = LoadedProgram { program: LoadedProgramType::Unloaded(env), + account_owner: LoadedProgramOwner::LoaderV2, account_size: 0, deployment_slot: 0, effective_slot: 0, @@ -3804,6 +3806,7 @@ mod tests { let env = Arc::new(BuiltinProgram::new_mock()); let program = LoadedProgram { program: LoadedProgramType::Unloaded(env), + account_owner: LoadedProgramOwner::LoaderV2, account_size: 0, deployment_slot: 0, effective_slot: 0, diff --git a/programs/loader-v4/src/lib.rs b/programs/loader-v4/src/lib.rs index 6a9026f25..23fab5eb9 100644 --- a/programs/loader-v4/src/lib.rs +++ b/programs/loader-v4/src/lib.rs @@ -608,7 +608,7 @@ pub fn process_instruction_inner( ic_logger_msg!(log_collector, "Program is not deployed"); Err(Box::new(InstructionError::InvalidAccountData) as Box) } - LoadedProgramType::Typed(executable) => execute(invoke_context, executable), + LoadedProgramType::Loaded(executable) => execute(invoke_context, executable), _ => Err(Box::new(InstructionError::IncorrectProgramId) as Box), } } diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index b7d64e6ec..3f16a49a4 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -99,7 +99,8 @@ use { compute_budget_processor::process_compute_budget_instructions, invoke_context::BuiltinFunctionWithContext, loaded_programs::{ - LoadedProgram, LoadedProgramMatchCriteria, LoadedProgramType, ProgramCache, + LoadedProgram, LoadedProgramMatchCriteria, LoadedProgramOwner, LoadedProgramType, + ProgramCache, }, timings::{ExecuteTimingType, ExecuteTimings}, }, @@ -6312,7 +6313,11 @@ impl Bank { self, program_id, name, - LoadedProgram::new_tombstone(self.slot, LoadedProgramType::Closed), + LoadedProgram::new_tombstone( + self.slot, + LoadedProgramOwner::NativeLoader, + LoadedProgramType::Closed, + ), ); debug!("Removed program {}", program_id); } diff --git a/runtime/src/bank/builtins/core_bpf_migration/mod.rs b/runtime/src/bank/builtins/core_bpf_migration/mod.rs index 97ed1d250..3be7a8d8a 100644 --- a/runtime/src/bank/builtins/core_bpf_migration/mod.rs +++ b/runtime/src/bank/builtins/core_bpf_migration/mod.rs @@ -454,7 +454,7 @@ mod tests { assert_eq!(target_entry.latest_access_slot.load(Relaxed), bank.slot()); // The target program entry should now be a BPF program. - assert_matches!(target_entry.program, LoadedProgramType::LegacyV1(..)); + assert_matches!(target_entry.program, LoadedProgramType::Loaded(..)); } } diff --git a/runtime/src/bank/tests.rs b/runtime/src/bank/tests.rs index 08ff707bb..6d81aaafd 100644 --- a/runtime/src/bank/tests.rs +++ b/runtime/src/bank/tests.rs @@ -7106,7 +7106,7 @@ fn test_bank_load_program() { bank.store_account_and_update_capitalization(&key1, &program_account); bank.store_account_and_update_capitalization(&programdata_key, &programdata_account); let program = bank.load_program(&key1, false, bank.epoch()).unwrap(); - assert_matches!(program.program, LoadedProgramType::LegacyV1(_)); + assert_matches!(program.program, LoadedProgramType::Loaded(_)); assert_eq!( program.account_size, program_account.data().len() + programdata_account.data().len() @@ -7352,7 +7352,7 @@ fn test_bpf_loader_upgradeable_deploy_with_max_len() { assert_eq!(slot_versions[1].effective_slot, 1); assert!(matches!( slot_versions[1].program, - LoadedProgramType::LegacyV1(_), + LoadedProgramType::Loaded(_), )); } diff --git a/svm/src/transaction_processor.rs b/svm/src/transaction_processor.rs index bc7cce7c7..8cd0eb6dd 100644 --- a/svm/src/transaction_processor.rs +++ b/svm/src/transaction_processor.rs @@ -20,8 +20,8 @@ use { invoke_context::InvokeContext, loaded_programs::{ ForkGraph, LoadProgramMetrics, LoadedProgram, LoadedProgramMatchCriteria, - LoadedProgramType, LoadedProgramsForTxBatch, ProgramCache, ProgramRuntimeEnvironment, - DELAY_VISIBILITY_SLOT_OFFSET, + LoadedProgramOwner, LoadedProgramType, LoadedProgramsForTxBatch, ProgramCache, + ProgramRuntimeEnvironment, DELAY_VISIBILITY_SLOT_OFFSET, }, log_collector::LogCollector, sysvar_cache::SysvarCache, @@ -30,6 +30,7 @@ use { solana_sdk::{ account::{AccountSharedData, ReadableAccount, PROGRAM_OWNERS}, account_utils::StateMut, + bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable::{self, UpgradeableLoaderState}, clock::{Epoch, Slot}, epoch_schedule::EpochSchedule, @@ -114,8 +115,9 @@ pub trait TransactionProcessingCallback { #[derive(Debug)] enum ProgramAccountLoadResult { - InvalidAccountData, - ProgramOfLoaderV1orV2(AccountSharedData), + InvalidAccountData(LoadedProgramOwner), + ProgramOfLoaderV1(AccountSharedData), + ProgramOfLoaderV2(AccountSharedData), ProgramOfLoaderV3(AccountSharedData, AccountSharedData, Slot), ProgramOfLoaderV4(AccountSharedData, Slot), } @@ -411,12 +413,11 @@ impl TransactionBatchProcessor { }; let mut loaded_program = match self.load_program_accounts(callbacks, pubkey)? { - ProgramAccountLoadResult::InvalidAccountData => Ok(LoadedProgram::new_tombstone( - self.slot, - LoadedProgramType::Closed, - )), + ProgramAccountLoadResult::InvalidAccountData(owner) => Ok( + LoadedProgram::new_tombstone(self.slot, owner, LoadedProgramType::Closed), + ), - ProgramAccountLoadResult::ProgramOfLoaderV1orV2(program_account) => { + ProgramAccountLoadResult::ProgramOfLoaderV1(program_account) => { Self::load_program_from_bytes( &mut load_program_metrics, program_account.data(), @@ -426,7 +427,20 @@ impl TransactionBatchProcessor { environments.program_runtime_v1.clone(), reload, ) - .map_err(|_| (0, environments.program_runtime_v1.clone())) + .map_err(|_| (0, LoadedProgramOwner::LoaderV1)) + } + + ProgramAccountLoadResult::ProgramOfLoaderV2(program_account) => { + Self::load_program_from_bytes( + &mut load_program_metrics, + program_account.data(), + program_account.owner(), + program_account.data().len(), + 0, + environments.program_runtime_v1.clone(), + reload, + ) + .map_err(|_| (0, LoadedProgramOwner::LoaderV2)) } ProgramAccountLoadResult::ProgramOfLoaderV3( @@ -451,7 +465,7 @@ impl TransactionBatchProcessor { reload, ) }) - .map_err(|_| (slot, environments.program_runtime_v1.clone())), + .map_err(|_| (slot, LoadedProgramOwner::LoaderV3)), ProgramAccountLoadResult::ProgramOfLoaderV4(program_account, slot) => program_account .data() @@ -468,10 +482,15 @@ impl TransactionBatchProcessor { reload, ) }) - .map_err(|_| (slot, environments.program_runtime_v2.clone())), + .map_err(|_| (slot, LoadedProgramOwner::LoaderV4)), } - .unwrap_or_else(|(slot, env)| { - LoadedProgram::new_tombstone(slot, LoadedProgramType::FailedVerification(env)) + .unwrap_or_else(|(slot, owner)| { + let env = if let LoadedProgramOwner::LoaderV4 = &owner { + environments.program_runtime_v2.clone() + } else { + environments.program_runtime_v1.clone() + }; + LoadedProgram::new_tombstone(slot, owner, LoadedProgramType::FailedVerification(env)) }); let mut timings = ExecuteDetailsTimings::default(); @@ -856,14 +875,18 @@ impl TransactionBatchProcessor { (!matches!(state.status, LoaderV4Status::Retracted)).then_some(state.slot) }) .map(|slot| ProgramAccountLoadResult::ProgramOfLoaderV4(program_account, slot)) - .unwrap_or(ProgramAccountLoadResult::InvalidAccountData), + .unwrap_or(ProgramAccountLoadResult::InvalidAccountData( + LoadedProgramOwner::LoaderV4, + )), ); } - if !bpf_loader_upgradeable::check_id(program_account.owner()) { - return Some(ProgramAccountLoadResult::ProgramOfLoaderV1orV2( - program_account, - )); + if bpf_loader_deprecated::check_id(program_account.owner()) { + return Some(ProgramAccountLoadResult::ProgramOfLoaderV1(program_account)); + } + + if bpf_loader::check_id(program_account.owner()) { + return Some(ProgramAccountLoadResult::ProgramOfLoaderV2(program_account)); } if let Ok(UpgradeableLoaderState::Program { @@ -886,7 +909,9 @@ impl TransactionBatchProcessor { } } } - Some(ProgramAccountLoadResult::InvalidAccountData) + Some(ProgramAccountLoadResult::InvalidAccountData( + LoadedProgramOwner::LoaderV3, + )) } /// Extract the InnerInstructionsList from a TransactionContext @@ -1139,7 +1164,7 @@ mod tests { let result = batch_processor.load_program_accounts(&mock_bank, &key); assert!(matches!( result, - Some(ProgramAccountLoadResult::InvalidAccountData) + Some(ProgramAccountLoadResult::InvalidAccountData(_)) )); account_data.set_data(Vec::new()); @@ -1152,7 +1177,7 @@ mod tests { assert!(matches!( result, - Some(ProgramAccountLoadResult::InvalidAccountData) + Some(ProgramAccountLoadResult::InvalidAccountData(_)) )); } @@ -1171,7 +1196,7 @@ mod tests { let result = batch_processor.load_program_accounts(&mock_bank, &key); assert!(matches!( result, - Some(ProgramAccountLoadResult::InvalidAccountData) + Some(ProgramAccountLoadResult::InvalidAccountData(_)) )); account_data.set_data(vec![0; 64]); @@ -1182,7 +1207,7 @@ mod tests { let result = batch_processor.load_program_accounts(&mock_bank, &key); assert!(matches!( result, - Some(ProgramAccountLoadResult::InvalidAccountData) + Some(ProgramAccountLoadResult::InvalidAccountData(_)) )); let loader_data = LoaderV4State { @@ -1227,7 +1252,8 @@ mod tests { let result = batch_processor.load_program_accounts(&mock_bank, &key); match result { - Some(ProgramAccountLoadResult::ProgramOfLoaderV1orV2(data)) => { + Some(ProgramAccountLoadResult::ProgramOfLoaderV1(data)) + | Some(ProgramAccountLoadResult::ProgramOfLoaderV2(data)) => { assert_eq!(data, account_data); } _ => panic!("Invalid result"), @@ -1351,6 +1377,7 @@ mod tests { let loaded_program = LoadedProgram::new_tombstone( 0, + LoadedProgramOwner::LoaderV4, LoadedProgramType::FailedVerification( batch_processor .program_cache @@ -1380,6 +1407,7 @@ mod tests { let result = batch_processor.load_program_with_pubkey(&mock_bank, &key, false, 20); let loaded_program = LoadedProgram::new_tombstone( 0, + LoadedProgramOwner::LoaderV2, LoadedProgramType::FailedVerification( batch_processor .program_cache @@ -1450,6 +1478,7 @@ mod tests { let result = batch_processor.load_program_with_pubkey(&mock_bank, &key1, false, 0); let loaded_program = LoadedProgram::new_tombstone( 0, + LoadedProgramOwner::LoaderV3, LoadedProgramType::FailedVerification( batch_processor .program_cache @@ -1526,6 +1555,7 @@ mod tests { let result = batch_processor.load_program_with_pubkey(&mock_bank, &key, false, 0); let loaded_program = LoadedProgram::new_tombstone( 0, + LoadedProgramOwner::LoaderV4, LoadedProgramType::FailedVerification( batch_processor .program_cache