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

Commit

Permalink
Integrate program loader-v4 with bank
Browse files Browse the repository at this point in the history
  • Loading branch information
pgarg66 committed Aug 14, 2023
1 parent 52616cf commit a81b695
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 60 deletions.
6 changes: 4 additions & 2 deletions ledger-tool/src/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -536,8 +536,10 @@ 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_from_cache(
bank.slot() + DELAY_VISIBILITY_SLOT_OFFSET,
&bank.loaded_programs_cache.read().unwrap(),
);
for key in cached_account_keys {
loaded_programs.replenish(key, bank.load_program(&key));
debug!("Loaded program {}", key);
Expand Down
23 changes: 22 additions & 1 deletion program-runtime/src/loaded_programs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -367,13 +367,32 @@ 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,
/// Runtime environment reference from LoadedProgram cache
pub program_runtime_environment_v1: Arc<BuiltinProgram<InvokeContext<'static>>>,
/// RuntimeV2 environment reference from LoadedProgram cache
pub program_runtime_environment_v2: Arc<BuiltinProgram<InvokeContext<'static>>>,
}

impl LoadedProgramsForTxBatch {
pub fn new(slot: Slot) -> Self {
pub fn new_from_cache(slot: Slot, loaded_programs_cache: &LoadedPrograms) -> Self {
Self {
entries: HashMap::new(),
slot,
program_runtime_environment_v1: loaded_programs_cache
.program_runtime_environment_v1
.clone(),
program_runtime_environment_v2: loaded_programs_cache
.program_runtime_environment_v2
.clone(),
}
}

pub fn new_from_tx_batch_cache(slot: Slot, tx_batch_cache: &LoadedProgramsForTxBatch) -> Self {
Self {
entries: HashMap::new(),
slot,
program_runtime_environment_v1: tx_batch_cache.program_runtime_environment_v1.clone(),
program_runtime_environment_v2: tx_batch_cache.program_runtime_environment_v2.clone(),
}
}

Expand Down Expand Up @@ -654,6 +673,8 @@ impl LoadedPrograms {
LoadedProgramsForTxBatch {
entries: found,
slot: working_slot.current_slot(),
program_runtime_environment_v1: self.program_runtime_environment_v1.clone(),
program_runtime_environment_v2: self.program_runtime_environment_v2.clone(),
},
missing,
)
Expand Down
119 changes: 66 additions & 53 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(
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,36 @@ 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
.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 +479,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 +617,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
61 changes: 57 additions & 4 deletions runtime/src/bank.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ use {
invoke_context::ProcessInstructionWithContext,
loaded_programs::{
LoadProgramMetrics, LoadedProgram, LoadedProgramMatchCriteria, LoadedProgramType,
LoadedPrograms, LoadedProgramsForTxBatch, WorkingSlot,
LoadedPrograms, LoadedProgramsForTxBatch, WorkingSlot, DELAY_VISIBILITY_SLOT_OFFSET,
},
log_collector::LogCollector,
message_processor::MessageProcessor,
Expand Down Expand Up @@ -150,7 +150,7 @@ use {
inflation::Inflation,
instruction::InstructionError,
lamports::LamportsError,
loader_v4,
loader_v4::{self, LoaderV4State},
message::{AccountKeys, SanitizedMessage},
native_loader,
native_token::LAMPORTS_PER_SOL,
Expand Down Expand Up @@ -304,8 +304,10 @@ impl BankRc {
enum ProgramAccountLoadResult {
AccountNotFound,
InvalidAccountData,
InvalidV4Program,
ProgramOfLoaderV1orV2(AccountSharedData),
ProgramOfLoaderV3(AccountSharedData, AccountSharedData, Slot),
ProgramOfLoaderV4(AccountSharedData, Slot),
}

pub struct LoadAndExecuteTransactionsOutput {
Expand Down Expand Up @@ -4601,6 +4603,14 @@ impl Bank {
program_account.owner()
));

if loader_v4::check_id(program_account.owner()) {
return solana_loader_v4_program::get_state(program_account.data())
.ok()
.and_then(|state| state.is_deployed.then_some(state.slot))
.map(|slot| ProgramAccountLoadResult::ProgramOfLoaderV4(program_account, slot))
.unwrap_or(ProgramAccountLoadResult::InvalidV4Program);
}

if !bpf_loader_upgradeable::check_id(program_account.owner()) {
return ProgramAccountLoadResult::ProgramOfLoaderV1orV2(program_account);
}
Expand Down Expand Up @@ -4637,6 +4647,13 @@ impl Bank {
.program_runtime_environment_v1
.clone();

let program_runtime_environment_v2 = self
.loaded_programs_cache
.read()
.unwrap()
.program_runtime_environment_v2
.clone();

let mut load_program_metrics = LoadProgramMetrics {
program_id: pubkey.to_string(),
..LoadProgramMetrics::default()
Expand Down Expand Up @@ -4688,6 +4705,35 @@ impl Bank {
program_runtime_environment_v1.clone(),
)
}),

ProgramAccountLoadResult::ProgramOfLoaderV4(program_account, slot) => {
let loaded_program = program_account
.data()
.get(LoaderV4State::program_data_offset()..)
.and_then(|elf_bytes| {
LoadedProgram::new(
&loader_v4::id(),
program_runtime_environment_v2.clone(),
slot,
slot.saturating_add(DELAY_VISIBILITY_SLOT_OFFSET),
None,
elf_bytes,
program_account.data().len(),
&mut load_program_metrics,
)
.ok()
})
.unwrap_or(LoadedProgram::new_tombstone(
self.slot,
LoadedProgramType::FailedVerification(program_runtime_environment_v2),
));
Ok(loaded_program)
}

ProgramAccountLoadResult::InvalidV4Program => Ok(LoadedProgram::new_tombstone(
self.slot,
LoadedProgramType::FailedVerification(program_runtime_environment_v2),
)),
}
.unwrap_or_else(|_| {
LoadedProgram::new_tombstone(
Expand Down Expand Up @@ -4773,8 +4819,15 @@ impl Bank {
let (blockhash, lamports_per_signature) = self.last_blockhash_and_lamports_per_signature();

let mut executed_units = 0u64;
let mut programs_modified_by_tx = LoadedProgramsForTxBatch::new(self.slot);
let mut programs_updated_only_for_global_cache = LoadedProgramsForTxBatch::new(self.slot);
let mut programs_modified_by_tx = LoadedProgramsForTxBatch::new_from_tx_batch_cache(
self.slot,
programs_loaded_for_tx_batch,
);
let mut programs_updated_only_for_global_cache =
LoadedProgramsForTxBatch::new_from_tx_batch_cache(
self.slot,
programs_loaded_for_tx_batch,
);
let mut process_message_time = Measure::start("process_message_time");
let process_result = MessageProcessor::process_message(
tx.message(),
Expand Down

0 comments on commit a81b695

Please sign in to comment.