Refactor - `LoadedProgramType::Loaded` and `LoadedProgramOwner` (#606)

* Adds LoadedProgram::account_owner() and LoadedProgramOwner.
Merges LoadedProgramTypes LegacyV0, LegacyV1, Typed and TestLoaded into Loaded.

* Review feedback.
This commit is contained in:
Alexander Meißner 2024-04-17 17:48:14 +02:00 committed by GitHub
parent 2aeac5b468
commit ac36dbbc87
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 214 additions and 123 deletions

View File

@ -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:?}")),

View File

@ -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<Self, ()> {
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<LoadedProgramOwner> 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<InvokeContext<'static>>),
/// Verified and compiled program of loader-v3 (aka upgradable loader)
LegacyV1(Executable<InvokeContext<'static>>),
/// Verified and compiled program of loader-v4
Typed(Executable<InvokeContext<'static>>),
#[cfg(test)]
TestLoaded(ProgramRuntimeEnvironment),
/// Verified and compiled program
Loaded(Executable<InvokeContext<'static>>),
/// A built-in program which is not stored on-chain but backed into and distributed with the validator
Builtin(BuiltinProgram<InvokeContext<'static>>),
}
@ -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<Self> {
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<FG: ForkGraph> ProgramCache<FG> {
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<FG: ForkGraph> ProgramCache<FG> {
// the tombstone to reflect that.
Arc::new(LoadedProgram::new_tombstone(
entry.deployment_slot,
entry.account_owner,
LoadedProgramType::DelayVisibility,
))
} else {
@ -1061,16 +1087,17 @@ impl<FG: ForkGraph> ProgramCache<FG> {
.slot_versions
.iter()
.filter_map(move |program| match program.program {
LoadedProgramType::LegacyV0(_) | LoadedProgramType::LegacyV1(_)
if include_program_runtime_v1 =>
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<FG: ForkGraph> 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<ProgramRuntimeEnvironment> =
std::sync::OnceLock::<ProgramRuntimeEnvironment>::new();
fn get_mock_env() -> ProgramRuntimeEnvironment {
MOCK_ENVIRONMENT
.get_or_init(|| Arc::new(BuiltinProgram::new_mock()))
.clone()
}
fn new_mock_cache<FG: ForkGraph>() -> ProgramCache<FG> {
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<LoadedProgram> {
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<LoadedProgram> {
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<LoadedProgram> {
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<LoadedProgram> {
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::<TestForkGraph>();
let tombstone = Arc::new(LoadedProgram::new_tombstone(0, LoadedProgramType::Closed));
let tombstone = Arc::new(LoadedProgram::new_tombstone(
0,
LoadedProgramOwner::LoaderV2,
LoadedProgramType::Closed,
));
assert!(
ProgramCache::<TestForkGraph>::matches_loaded_program_criteria(

View File

@ -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<dyn std::error::Error>)
}
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<dyn std::error::Error>),
}
.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,

View File

@ -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<dyn std::error::Error>)
}
LoadedProgramType::Typed(executable) => execute(invoke_context, executable),
LoadedProgramType::Loaded(executable) => execute(invoke_context, executable),
_ => Err(Box::new(InstructionError::IncorrectProgramId) as Box<dyn std::error::Error>),
}
}

View File

@ -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);
}

View File

@ -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(..));
}
}

View File

@ -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(_),
));
}

View File

@ -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<FG: ForkGraph> TransactionBatchProcessor<FG> {
};
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<FG: ForkGraph> TransactionBatchProcessor<FG> {
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<FG: ForkGraph> TransactionBatchProcessor<FG> {
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<FG: ForkGraph> TransactionBatchProcessor<FG> {
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<FG: ForkGraph> TransactionBatchProcessor<FG> {
(!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<FG: ForkGraph> TransactionBatchProcessor<FG> {
}
}
}
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