Skip to content

Commit

Permalink
Code to load a program from its account
Browse files Browse the repository at this point in the history
  • Loading branch information
pgarg66 committed Feb 13, 2023
1 parent 5441645 commit e49b0f5
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 22 deletions.
32 changes: 28 additions & 4 deletions program-runtime/src/loaded_programs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ impl Debug for LoadedProgramType {
pub struct LoadedProgram {
/// The program of this entry
pub program: LoadedProgramType,
/// Size of account that stores the program
pub account_size: usize,
/// Pubkey of programdata account, if the program uses bpf_loader_upgradeable
pub maybe_programdata: Option<Pubkey>,
/// Size of programdata account
pub programdata_account_size: usize,
/// Slot in which the program was (re)deployed
pub deployment_slot: Slot,
/// Slot in which this entry will become active (can be in the future)
Expand All @@ -85,6 +91,9 @@ impl LoadedProgram {
loader: Arc<BuiltInProgram<InvokeContext<'static>>>,
deployment_slot: Slot,
elf_bytes: &[u8],
account_size: usize,
programdata_key: Option<Pubkey>,
programdata_size: usize,
) -> Result<Self, EbpfError> {
let program = if bpf_loader_deprecated::check_id(loader_key) {
let executable = Executable::load(elf_bytes, loader.clone())?;
Expand All @@ -97,6 +106,9 @@ impl LoadedProgram {
};
Ok(Self {
deployment_slot,
account_size,
maybe_programdata: programdata_key,
programdata_account_size: programdata_size,
effective_slot: deployment_slot.saturating_add(1),
usage_counter: AtomicU64::new(0),
program,
Expand All @@ -110,6 +122,9 @@ impl LoadedProgram {
) -> Self {
Self {
deployment_slot,
account_size: 0,
maybe_programdata: None,
programdata_account_size: 0,
effective_slot: deployment_slot.saturating_add(1),
usage_counter: AtomicU64::new(0),
program: LoadedProgramType::BuiltIn(program),
Expand All @@ -135,9 +150,14 @@ impl solana_frozen_abi::abi_example::AbiExample for LoadedPrograms {
}
}

pub enum LoadedProgramEntry {
PreExisting(Arc<LoadedProgram>),
Inserted(Arc<LoadedProgram>),
}

impl LoadedPrograms {
/// Inserts a single entry
pub fn insert_entry(&mut self, key: Pubkey, entry: LoadedProgram) -> bool {
pub fn insert_entry(&mut self, key: Pubkey, entry: LoadedProgram) -> LoadedProgramEntry {
let second_level = self.entries.entry(key).or_insert_with(Vec::new);
let index = second_level
.iter()
Expand All @@ -149,11 +169,12 @@ impl LoadedPrograms {
if existing.deployment_slot == entry.deployment_slot
&& existing.effective_slot == entry.effective_slot
{
return false;
return LoadedProgramEntry::PreExisting(existing.clone());
}
}
second_level.insert(index.unwrap_or(second_level.len()), Arc::new(entry));
true
let new_entry = Arc::new(entry);
second_level.insert(index.unwrap_or(second_level.len()), new_entry.clone());
LoadedProgramEntry::Inserted(new_entry)
}

/// Before rerooting the blockstore this removes all programs of orphan forks
Expand Down Expand Up @@ -375,6 +396,9 @@ mod tests {
fn new_test_loaded_program(deployment_slot: Slot, effective_slot: Slot) -> Arc<LoadedProgram> {
Arc::new(LoadedProgram {
program: LoadedProgramType::Invalid,
account_size: 0,
maybe_programdata: None,
programdata_account_size: 0,
deployment_slot,
effective_slot,
usage_counter: AtomicU64::default(),
Expand Down
105 changes: 87 additions & 18 deletions programs/bpf_loader/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use {
executor_cache::TransactionExecutorCache,
ic_logger_msg, ic_msg,
invoke_context::InvokeContext,
loaded_programs::LoadedProgram,
log_collector::LogCollector,
stable_log,
sysvar_cache::get_sysvar_with_account_check,
Expand Down Expand Up @@ -180,24 +181,12 @@ fn create_executor_from_bytes(
}))
}

pub fn create_executor_from_account(
feature_set: &FeatureSet,
compute_budget: &ComputeBudget,
log_collector: Option<Rc<RefCell<LogCollector>>>,
tx_executor_cache: Option<RefMut<TransactionExecutorCache>>,
fn get_programdata_offset(
log_collector: &Option<Rc<RefCell<LogCollector>>>,
program: &BorrowedAccount,
programdata: &BorrowedAccount,
use_jit: bool,
) -> Result<(Arc<dyn Executor>, Option<CreateMetrics>), InstructionError> {
if !check_loader_id(program.get_owner()) {
ic_logger_msg!(
log_collector,
"Executable account not owned by the BPF loader"
);
return Err(InstructionError::IncorrectProgramId);
}

let programdata_offset = if bpf_loader_upgradeable::check_id(program.get_owner()) {
) -> Result<usize, InstructionError> {
if bpf_loader_upgradeable::check_id(program.get_owner()) {
if let UpgradeableLoaderState::Program {
programdata_address,
} = program.get_state()?
Expand All @@ -219,15 +208,95 @@ pub fn create_executor_from_account(
ic_logger_msg!(log_collector, "Program has been closed");
return Err(InstructionError::InvalidAccountData);
}
UpgradeableLoaderState::size_of_programdata_metadata()
Ok(UpgradeableLoaderState::size_of_programdata_metadata())
} else {
ic_logger_msg!(log_collector, "Invalid Program account");
return Err(InstructionError::InvalidAccountData);
Err(InstructionError::InvalidAccountData)
}
} else {
Ok(0)
}
}

pub fn load_program_from_account(
feature_set: &FeatureSet,
compute_budget: &ComputeBudget,
log_collector: Option<Rc<RefCell<LogCollector>>>,
program: &BorrowedAccount,
programdata_key: Option<Pubkey>,
programdata: &BorrowedAccount,
) -> Result<(LoadedProgram, Option<CreateMetrics>), InstructionError> {
if !check_loader_id(program.get_owner()) {
ic_logger_msg!(
log_collector,
"Executable account not owned by the BPF loader"
);
return Err(InstructionError::IncorrectProgramId);
}

let programdata_offset = get_programdata_offset(&log_collector, program, programdata)?;
let programdata_size = if programdata_offset != 0 {
programdata.get_data().len()
} else {
0
};

let mut create_executor_metrics = CreateMetrics {
program_id: program.get_key().to_string(),
..CreateMetrics::default()
};

let mut register_syscalls_time = Measure::start("register_syscalls_time");
let loader = syscalls::create_loader(feature_set, compute_budget, false, false, false)
.map_err(|e| {
ic_logger_msg!(log_collector, "Failed to register syscalls: {}", e);
InstructionError::ProgramEnvironmentSetupFailure
})?;
register_syscalls_time.stop();
create_executor_metrics.register_syscalls_us = register_syscalls_time.as_us();

let mut load_elf_time = Measure::start("load_elf_time");
let loaded_program = LoadedProgram::new(
program.get_owner(),
loader,
0, // Fill in the deployment slot of the program
programdata
.get_data()
.get(programdata_offset..)
.ok_or(InstructionError::AccountDataTooSmall)?,
program.get_data().len(),
programdata_key,
programdata_size,
)
.map_err(|err| {
ic_logger_msg!(log_collector, "{}", err);
InstructionError::InvalidAccountData
})?;
load_elf_time.stop();
create_executor_metrics.load_elf_us = load_elf_time.as_us();

Ok((loaded_program, Some(create_executor_metrics)))
}

pub fn create_executor_from_account(
feature_set: &FeatureSet,
compute_budget: &ComputeBudget,
log_collector: Option<Rc<RefCell<LogCollector>>>,
tx_executor_cache: Option<RefMut<TransactionExecutorCache>>,
program: &BorrowedAccount,
programdata: &BorrowedAccount,
use_jit: bool,
) -> Result<(Arc<dyn Executor>, Option<CreateMetrics>), InstructionError> {
if !check_loader_id(program.get_owner()) {
ic_logger_msg!(
log_collector,
"Executable account not owned by the BPF loader"
);
return Err(InstructionError::IncorrectProgramId);
}

let programdata_offset = get_programdata_offset(&log_collector, program, programdata)?;

if let Some(ref tx_executor_cache) = tx_executor_cache {
match tx_executor_cache.get(program.get_key()) {
// Executor exists and is cached, use it
Expand Down

0 comments on commit e49b0f5

Please sign in to comment.