Refactor - Remove `first_instruction_account` in `bpf_loader` (#30614)
* Removes first_instruction_account from process_loader_upgradeable_instruction(), process_loader_instruction() and write_program_data(). * Removes first_instruction_account from process_instruction_common(). * Removes get_index_in_transaction() and try_borrow_account().
This commit is contained in:
parent
d8a6953eb2
commit
d2fc2e5f62
|
@ -86,40 +86,6 @@ pub enum BpfError {
|
||||||
}
|
}
|
||||||
impl UserDefinedError for BpfError {}
|
impl UserDefinedError for BpfError {}
|
||||||
|
|
||||||
// The BPF loader is special in that it is the only place in the runtime and its built-in programs,
|
|
||||||
// where data comes not only from instruction account but also program accounts.
|
|
||||||
// Thus, these two helper methods have to distinguish the mixed sources via index_in_instruction.
|
|
||||||
|
|
||||||
fn get_index_in_transaction(
|
|
||||||
instruction_context: &InstructionContext,
|
|
||||||
index_in_instruction: IndexOfAccount,
|
|
||||||
) -> Result<IndexOfAccount, InstructionError> {
|
|
||||||
if index_in_instruction < instruction_context.get_number_of_program_accounts() {
|
|
||||||
instruction_context.get_index_of_program_account_in_transaction(index_in_instruction)
|
|
||||||
} else {
|
|
||||||
instruction_context.get_index_of_instruction_account_in_transaction(
|
|
||||||
index_in_instruction
|
|
||||||
.saturating_sub(instruction_context.get_number_of_program_accounts()),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_borrow_account<'a>(
|
|
||||||
transaction_context: &'a TransactionContext,
|
|
||||||
instruction_context: &'a InstructionContext,
|
|
||||||
index_in_instruction: IndexOfAccount,
|
|
||||||
) -> Result<BorrowedAccount<'a>, InstructionError> {
|
|
||||||
if index_in_instruction < instruction_context.get_number_of_program_accounts() {
|
|
||||||
instruction_context.try_borrow_program_account(transaction_context, index_in_instruction)
|
|
||||||
} else {
|
|
||||||
instruction_context.try_borrow_instruction_account(
|
|
||||||
transaction_context,
|
|
||||||
index_in_instruction
|
|
||||||
.saturating_sub(instruction_context.get_number_of_program_accounts()),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn load_program_from_bytes(
|
pub fn load_program_from_bytes(
|
||||||
feature_set: &FeatureSet,
|
feature_set: &FeatureSet,
|
||||||
|
@ -299,18 +265,13 @@ macro_rules! deploy_program {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_program_data(
|
fn write_program_data(
|
||||||
program_account_index: IndexOfAccount,
|
|
||||||
program_data_offset: usize,
|
program_data_offset: usize,
|
||||||
bytes: &[u8],
|
bytes: &[u8],
|
||||||
invoke_context: &mut InvokeContext,
|
invoke_context: &mut InvokeContext,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
let transaction_context = &invoke_context.transaction_context;
|
let transaction_context = &invoke_context.transaction_context;
|
||||||
let instruction_context = transaction_context.get_current_instruction_context()?;
|
let instruction_context = transaction_context.get_current_instruction_context()?;
|
||||||
let mut program = try_borrow_account(
|
let mut program = instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
|
||||||
transaction_context,
|
|
||||||
instruction_context,
|
|
||||||
program_account_index,
|
|
||||||
)?;
|
|
||||||
let data = program.get_data_mut()?;
|
let data = program.get_data_mut()?;
|
||||||
let write_offset = program_data_offset.saturating_add(bytes.len());
|
let write_offset = program_data_offset.saturating_add(bytes.len());
|
||||||
if data.len() < write_offset {
|
if data.len() < write_offset {
|
||||||
|
@ -396,120 +357,80 @@ fn process_instruction_common(
|
||||||
let log_collector = invoke_context.get_log_collector();
|
let log_collector = invoke_context.get_log_collector();
|
||||||
let transaction_context = &invoke_context.transaction_context;
|
let transaction_context = &invoke_context.transaction_context;
|
||||||
let instruction_context = transaction_context.get_current_instruction_context()?;
|
let instruction_context = transaction_context.get_current_instruction_context()?;
|
||||||
let program_id = instruction_context.get_last_program_key(transaction_context)?;
|
let program_account =
|
||||||
|
instruction_context.try_borrow_last_program_account(transaction_context)?;
|
||||||
|
|
||||||
let first_instruction_account = {
|
// Program Management Instruction
|
||||||
let borrowed_root_account =
|
if native_loader::check_id(program_account.get_owner()) {
|
||||||
instruction_context.try_borrow_program_account(transaction_context, 0)?;
|
drop(program_account);
|
||||||
let owner_id = borrowed_root_account.get_owner();
|
if instruction_context
|
||||||
if native_loader::check_id(owner_id) {
|
.try_borrow_instruction_account(transaction_context, 0)
|
||||||
1
|
.map(|account| account.is_executable())
|
||||||
} else {
|
.unwrap_or(false)
|
||||||
0
|
{
|
||||||
}
|
|
||||||
};
|
|
||||||
let first_account_key = transaction_context.get_key_of_account_at_index(
|
|
||||||
get_index_in_transaction(instruction_context, first_instruction_account)?,
|
|
||||||
)?;
|
|
||||||
let second_account_key = get_index_in_transaction(
|
|
||||||
instruction_context,
|
|
||||||
first_instruction_account.saturating_add(1),
|
|
||||||
)
|
|
||||||
.and_then(|index_in_transaction| {
|
|
||||||
transaction_context.get_key_of_account_at_index(index_in_transaction)
|
|
||||||
});
|
|
||||||
|
|
||||||
let program_account_index = if first_account_key == program_id {
|
|
||||||
first_instruction_account
|
|
||||||
} else if second_account_key
|
|
||||||
.map(|key| key == program_id)
|
|
||||||
.unwrap_or(false)
|
|
||||||
{
|
|
||||||
first_instruction_account.saturating_add(1)
|
|
||||||
} else {
|
|
||||||
let first_account = try_borrow_account(
|
|
||||||
transaction_context,
|
|
||||||
instruction_context,
|
|
||||||
first_instruction_account,
|
|
||||||
)?;
|
|
||||||
if first_account.is_executable() {
|
|
||||||
ic_logger_msg!(log_collector, "BPF loader is executable");
|
ic_logger_msg!(log_collector, "BPF loader is executable");
|
||||||
return Err(InstructionError::IncorrectProgramId);
|
return Err(InstructionError::IncorrectProgramId);
|
||||||
}
|
}
|
||||||
first_instruction_account
|
let program_id = instruction_context.get_last_program_key(transaction_context)?;
|
||||||
};
|
return if bpf_loader_upgradeable::check_id(program_id) {
|
||||||
|
process_loader_upgradeable_instruction(invoke_context, use_jit)
|
||||||
let program = try_borrow_account(
|
|
||||||
transaction_context,
|
|
||||||
instruction_context,
|
|
||||||
program_account_index,
|
|
||||||
)?;
|
|
||||||
if program.is_executable() {
|
|
||||||
// First instruction account can only be zero if called from CPI, which
|
|
||||||
// means stack height better be greater than one
|
|
||||||
debug_assert_eq!(
|
|
||||||
first_instruction_account == 0,
|
|
||||||
invoke_context.get_stack_height() > 1
|
|
||||||
);
|
|
||||||
|
|
||||||
let programdata = if program_account_index == first_instruction_account {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(try_borrow_account(
|
|
||||||
transaction_context,
|
|
||||||
instruction_context,
|
|
||||||
first_instruction_account,
|
|
||||||
)?)
|
|
||||||
};
|
|
||||||
let mut get_or_create_executor_time = Measure::start("get_or_create_executor_time");
|
|
||||||
let (executor, load_program_metrics) = load_program_from_account(
|
|
||||||
&invoke_context.feature_set,
|
|
||||||
invoke_context.get_compute_budget(),
|
|
||||||
log_collector,
|
|
||||||
Some(invoke_context.tx_executor_cache.borrow_mut()),
|
|
||||||
&program,
|
|
||||||
programdata.as_ref().unwrap_or(&program),
|
|
||||||
use_jit,
|
|
||||||
)?;
|
|
||||||
drop(program);
|
|
||||||
drop(programdata);
|
|
||||||
get_or_create_executor_time.stop();
|
|
||||||
saturating_add_assign!(
|
|
||||||
invoke_context.timings.get_or_create_executor_us,
|
|
||||||
get_or_create_executor_time.as_us()
|
|
||||||
);
|
|
||||||
if let Some(load_program_metrics) = load_program_metrics {
|
|
||||||
load_program_metrics.submit_datapoint(&mut invoke_context.timings);
|
|
||||||
}
|
|
||||||
match &executor.program {
|
|
||||||
LoadedProgramType::Invalid => Err(InstructionError::InvalidAccountData),
|
|
||||||
LoadedProgramType::LegacyV0(executable) => execute(executable, invoke_context),
|
|
||||||
LoadedProgramType::LegacyV1(executable) => execute(executable, invoke_context),
|
|
||||||
LoadedProgramType::BuiltIn(_) => Err(InstructionError::IncorrectProgramId),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
drop(program);
|
|
||||||
debug_assert_eq!(first_instruction_account, 1);
|
|
||||||
if bpf_loader_upgradeable::check_id(program_id) {
|
|
||||||
process_loader_upgradeable_instruction(
|
|
||||||
first_instruction_account,
|
|
||||||
invoke_context,
|
|
||||||
use_jit,
|
|
||||||
)
|
|
||||||
} else if bpf_loader::check_id(program_id) {
|
} else if bpf_loader::check_id(program_id) {
|
||||||
process_loader_instruction(first_instruction_account, invoke_context, use_jit)
|
process_loader_instruction(invoke_context, use_jit)
|
||||||
} else if bpf_loader_deprecated::check_id(program_id) {
|
} else if bpf_loader_deprecated::check_id(program_id) {
|
||||||
ic_logger_msg!(log_collector, "Deprecated loader is no longer supported");
|
ic_logger_msg!(log_collector, "Deprecated loader is no longer supported");
|
||||||
Err(InstructionError::UnsupportedProgramId)
|
Err(InstructionError::UnsupportedProgramId)
|
||||||
} else {
|
} else {
|
||||||
ic_logger_msg!(log_collector, "Invalid BPF loader id");
|
ic_logger_msg!(log_collector, "Invalid BPF loader id");
|
||||||
Err(InstructionError::IncorrectProgramId)
|
Err(InstructionError::IncorrectProgramId)
|
||||||
}
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Program Invocation
|
||||||
|
if !program_account.is_executable() {
|
||||||
|
ic_logger_msg!(log_collector, "Program is not executable");
|
||||||
|
return Err(InstructionError::IncorrectProgramId);
|
||||||
|
}
|
||||||
|
let programdata_account = if bpf_loader_upgradeable::check_id(program_account.get_owner()) {
|
||||||
|
let programdata_account = instruction_context.try_borrow_program_account(
|
||||||
|
transaction_context,
|
||||||
|
instruction_context
|
||||||
|
.get_number_of_program_accounts()
|
||||||
|
.saturating_sub(2),
|
||||||
|
)?;
|
||||||
|
Some(programdata_account)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut get_or_create_executor_time = Measure::start("get_or_create_executor_time");
|
||||||
|
let (executor, load_program_metrics) = load_program_from_account(
|
||||||
|
&invoke_context.feature_set,
|
||||||
|
invoke_context.get_compute_budget(),
|
||||||
|
log_collector,
|
||||||
|
Some(invoke_context.tx_executor_cache.borrow_mut()),
|
||||||
|
&program_account,
|
||||||
|
programdata_account.as_ref().unwrap_or(&program_account),
|
||||||
|
use_jit,
|
||||||
|
)?;
|
||||||
|
drop(program_account);
|
||||||
|
drop(programdata_account);
|
||||||
|
get_or_create_executor_time.stop();
|
||||||
|
saturating_add_assign!(
|
||||||
|
invoke_context.timings.get_or_create_executor_us,
|
||||||
|
get_or_create_executor_time.as_us()
|
||||||
|
);
|
||||||
|
if let Some(load_program_metrics) = load_program_metrics {
|
||||||
|
load_program_metrics.submit_datapoint(&mut invoke_context.timings);
|
||||||
|
}
|
||||||
|
match &executor.program {
|
||||||
|
LoadedProgramType::Invalid => Err(InstructionError::InvalidAccountData),
|
||||||
|
LoadedProgramType::LegacyV0(executable) => execute(executable, invoke_context),
|
||||||
|
LoadedProgramType::LegacyV1(executable) => execute(executable, invoke_context),
|
||||||
|
LoadedProgramType::BuiltIn(_) => Err(InstructionError::IncorrectProgramId),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_loader_upgradeable_instruction(
|
fn process_loader_upgradeable_instruction(
|
||||||
first_instruction_account: IndexOfAccount,
|
|
||||||
invoke_context: &mut InvokeContext,
|
invoke_context: &mut InvokeContext,
|
||||||
use_jit: bool,
|
use_jit: bool,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
|
@ -565,7 +486,6 @@ fn process_loader_upgradeable_instruction(
|
||||||
}
|
}
|
||||||
drop(buffer);
|
drop(buffer);
|
||||||
write_program_data(
|
write_program_data(
|
||||||
first_instruction_account,
|
|
||||||
UpgradeableLoaderState::size_of_buffer_metadata().saturating_add(offset as usize),
|
UpgradeableLoaderState::size_of_buffer_metadata().saturating_add(offset as usize),
|
||||||
&bytes,
|
&bytes,
|
||||||
invoke_context,
|
invoke_context,
|
||||||
|
@ -1377,7 +1297,6 @@ fn common_close_account(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_loader_instruction(
|
fn process_loader_instruction(
|
||||||
first_instruction_account: IndexOfAccount,
|
|
||||||
invoke_context: &mut InvokeContext,
|
invoke_context: &mut InvokeContext,
|
||||||
use_jit: bool,
|
use_jit: bool,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
|
@ -1401,12 +1320,7 @@ fn process_loader_instruction(
|
||||||
return Err(InstructionError::MissingRequiredSignature);
|
return Err(InstructionError::MissingRequiredSignature);
|
||||||
}
|
}
|
||||||
drop(program);
|
drop(program);
|
||||||
write_program_data(
|
write_program_data(offset as usize, &bytes, invoke_context)?;
|
||||||
first_instruction_account,
|
|
||||||
offset as usize,
|
|
||||||
&bytes,
|
|
||||||
invoke_context,
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
LoaderInstruction::Finalize => {
|
LoaderInstruction::Finalize => {
|
||||||
if !is_program_signer {
|
if !is_program_signer {
|
||||||
|
|
Loading…
Reference in New Issue