Skip to content
This repository has been archived by the owner on Jan 22, 2025. It is now read-only.

Integrate program loader-v4 with bank #32832

Merged
merged 6 commits into from
Aug 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions ledger-tool/src/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -536,8 +536,14 @@ pub fn program(ledger_path: &Path, matches: &ArgMatches<'_>) {
with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);

// Adding `DELAY_VISIBILITY_SLOT_OFFSET` to slots to accommodate for delay visibility of the program
let mut loaded_programs =
LoadedProgramsForTxBatch::new(bank.slot() + DELAY_VISIBILITY_SLOT_OFFSET);
let mut loaded_programs = LoadedProgramsForTxBatch::new(
bank.slot() + DELAY_VISIBILITY_SLOT_OFFSET,
bank.loaded_programs_cache
.read()
.unwrap()
.environments
.clone(),
);
for key in cached_account_keys {
loaded_programs.replenish(key, bank.load_program(&key));
debug!("Loaded program {}", key);
Expand Down
8 changes: 8 additions & 0 deletions program-runtime/src/invoke_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,14 @@ impl<'a> InvokeContext<'a> {
}
}

pub fn find_program_in_cache(&self, pubkey: &Pubkey) -> Option<Arc<LoadedProgram>> {
// First lookup the cache of the programs modified by the current transaction. If not found, lookup
// the cache of the cache of the programs that are loaded for the transaction batch.
self.programs_modified_by_tx
.find(pubkey)
.or_else(|| self.programs_loaded_for_tx_batch.find(pubkey))
}

/// Push a stack frame onto the invocation stack
pub fn push(&mut self) -> Result<(), InstructionError> {
let instruction_context = self
Expand Down
26 changes: 17 additions & 9 deletions program-runtime/src/loaded_programs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,16 +347,21 @@ impl LoadedProgram {
}
}

#[derive(Clone, Debug, Default)]
pub struct ProgramRuntimeEnvironments {
/// Globally shared RBPF config and syscall registry
pub program_runtime_v1: Arc<BuiltinProgram<InvokeContext<'static>>>,
/// Globally shared RBPF config and syscall registry for runtime V2
pub program_runtime_v2: Arc<BuiltinProgram<InvokeContext<'static>>>,
}

#[derive(Debug, Default)]
pub struct LoadedPrograms {
/// A two level index:
///
/// Pubkey is the address of a program, multiple versions can coexists simultaneously under the same address (in different slots).
entries: HashMap<Pubkey, Vec<Arc<LoadedProgram>>>,
/// Globally shared RBPF config and syscall registry
pub program_runtime_environment_v1: Arc<BuiltinProgram<InvokeContext<'static>>>,
/// Globally shared RBPF config and syscall registry for runtime V2
pub program_runtime_environment_v2: Arc<BuiltinProgram<InvokeContext<'static>>>,
pub environments: ProgramRuntimeEnvironments,
latest_root: Slot,
pub stats: Stats,
}
Expand All @@ -367,13 +372,15 @@ pub struct LoadedProgramsForTxBatch {
/// LoadedProgram is the corresponding program entry valid for the slot in which a transaction is being executed.
entries: HashMap<Pubkey, Arc<LoadedProgram>>,
slot: Slot,
pub environments: ProgramRuntimeEnvironments,
}

