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 3 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
31 changes: 22 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_environment_v1: Arc<BuiltinProgram<InvokeContext<'static>>>,
pgarg66 marked this conversation as resolved.
Show resolved Hide resolved
/// Globally shared RBPF config and syscall registry for runtime V2
pub program_runtime_environment_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,27 @@ impl LoadedPrograms {
LoadedProgramType::LegacyV0(program) | LoadedProgramType::LegacyV1(program)
if Arc::ptr_eq(
program.get_loader(),
&self.program_runtime_environment_v1,
&self.environments.program_runtime_environment_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_environment_v1,
) || Arc::ptr_eq(
environment,
&self.environments.program_runtime_environment_v2,
) =>
{
true
}
LoadedProgramType::Typed(program)
if Arc::ptr_eq(
program.get_loader(),
&self.program_runtime_environment_v2,
&self.environments.program_runtime_environment_v2,
) =>
{
true
Expand Down Expand Up @@ -654,6 +666,7 @@ impl LoadedPrograms {
LoadedProgramsForTxBatch {
entries: found,
slot: working_slot.current_slot(),
environments: self.environments.clone(),
},
missing,
)
Expand Down
176 changes: 121 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 @@ -412,6 +378,18 @@ pub fn process_instruction_truncate(
Ok(())
}

fn find_program_in_cache(
pgarg66 marked this conversation as resolved.
Show resolved Hide resolved
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))
}

pub fn process_instruction_deploy(
invoke_context: &mut InvokeContext,
) -> Result<(), InstructionError> {
Expand Down Expand Up @@ -458,13 +436,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_environment_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 +480,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) = find_program_in_cache(invoke_context, 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 +618,12 @@ 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 = find_program_in_cache(invoke_context, 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 +665,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_environment_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 +734,15 @@ mod tests {
instruction_accounts,
expected_result,
super::process_instruction,
|_invoke_context| {},
|invoke_context| {
invoke_context
.programs_modified_by_tx
.environments
.program_runtime_environment_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 +1513,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