From 451404764db01df46d8a50349bd8a4937e05583c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Fri, 29 Mar 2024 12:41:35 +0100 Subject: [PATCH] Adds LoadedProgram::account_owner() and LoadedProgramOwner. Merges LoadedProgramTypes LegacyV0, LegacyV1, Typed and TestLoaded into Loaded. --- ledger-tool/src/program.rs | 2 +- program-runtime/src/loaded_programs.rs | 207 ++++++++++++++----------- programs/bpf_loader/src/lib.rs | 9 +- programs/loader-v4/src/lib.rs | 2 +- runtime/src/bank.rs | 9 +- runtime/src/bank/tests.rs | 2 +- svm/src/transaction_processor.rs | 80 +++++++--- 7 files changed, 191 insertions(+), 120 deletions(-) diff --git a/ledger-tool/src/program.rs b/ledger-tool/src/program.rs index 7a5f5cc6492e6b..1dd64118c58575 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 419ae7330b410e..382727bb7399df 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,17 @@ pub trait ForkGraph { } } +/// The owner of a programs accounts, thus the loader of a program +#[derive(Default, Clone, Debug)] +pub enum LoadedProgramOwner { + #[default] + NativeLoader, + LoaderV1, + LoaderV2, + LoaderV3, + LoaderV4, +} + /// Actual payload of [LoadedProgram]. #[derive(Default)] pub enum LoadedProgramType { @@ -76,14 +87,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 +102,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 +112,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 +128,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 +354,25 @@ 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) + let account_owner = if bpf_loader_deprecated::check_id(loader_key) { + LoadedProgramOwner::LoaderV1 + } else if bpf_loader::check_id(loader_key) { + LoadedProgramOwner::LoaderV2 + } else if bpf_loader_upgradeable::check_id(loader_key) { + LoadedProgramOwner::LoaderV3 } else if loader_v4::check_id(loader_key) { - LoadedProgramType::Typed(executable) + LoadedProgramOwner::LoaderV4 } else { panic!(); }; Ok(Self { deployment_slot, + account_owner, 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 +380,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 +391,7 @@ impl LoadedProgram { } Some(Self { program: LoadedProgramType::Unloaded(self.program.get_environment()?.clone()), + account_owner: self.account_owner.clone(), account_size: self.account_size, deployment_slot: self.deployment_slot, effective_slot: self.effective_slot, @@ -414,6 +413,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 +423,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 +469,16 @@ 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 { + match self.account_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(), + } + } } /// Globally shared RBPF config and syscall registry @@ -691,6 +706,7 @@ impl LoadedProgramsForTxBatch { // the tombstone to reflect that. Arc::new(LoadedProgram::new_tombstone( entry.deployment_slot, + entry.account_owner.clone(), LoadedProgramType::DelayVisibility, )) } else { @@ -778,15 +794,8 @@ impl ProgramCache { match (&existing.program, &entry.program) { // Add test for Closed => Loaded transition in same slot (LoadedProgramType::Builtin(_), LoadedProgramType::Builtin(_)) - | (LoadedProgramType::Closed, LoadedProgramType::LegacyV0(_)) - | (LoadedProgramType::Closed, LoadedProgramType::LegacyV1(_)) - | (LoadedProgramType::Closed, LoadedProgramType::Typed(_)) - | (LoadedProgramType::Unloaded(_), LoadedProgramType::LegacyV0(_)) - | (LoadedProgramType::Unloaded(_), LoadedProgramType::LegacyV1(_)) - | (LoadedProgramType::Unloaded(_), LoadedProgramType::Typed(_)) => {} - #[cfg(test)] - (LoadedProgramType::Closed, LoadedProgramType::TestLoaded(_)) - | (LoadedProgramType::Unloaded(_), LoadedProgramType::TestLoaded(_)) => {} + | (LoadedProgramType::Closed, LoadedProgramType::Loaded(_)) + | (LoadedProgramType::Unloaded(_), LoadedProgramType::Loaded(_)) => {} _ => { // Something is wrong, I can feel it ... error!("ProgramCache::assign_program() failed key={:?} existing={:?} entry={:?}", key, slot_versions, entry); @@ -972,6 +981,7 @@ impl ProgramCache { // the tombstone to reflect that. Arc::new(LoadedProgram::new_tombstone( entry.deployment_slot, + entry.account_owner.clone(), LoadedProgramType::DelayVisibility, )) } else { @@ -1066,16 +1076,19 @@ 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::Typed(_) if include_program_runtime_v2 => { - Some((*id, program.clone())) + LoadedProgramType::Loaded(_) => { + let include = + if let LoadedProgramOwner::LoaderV4 = program.account_owner { + include_program_runtime_v2 + } else { + include_program_runtime_v1 + }; + if include { + Some((*id, program.clone())) + } else { + None + } } - #[cfg(test)] - LoadedProgramType::TestLoaded(_) => Some((*id, program.clone())), _ => None, }) }) @@ -1212,15 +1225,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}, @@ -1246,13 +1261,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(MOCK_ENVIRONMENT.get().unwrap().clone()), + account_owner: LoadedProgramOwner::LoaderV2, account_size: 0, deployment_slot, effective_slot, @@ -1265,6 +1291,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, @@ -1280,7 +1307,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 } @@ -1290,21 +1321,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 } @@ -1444,7 +1463,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(_)) @@ -1473,7 +1492,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(_)) @@ -1532,7 +1551,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(_)) @@ -1578,7 +1597,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(_)) @@ -1685,13 +1704,13 @@ mod tests { #[test_matrix( ( LoadedProgramType::FailedVerification(Arc::new(BuiltinProgram::new_mock())), - LoadedProgramType::TestLoaded(Arc::new(BuiltinProgram::new_mock())), + new_loaded_program(MOCK_ENVIRONMENT.get().unwrap().clone()), ), ( 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(MOCK_ENVIRONMENT.get().unwrap().clone()), LoadedProgramType::Builtin(BuiltinProgram::new_mock()), ) )] @@ -1713,7 +1732,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(MOCK_ENVIRONMENT.get().unwrap().clone()), ) )] #[should_panic(expected = "Unexpected replacement of an entry")] @@ -1724,6 +1743,7 @@ mod tests { program_id, Arc::new(LoadedProgram { program: old, + account_owner: LoadedProgramOwner::LoaderV2, account_size: 0, deployment_slot: 10, effective_slot: 11, @@ -1736,6 +1756,7 @@ mod tests { program_id, Arc::new(LoadedProgram { program: new, + account_owner: LoadedProgramOwner::LoaderV2, account_size: 0, deployment_slot: 10, effective_slot: 11, @@ -1746,13 +1767,10 @@ mod tests { ); } - #[test_case( - LoadedProgramType::Closed, - LoadedProgramType::TestLoaded(Arc::new(BuiltinProgram::new_mock())) - )] + #[test_case(LoadedProgramType::Closed, new_loaded_program(MOCK_ENVIRONMENT.get().unwrap().clone()))] #[test_case( LoadedProgramType::Unloaded(Arc::new(BuiltinProgram::new_mock())), - LoadedProgramType::TestLoaded(Arc::new(BuiltinProgram::new_mock())) + new_loaded_program(MOCK_ENVIRONMENT.get().unwrap().clone()) )] #[test_case( LoadedProgramType::Builtin(BuiltinProgram::new_mock()), @@ -1765,6 +1783,7 @@ mod tests { program_id, Arc::new(LoadedProgram { program: old, + account_owner: LoadedProgramOwner::LoaderV2, account_size: 0, deployment_slot: 10, effective_slot: 11, @@ -1777,6 +1796,7 @@ mod tests { program_id, Arc::new(LoadedProgram { program: new, + account_owner: LoadedProgramOwner::LoaderV2, account_size: 0, deployment_slot: 10, effective_slot: 11, @@ -1790,14 +1810,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); @@ -1930,7 +1957,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, @@ -2469,6 +2497,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, @@ -2632,7 +2661,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 dfb27ec2eb97b1..1ebb2df672e5f9 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 6a9026f25708d0..23fab5eb9c7f2b 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 a05cf04bf4185c..89ccc75cfd0554 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, }, runtime_config::RuntimeConfig, timings::{ExecuteTimingType, ExecuteTimings}, @@ -6468,7 +6469,11 @@ impl Bank { self.add_builtin( 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/tests.rs b/runtime/src/bank/tests.rs index 9e8b44ffbe625e..230d747806a411 100644 --- a/runtime/src/bank/tests.rs +++ b/runtime/src/bank/tests.rs @@ -7138,7 +7138,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() diff --git a/svm/src/transaction_processor.rs b/svm/src/transaction_processor.rs index d827695e159f4b..05ad0b03c676cb 100644 --- a/svm/src/transaction_processor.rs +++ b/svm/src/transaction_processor.rs @@ -19,8 +19,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, runtime_config::RuntimeConfig, @@ -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, @@ -112,8 +113,9 @@ pub trait TransactionProcessingCallback { #[derive(Debug)] enum ProgramAccountLoadResult { - InvalidAccountData, - ProgramOfLoaderV1orV2(AccountSharedData), + InvalidAccountData(LoadedProgramOwner), + ProgramOfLoaderV1(AccountSharedData), + ProgramOfLoaderV2(AccountSharedData), ProgramOfLoaderV3(AccountSharedData, AccountSharedData, Slot), ProgramOfLoaderV4(AccountSharedData, Slot), } @@ -404,12 +406,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(), @@ -419,7 +420,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( @@ -444,7 +458,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() @@ -461,10 +475,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(); @@ -849,14 +868,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 { @@ -879,7 +902,9 @@ impl TransactionBatchProcessor { } } } - Some(ProgramAccountLoadResult::InvalidAccountData) + Some(ProgramAccountLoadResult::InvalidAccountData( + LoadedProgramOwner::LoaderV3, + )) } /// Extract the InnerInstructionsList from a TransactionContext @@ -1097,7 +1122,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()); @@ -1107,7 +1132,7 @@ mod tests { assert!(matches!( result, - Some(ProgramAccountLoadResult::InvalidAccountData) + Some(ProgramAccountLoadResult::InvalidAccountData(_)) )); } @@ -1125,7 +1150,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]); @@ -1135,7 +1160,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 { @@ -1178,7 +1203,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"), @@ -1299,6 +1325,7 @@ mod tests { let loaded_program = LoadedProgram::new_tombstone( 0, + LoadedProgramOwner::LoaderV4, LoadedProgramType::FailedVerification( batch_processor .program_cache @@ -1327,6 +1354,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 @@ -1394,6 +1422,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 @@ -1468,6 +1497,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