impl LoadedProgramsForTxBatch {
pub fn new(slot: Slot) -> Self {
pub fn new(slot: Slot, environments: ProgramRuntimeEnvironments) -> Self {
Self {
entries: HashMap::new(),
slot,
environments,
}
}

Expand Down Expand Up @@ -496,22 +503,22 @@ impl LoadedPrograms {
LoadedProgramType::LegacyV0(program) | LoadedProgramType::LegacyV1(program)
if Arc::ptr_eq(
program.get_loader(),
&self.program_runtime_environment_v1,
&self.environments.program_runtime_v1,
) =>
{
true
}
LoadedProgramType::Unloaded(environment)
| LoadedProgramType::FailedVerification(environment)
if Arc::ptr_eq(environment, &self.program_runtime_environment_v1)
|| Arc::ptr_eq(environment, &self.program_runtime_environment_v2) =>
if Arc::ptr_eq(environment, &self.environments.program_runtime_v1)
|| Arc::ptr_eq(environment, &self.environments.program_runtime_v2) =>
{
true
}
LoadedProgramType::Typed(program)
if Arc::ptr_eq(
program.get_loader(),
&self.program_runtime_environment_v2,
&self.environments.program_runtime_v2,
) =>
{
true
Expand Down Expand Up @@ -654,6 +661,7 @@ impl LoadedPrograms {
LoadedProgramsForTxBatch {
entries: found,
slot: working_slot.current_slot(),
environments: self.environments.clone(),
},
missing,
)
Expand Down
17 changes: 3 additions & 14 deletions programs/bpf_loader/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,18 +99,6 @@ pub fn load_program_from_bytes(
Ok(loaded_program)
}

fn find_program_in_cache(
invoke_context: &InvokeContext,
pubkey: &Pubkey,
) -> Option<Arc<LoadedProgram>> {
// First lookup the cache of the programs modified by the current transaction. If not found, lookup
// the cache of the cache of the programs that are loaded for the transaction batch.
invoke_context
.programs_modified_by_tx
.find(pubkey)
.or_else(|| invoke_context.programs_loaded_for_tx_batch.find(pubkey))
}

macro_rules! deploy_program {
($invoke_context:expr, $program_id:expr, $loader_key:expr,
$account_size:expr, $slot:expr, $drop:expr, $new_programdata:expr $(,)?) => {{
Expand All @@ -137,7 +125,7 @@ macro_rules! deploy_program {
$slot,
Arc::new(program_runtime_environment),
)?;
if let Some(old_entry) = find_program_in_cache($invoke_context, &$program_id) {
if let Some(old_entry) = $invoke_context.find_program_in_cache(&$program_id) {
executor.tx_usage_counter.store(
old_entry.tx_usage_counter.load(Ordering::Relaxed),
Ordering::Relaxed
Expand Down Expand Up @@ -507,7 +495,8 @@ fn process_instruction_inner(
}

let mut get_or_create_executor_time = Measure::start("get_or_create_executor_time");
let executor = find_program_in_cache(invoke_context, program_account.get_key())
let executor = invoke_context
.find_program_in_cache(program_account.get_key())
.ok_or(InstructionError::InvalidAccountData)?;

if executor.is_tombstone() {
Expand Down
166 changes: 111 additions & 55 deletions programs/loader-v4/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ use {
compute_budget::ComputeBudget,
ic_logger_msg,
invoke_context::InvokeContext,
loaded_programs::{LoadProgramMetrics, LoadedProgram, LoadedProgramType},
loaded_programs::{
LoadProgramMetrics, LoadedProgram, LoadedProgramType, DELAY_VISIBILITY_SLOT_OFFSET,
},
log_collector::LogCollector,
stable_log,
},
Expand All @@ -22,7 +24,7 @@ use {
},
solana_sdk::{
entrypoint::{HEAP_LENGTH, SUCCESS},
feature_set::{self, FeatureSet},
feature_set,
instruction::InstructionError,
loader_v4::{self, LoaderV4State, DEPLOYMENT_COOLDOWN_IN_SLOTS},
loader_v4_instruction::LoaderV4Instruction,
Expand Down Expand Up @@ -100,42 +102,6 @@ pub fn create_program_runtime_environment_v2<'a>(
BuiltinProgram::new_loader(config)
}

pub fn load_program_from_account(
_feature_set: &FeatureSet,
compute_budget: &ComputeBudget,
log_collector: Option<Rc<RefCell<LogCollector>>>,
program: &BorrowedAccount,
debugging_features: bool,
) -> Result<(Arc<LoadedProgram>, LoadProgramMetrics), InstructionError> {
let mut load_program_metrics = LoadProgramMetrics {
program_id: program.get_key().to_string(),
..LoadProgramMetrics::default()
};
let state = get_state(program.get_data())?;
let programdata = program
.get_data()
.get(LoaderV4State::program_data_offset()..)
.ok_or(InstructionError::AccountDataTooSmall)?;
let loaded_program = LoadedProgram::new(
&loader_v4::id(),
Arc::new(create_program_runtime_environment_v2(
compute_budget,
debugging_features,
)),
state.slot,
state.slot.saturating_add(1),
None,
programdata,
program.get_data().len(),
&mut load_program_metrics,
)
.map_err(|err| {
ic_logger_msg!(log_collector, "{}", err);
InstructionError::InvalidAccountData
})?;
Ok((Arc::new(loaded_program), load_program_metrics))
}

fn calculate_heap_cost(heap_size: u64, heap_cost: u64) -> u64 {
const KIBIBYTE: u64 = 1024;
const PAGE_SIZE_KB: u64 = 32;
Expand Down Expand Up @@ -458,13 +424,37 @@ pub fn process_instruction_deploy(
} else {
&program
};
let (_executor, load_program_metrics) = load_program_from_account(
&invoke_context.feature_set,
invoke_context.get_compute_budget(),
invoke_context.get_log_collector(),
buffer,
false, /* debugging_features */
)?;

let programdata = buffer
.get_data()
.get(LoaderV4State::program_data_offset()..)
.ok_or(InstructionError::AccountDataTooSmall)?;

let deployment_slot = state.slot;
let effective_slot = deployment_slot.saturating_add(DELAY_VISIBILITY_SLOT_OFFSET);

let mut load_program_metrics = LoadProgramMetrics {
program_id: buffer.get_key().to_string(),
..LoadProgramMetrics::default()
};
let executor = LoadedProgram::new(
&loader_v4::id(),
invoke_context
.programs_modified_by_tx
.environments
.program_runtime_v2
.clone(),
deployment_slot,
effective_slot,
None,
programdata,
buffer.get_data().len(),
&mut load_program_metrics,
)
.map_err(|err| {
ic_logger_msg!(log_collector, "{}", err);
InstructionError::InvalidAccountData
})?;
load_program_metrics.submit_datapoint(&mut invoke_context.timings);
if let Some(mut source_program) = source_program {
let rent = invoke_context.get_sysvar_cache().get_rent()?;
Expand All @@ -478,6 +468,20 @@ pub fn process_instruction_deploy(
let state = get_state_mut(program.get_data_mut()?)?;
state.slot = current_slot;
state.is_deployed = true;

if let Some(old_entry) = invoke_context.find_program_in_cache(program.get_key()) {
executor.tx_usage_counter.store(
old_entry.tx_usage_counter.load(Ordering::Relaxed),
Ordering::Relaxed,
);
executor.ix_usage_counter.store(
old_entry.ix_usage_counter.load(Ordering::Relaxed),
Ordering::Relaxed,
);
}
invoke_context
.programs_modified_by_tx
.replenish(*program.get_key(), Arc::new(executor));
Ok(())
}

Expand Down Expand Up @@ -602,14 +606,13 @@ pub fn process_instruction_inner(
return Err(Box::new(InstructionError::InvalidArgument));
}
let mut get_or_create_executor_time = Measure::start("get_or_create_executor_time");
let (loaded_program, load_program_metrics) = load_program_from_account(
&invoke_context.feature_set,
invoke_context.get_compute_budget(),
invoke_context.get_log_collector(),
&program,
false, /* debugging_features */
)?;
load_program_metrics.submit_datapoint(&mut invoke_context.timings);
let loaded_program = invoke_context
.find_program_in_cache(program.get_key())
.ok_or(InstructionError::InvalidAccountData)?;

if loaded_program.is_tombstone() {
return Err(Box::new(InstructionError::InvalidAccountData));
}
get_or_create_executor_time.stop();
saturating_add_assign!(
invoke_context.timings.get_or_create_executor_us,
Expand Down Expand Up @@ -651,6 +654,50 @@ mod tests {
std::{fs::File, io::Read, path::Path},
};

pub fn load_all_invoked_programs(invoke_context: &mut InvokeContext) {
let mut load_program_metrics = LoadProgramMetrics::default();
let num_accounts = invoke_context.transaction_context.get_number_of_accounts();
for index in 0..num_accounts {
let account = invoke_context
.transaction_context
.get_account_at_index(index)
.expect("Failed to get the account")
.borrow();

let owner = account.owner();
if loader_v4::check_id(owner) {
let pubkey = invoke_context
.transaction_context
.get_key_of_account_at_index(index)
.expect("Failed to get account key");

if let Some(programdata) =
account.data().get(LoaderV4State::program_data_offset()..)
{
if let Ok(loaded_program) = LoadedProgram::new(
&loader_v4::id(),
invoke_context
.programs_modified_by_tx
.environments
.program_runtime_v2
.clone(),
0,
0,
None,
programdata,
account.data().len(),
&mut load_program_metrics,
) {
invoke_context.programs_modified_by_tx.set_slot_for_tests(0);
invoke_context
.programs_modified_by_tx
.replenish(*pubkey, Arc::new(loaded_program));
}
}
}
}
}

fn process_instruction(
program_indices: Vec<IndexOfAccount>,
instruction_data: &[u8],
Expand All @@ -676,7 +723,16 @@ mod tests {
instruction_accounts,
expected_result,
super::process_instruction,
|_invoke_context| {},
|invoke_context| {
invoke_context
.programs_modified_by_tx
.environments
.program_runtime_v2 = Arc::new(create_program_runtime_environment_v2(
&ComputeBudget::default(),
false,
));
load_all_invoked_programs(invoke_context);
},
|_invoke_context| {},
)
}
Expand Down Expand Up @@ -1447,7 +1503,7 @@ mod tests {
load_program_account_from_elf(false, None, "rodata"),
),
(
program_address,
Pubkey::new_unique(),
load_program_account_from_elf(true, None, "invalid"),
),
];
Expand Down
Loading