2023-03-23 10:13:47 -07:00
|
|
|
use {
|
|
|
|
solana_measure::measure::Measure,
|
|
|
|
solana_program_runtime::{
|
|
|
|
compute_budget::ComputeBudget,
|
|
|
|
ic_logger_msg,
|
|
|
|
invoke_context::InvokeContext,
|
2023-08-16 10:50:23 -07:00
|
|
|
loaded_programs::{
|
|
|
|
LoadProgramMetrics, LoadedProgram, LoadedProgramType, DELAY_VISIBILITY_SLOT_OFFSET,
|
|
|
|
},
|
2023-03-23 10:13:47 -07:00
|
|
|
log_collector::LogCollector,
|
|
|
|
stable_log,
|
|
|
|
},
|
|
|
|
solana_rbpf::{
|
|
|
|
aligned_memory::AlignedMemory,
|
|
|
|
ebpf,
|
2023-09-06 01:54:15 -07:00
|
|
|
elf::{Executable, FunctionRegistry},
|
2023-03-23 10:13:47 -07:00
|
|
|
memory_region::{MemoryMapping, MemoryRegion},
|
2023-09-06 01:54:15 -07:00
|
|
|
vm::{BuiltinProgram, Config, ContextObject, EbpfVm, ProgramResult},
|
2023-03-23 10:13:47 -07:00
|
|
|
},
|
|
|
|
solana_sdk::{
|
2023-09-21 07:24:47 -07:00
|
|
|
entrypoint::SUCCESS,
|
2023-08-16 10:50:23 -07:00
|
|
|
feature_set,
|
2023-03-23 10:13:47 -07:00
|
|
|
instruction::InstructionError,
|
2023-08-28 11:14:01 -07:00
|
|
|
loader_v4::{self, LoaderV4State, LoaderV4Status, DEPLOYMENT_COOLDOWN_IN_SLOTS},
|
2023-05-10 23:29:06 -07:00
|
|
|
loader_v4_instruction::LoaderV4Instruction,
|
2023-03-23 10:13:47 -07:00
|
|
|
program_utils::limited_deserialize,
|
|
|
|
pubkey::Pubkey,
|
|
|
|
saturating_add_assign,
|
|
|
|
transaction_context::{BorrowedAccount, InstructionContext},
|
|
|
|
},
|
2023-03-27 06:48:05 -07:00
|
|
|
std::{
|
|
|
|
cell::RefCell,
|
|
|
|
rc::Rc,
|
|
|
|
sync::{atomic::Ordering, Arc},
|
|
|
|
},
|
2023-03-23 10:13:47 -07:00
|
|
|
};
|
|
|
|
|
2023-06-02 08:22:22 -07:00
|
|
|
pub const DEFAULT_COMPUTE_UNITS: u64 = 2_000;
|
|
|
|
|
2023-05-10 23:29:06 -07:00
|
|
|
pub fn get_state(data: &[u8]) -> Result<&LoaderV4State, InstructionError> {
|
2023-03-23 10:13:47 -07:00
|
|
|
unsafe {
|
|
|
|
let data = data
|
2023-05-10 23:29:06 -07:00
|
|
|
.get(0..LoaderV4State::program_data_offset())
|
2023-03-23 10:13:47 -07:00
|
|
|
.ok_or(InstructionError::AccountDataTooSmall)?
|
|
|
|
.try_into()
|
|
|
|
.unwrap();
|
|
|
|
Ok(std::mem::transmute::<
|
2023-05-10 23:29:06 -07:00
|
|
|
&[u8; LoaderV4State::program_data_offset()],
|
|
|
|
&LoaderV4State,
|
2023-03-23 10:13:47 -07:00
|
|
|
>(data))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-10 23:29:06 -07:00
|
|
|
fn get_state_mut(data: &mut [u8]) -> Result<&mut LoaderV4State, InstructionError> {
|
2023-03-23 10:13:47 -07:00
|
|
|
unsafe {
|
|
|
|
let data = data
|
2023-05-10 23:29:06 -07:00
|
|
|
.get_mut(0..LoaderV4State::program_data_offset())
|
2023-03-23 10:13:47 -07:00
|
|
|
.ok_or(InstructionError::AccountDataTooSmall)?
|
|
|
|
.try_into()
|
|
|
|
.unwrap();
|
|
|
|
Ok(std::mem::transmute::<
|
2023-05-10 23:29:06 -07:00
|
|
|
&mut [u8; LoaderV4State::program_data_offset()],
|
|
|
|
&mut LoaderV4State,
|
2023-03-23 10:13:47 -07:00
|
|
|
>(data))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-11 11:58:22 -07:00
|
|
|
pub fn create_program_runtime_environment_v2<'a>(
|
2023-03-23 10:13:47 -07:00
|
|
|
compute_budget: &ComputeBudget,
|
|
|
|
debugging_features: bool,
|
2023-08-11 11:58:22 -07:00
|
|
|
) -> BuiltinProgram<InvokeContext<'a>> {
|
2023-03-23 10:13:47 -07:00
|
|
|
let config = Config {
|
|
|
|
max_call_depth: compute_budget.max_call_depth,
|
|
|
|
stack_frame_size: compute_budget.stack_frame_size,
|
2023-05-12 09:07:13 -07:00
|
|
|
enable_address_translation: true, // To be deactivated once we have BTF inference and verification
|
2023-03-23 10:13:47 -07:00
|
|
|
enable_stack_frame_gaps: false,
|
|
|
|
instruction_meter_checkpoint_distance: 10000,
|
|
|
|
enable_instruction_meter: true,
|
|
|
|
enable_instruction_tracing: debugging_features,
|
|
|
|
enable_symbol_and_section_labels: debugging_features,
|
|
|
|
reject_broken_elfs: true,
|
|
|
|
noop_instruction_rate: 256,
|
|
|
|
sanitize_user_provided_values: true,
|
2023-09-06 01:54:15 -07:00
|
|
|
encrypt_runtime_environment: true,
|
2023-03-23 10:13:47 -07:00
|
|
|
external_internal_function_hash_collision: true,
|
|
|
|
reject_callx_r10: true,
|
2023-07-05 10:46:21 -07:00
|
|
|
enable_sbpf_v1: false,
|
|
|
|
enable_sbpf_v2: true,
|
2023-03-23 10:13:47 -07:00
|
|
|
optimize_rodata: true,
|
|
|
|
new_elf_parser: true,
|
|
|
|
aligned_memory_mapping: true,
|
|
|
|
// Warning, do not use `Config::default()` so that configuration here is explicit.
|
|
|
|
};
|
2023-09-06 01:54:15 -07:00
|
|
|
BuiltinProgram::new_loader(config, FunctionRegistry::default())
|
2023-08-11 11:58:22 -07:00
|
|
|
}
|
|
|
|
|
2023-03-23 10:13:47 -07:00
|
|
|
fn calculate_heap_cost(heap_size: u64, heap_cost: u64) -> u64 {
|
|
|
|
const KIBIBYTE: u64 = 1024;
|
|
|
|
const PAGE_SIZE_KB: u64 = 32;
|
|
|
|
heap_size
|
|
|
|
.saturating_add(PAGE_SIZE_KB.saturating_mul(KIBIBYTE).saturating_sub(1))
|
2023-08-29 11:58:53 -07:00
|
|
|
.checked_div(PAGE_SIZE_KB.saturating_mul(KIBIBYTE))
|
|
|
|
.expect("PAGE_SIZE_KB * KIBIBYTE > 0")
|
2023-03-23 10:13:47 -07:00
|
|
|
.saturating_sub(1)
|
|
|
|
.saturating_mul(heap_cost)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create the SBF virtual machine
|
|
|
|
pub fn create_vm<'a, 'b>(
|
|
|
|
invoke_context: &'a mut InvokeContext<'b>,
|
2023-09-06 01:54:15 -07:00
|
|
|
program: &'a Executable<InvokeContext<'b>>,
|
2023-07-05 10:46:21 -07:00
|
|
|
) -> Result<EbpfVm<'a, InvokeContext<'b>>, Box<dyn std::error::Error>> {
|
2023-05-12 09:07:13 -07:00
|
|
|
let config = program.get_config();
|
2023-07-05 10:46:21 -07:00
|
|
|
let sbpf_version = program.get_sbpf_version();
|
2023-03-23 10:13:47 -07:00
|
|
|
let compute_budget = invoke_context.get_compute_budget();
|
2023-09-21 07:24:47 -07:00
|
|
|
let heap_size = compute_budget.heap_size;
|
2023-03-23 10:13:47 -07:00
|
|
|
invoke_context.consume_checked(calculate_heap_cost(
|
|
|
|
heap_size as u64,
|
|
|
|
compute_budget.heap_cost,
|
|
|
|
))?;
|
|
|
|
let mut stack = AlignedMemory::<{ ebpf::HOST_ALIGN }>::zero_filled(config.stack_size());
|
2023-09-21 07:24:47 -07:00
|
|
|
let mut heap = AlignedMemory::<{ ebpf::HOST_ALIGN }>::zero_filled(compute_budget.heap_size);
|
2023-03-23 10:13:47 -07:00
|
|
|
let stack_len = stack.len();
|
|
|
|
let regions: Vec<MemoryRegion> = vec![
|
2023-05-12 09:07:13 -07:00
|
|
|
program.get_ro_region(),
|
2023-03-23 10:13:47 -07:00
|
|
|
MemoryRegion::new_writable_gapped(stack.as_slice_mut(), ebpf::MM_STACK_START, 0),
|
|
|
|
MemoryRegion::new_writable(heap.as_slice_mut(), ebpf::MM_HEAP_START),
|
|
|
|
];
|
|
|
|
let log_collector = invoke_context.get_log_collector();
|
2023-07-05 10:46:21 -07:00
|
|
|
let memory_mapping = MemoryMapping::new(regions, config, sbpf_version).map_err(|err| {
|
2023-04-05 06:50:34 -07:00
|
|
|
ic_logger_msg!(log_collector, "Failed to create SBF VM: {}", err);
|
|
|
|
Box::new(InstructionError::ProgramEnvironmentSetupFailure)
|
|
|
|
})?;
|
|
|
|
Ok(EbpfVm::new(
|
2023-07-05 10:46:21 -07:00
|
|
|
config,
|
|
|
|
sbpf_version,
|
2023-04-05 06:50:34 -07:00
|
|
|
invoke_context,
|
|
|
|
memory_mapping,
|
|
|
|
stack_len,
|
|
|
|
))
|
2023-03-23 10:13:47 -07:00
|
|
|
}
|
|
|
|
|
2023-07-05 10:46:21 -07:00
|
|
|
fn execute<'a, 'b: 'a>(
|
|
|
|
invoke_context: &'a mut InvokeContext<'b>,
|
2023-09-06 01:54:15 -07:00
|
|
|
executable: &'a Executable<InvokeContext<'static>>,
|
2023-04-05 06:50:34 -07:00
|
|
|
) -> Result<(), Box<dyn std::error::Error>> {
|
2023-07-05 10:46:21 -07:00
|
|
|
// We dropped the lifetime tracking in the Executor by setting it to 'static,
|
|
|
|
// thus we need to reintroduce the correct lifetime of InvokeContext here again.
|
2023-09-06 01:54:15 -07:00
|
|
|
let executable =
|
|
|
|
unsafe { std::mem::transmute::<_, &'a Executable<InvokeContext<'b>>>(executable) };
|
2023-03-23 10:13:47 -07:00
|
|
|
let log_collector = invoke_context.get_log_collector();
|
|
|
|
let stack_height = invoke_context.get_stack_height();
|
|
|
|
let transaction_context = &invoke_context.transaction_context;
|
|
|
|
let instruction_context = transaction_context.get_current_instruction_context()?;
|
|
|
|
let program_id = *instruction_context.get_last_program_key(transaction_context)?;
|
|
|
|
#[cfg(any(target_os = "windows", not(target_arch = "x86_64")))]
|
|
|
|
let use_jit = false;
|
|
|
|
#[cfg(all(not(target_os = "windows"), target_arch = "x86_64"))]
|
2023-07-05 10:46:21 -07:00
|
|
|
let use_jit = executable.get_compiled_program().is_some();
|
2023-03-23 10:13:47 -07:00
|
|
|
|
|
|
|
let compute_meter_prev = invoke_context.get_remaining();
|
|
|
|
let mut create_vm_time = Measure::start("create_vm");
|
2023-07-05 10:46:21 -07:00
|
|
|
let mut vm = create_vm(invoke_context, executable)?;
|
2023-03-23 10:13:47 -07:00
|
|
|
create_vm_time.stop();
|
|
|
|
|
|
|
|
let mut execute_time = Measure::start("execute");
|
|
|
|
stable_log::program_invoke(&log_collector, &program_id, stack_height);
|
2023-07-05 10:46:21 -07:00
|
|
|
let (compute_units_consumed, result) = vm.execute_program(executable, !use_jit);
|
2023-03-23 10:13:47 -07:00
|
|
|
drop(vm);
|
|
|
|
ic_logger_msg!(
|
|
|
|
log_collector,
|
|
|
|
"Program {} consumed {} of {} compute units",
|
|
|
|
&program_id,
|
|
|
|
compute_units_consumed,
|
|
|
|
compute_meter_prev
|
|
|
|
);
|
|
|
|
execute_time.stop();
|
|
|
|
|
|
|
|
let timings = &mut invoke_context.timings;
|
|
|
|
timings.create_vm_us = timings.create_vm_us.saturating_add(create_vm_time.as_us());
|
|
|
|
timings.execute_us = timings.execute_us.saturating_add(execute_time.as_us());
|
|
|
|
|
|
|
|
match result {
|
|
|
|
ProgramResult::Ok(status) if status != SUCCESS => {
|
2023-04-05 06:50:34 -07:00
|
|
|
let error: InstructionError = status.into();
|
|
|
|
Err(Box::new(error) as Box<dyn std::error::Error>)
|
2023-03-23 10:13:47 -07:00
|
|
|
}
|
2023-04-05 06:50:34 -07:00
|
|
|
ProgramResult::Err(error) => Err(error),
|
2023-03-23 10:13:47 -07:00
|
|
|
_ => Ok(()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn check_program_account(
|
|
|
|
log_collector: &Option<Rc<RefCell<LogCollector>>>,
|
|
|
|
instruction_context: &InstructionContext,
|
|
|
|
program: &BorrowedAccount,
|
|
|
|
authority_address: &Pubkey,
|
2023-05-10 23:29:06 -07:00
|
|
|
) -> Result<LoaderV4State, InstructionError> {
|
|
|
|
if !loader_v4::check_id(program.get_owner()) {
|
2023-03-23 10:13:47 -07:00
|
|
|
ic_logger_msg!(log_collector, "Program not owned by loader");
|
|
|
|
return Err(InstructionError::InvalidAccountOwner);
|
|
|
|
}
|
|
|
|
if program.get_data().is_empty() {
|
|
|
|
ic_logger_msg!(log_collector, "Program is uninitialized");
|
|
|
|
return Err(InstructionError::InvalidAccountData);
|
|
|
|
}
|
|
|
|
let state = get_state(program.get_data())?;
|
|
|
|
if !program.is_writable() {
|
|
|
|
ic_logger_msg!(log_collector, "Program is not writeable");
|
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
|
|
|
if !instruction_context.is_instruction_account_signer(1)? {
|
|
|
|
ic_logger_msg!(log_collector, "Authority did not sign");
|
|
|
|
return Err(InstructionError::MissingRequiredSignature);
|
|
|
|
}
|
2023-08-28 11:14:01 -07:00
|
|
|
if state.authority_address != *authority_address {
|
2023-03-23 10:13:47 -07:00
|
|
|
ic_logger_msg!(log_collector, "Incorrect authority provided");
|
|
|
|
return Err(InstructionError::IncorrectAuthority);
|
|
|
|
}
|
2023-08-28 11:14:01 -07:00
|
|
|
if matches!(state.status, LoaderV4Status::Finalized) {
|
|
|
|
ic_logger_msg!(log_collector, "Program is finalized");
|
|
|
|
return Err(InstructionError::Immutable);
|
|
|
|
}
|
2023-03-23 10:13:47 -07:00
|
|
|
Ok(*state)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn process_instruction_write(
|
|
|
|
invoke_context: &mut InvokeContext,
|
|
|
|
offset: u32,
|
|
|
|
bytes: Vec<u8>,
|
|
|
|
) -> Result<(), InstructionError> {
|
|
|
|
let log_collector = invoke_context.get_log_collector();
|
|
|
|
let transaction_context = &invoke_context.transaction_context;
|
|
|
|
let instruction_context = transaction_context.get_current_instruction_context()?;
|
|
|
|
let mut program = instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
|
|
|
|
let authority_address = instruction_context
|
|
|
|
.get_index_of_instruction_account_in_transaction(1)
|
|
|
|
.and_then(|index| transaction_context.get_key_of_account_at_index(index))?;
|
2023-08-30 15:44:58 -07:00
|
|
|
let state = check_program_account(
|
|
|
|
&log_collector,
|
|
|
|
instruction_context,
|
|
|
|
&program,
|
|
|
|
authority_address,
|
|
|
|
)?;
|
|
|
|
if !matches!(state.status, LoaderV4Status::Retracted) {
|
|
|
|
ic_logger_msg!(log_collector, "Program is not retracted");
|
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
|
|
|
let end_offset = (offset as usize).saturating_add(bytes.len());
|
|
|
|
program
|
|
|
|
.get_data_mut()?
|
|
|
|
.get_mut(
|
|
|
|
LoaderV4State::program_data_offset().saturating_add(offset as usize)
|
|
|
|
..LoaderV4State::program_data_offset().saturating_add(end_offset),
|
|
|
|
)
|
|
|
|
.ok_or_else(|| {
|
|
|
|
ic_logger_msg!(log_collector, "Write out of bounds");
|
|
|
|
InstructionError::AccountDataTooSmall
|
|
|
|
})?
|
|
|
|
.copy_from_slice(&bytes);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn process_instruction_truncate(
|
|
|
|
invoke_context: &mut InvokeContext,
|
|
|
|
new_size: u32,
|
|
|
|
) -> Result<(), InstructionError> {
|
|
|
|
let log_collector = invoke_context.get_log_collector();
|
|
|
|
let transaction_context = &invoke_context.transaction_context;
|
|
|
|
let instruction_context = transaction_context.get_current_instruction_context()?;
|
|
|
|
let mut program = instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
|
|
|
|
let authority_address = instruction_context
|
|
|
|
.get_index_of_instruction_account_in_transaction(1)
|
|
|
|
.and_then(|index| transaction_context.get_key_of_account_at_index(index))?;
|
|
|
|
let is_initialization =
|
|
|
|
new_size > 0 && program.get_data().len() < LoaderV4State::program_data_offset();
|
2023-03-23 10:13:47 -07:00
|
|
|
if is_initialization {
|
2023-05-10 23:29:06 -07:00
|
|
|
if !loader_v4::check_id(program.get_owner()) {
|
2023-03-23 10:13:47 -07:00
|
|
|
ic_logger_msg!(log_collector, "Program not owned by loader");
|
|
|
|
return Err(InstructionError::InvalidAccountOwner);
|
|
|
|
}
|
|
|
|
if !program.is_writable() {
|
|
|
|
ic_logger_msg!(log_collector, "Program is not writeable");
|
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
2023-08-30 15:44:58 -07:00
|
|
|
if !program.is_signer() {
|
|
|
|
ic_logger_msg!(log_collector, "Program did not sign");
|
|
|
|
return Err(InstructionError::MissingRequiredSignature);
|
|
|
|
}
|
2023-03-23 10:13:47 -07:00
|
|
|
if !instruction_context.is_instruction_account_signer(1)? {
|
|
|
|
ic_logger_msg!(log_collector, "Authority did not sign");
|
|
|
|
return Err(InstructionError::MissingRequiredSignature);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
let state = check_program_account(
|
|
|
|
&log_collector,
|
|
|
|
instruction_context,
|
|
|
|
&program,
|
|
|
|
authority_address,
|
|
|
|
)?;
|
2023-08-28 11:14:01 -07:00
|
|
|
if !matches!(state.status, LoaderV4Status::Retracted) {
|
2023-03-23 10:13:47 -07:00
|
|
|
ic_logger_msg!(log_collector, "Program is not retracted");
|
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
|
|
|
}
|
2023-08-30 15:44:58 -07:00
|
|
|
let required_lamports = if new_size == 0 {
|
|
|
|
0
|
|
|
|
} else {
|
|
|
|
let rent = invoke_context.get_sysvar_cache().get_rent()?;
|
|
|
|
rent.minimum_balance(LoaderV4State::program_data_offset().saturating_add(new_size as usize))
|
|
|
|
};
|
|
|
|
match program.get_lamports().cmp(&required_lamports) {
|
|
|
|
std::cmp::Ordering::Less => {
|
2023-03-23 10:13:47 -07:00
|
|
|
ic_logger_msg!(
|
|
|
|
log_collector,
|
|
|
|
"Insufficient lamports, {} are required",
|
|
|
|
required_lamports
|
|
|
|
);
|
|
|
|
return Err(InstructionError::InsufficientFunds);
|
|
|
|
}
|
2023-08-30 15:44:58 -07:00
|
|
|
std::cmp::Ordering::Greater => {
|
|
|
|
let mut recipient =
|
|
|
|
instruction_context.try_borrow_instruction_account(transaction_context, 2)?;
|
|
|
|
if !instruction_context.is_instruction_account_writable(2)? {
|
|
|
|
ic_logger_msg!(log_collector, "Recipient is not writeable");
|
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
|
|
|
let lamports_to_receive = program.get_lamports().saturating_sub(required_lamports);
|
|
|
|
program.checked_sub_lamports(lamports_to_receive)?;
|
|
|
|
recipient.checked_add_lamports(lamports_to_receive)?;
|
|
|
|
}
|
|
|
|
std::cmp::Ordering::Equal => {}
|
2023-03-23 10:13:47 -07:00
|
|
|
}
|
2023-08-30 15:44:58 -07:00
|
|
|
if new_size == 0 {
|
2023-03-23 10:13:47 -07:00
|
|
|
program.set_data_length(0)?;
|
|
|
|
} else {
|
|
|
|
program.set_data_length(
|
2023-08-30 15:44:58 -07:00
|
|
|
LoaderV4State::program_data_offset().saturating_add(new_size as usize),
|
2023-03-23 10:13:47 -07:00
|
|
|
)?;
|
2023-08-30 15:44:58 -07:00
|
|
|
if is_initialization {
|
|
|
|
let state = get_state_mut(program.get_data_mut()?)?;
|
2023-09-16 13:11:47 -07:00
|
|
|
state.slot = 0;
|
2023-08-30 15:44:58 -07:00
|
|
|
state.status = LoaderV4Status::Retracted;
|
|
|
|
state.authority_address = *authority_address;
|
|
|
|
}
|
|
|
|
}
|
2023-03-23 10:13:47 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn process_instruction_deploy(
|
|
|
|
invoke_context: &mut InvokeContext,
|
|
|
|
) -> Result<(), InstructionError> {
|
|
|
|
let log_collector = invoke_context.get_log_collector();
|
|
|
|
let transaction_context = &invoke_context.transaction_context;
|
|
|
|
let instruction_context = transaction_context.get_current_instruction_context()?;
|
|
|
|
let mut program = instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
|
|
|
|
let authority_address = instruction_context
|
|
|
|
.get_index_of_instruction_account_in_transaction(1)
|
|
|
|
.and_then(|index| transaction_context.get_key_of_account_at_index(index))?;
|
|
|
|
let source_program = instruction_context
|
|
|
|
.try_borrow_instruction_account(transaction_context, 2)
|
|
|
|
.ok();
|
|
|
|
let state = check_program_account(
|
|
|
|
&log_collector,
|
|
|
|
instruction_context,
|
|
|
|
&program,
|
|
|
|
authority_address,
|
|
|
|
)?;
|
|
|
|
let current_slot = invoke_context.get_sysvar_cache().get_clock()?.slot;
|
|
|
|
if state.slot.saturating_add(DEPLOYMENT_COOLDOWN_IN_SLOTS) > current_slot {
|
|
|
|
ic_logger_msg!(
|
|
|
|
log_collector,
|
|
|
|
"Program was deployed recently, cooldown still in effect"
|
|
|
|
);
|
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
2023-08-28 11:14:01 -07:00
|
|
|
if !matches!(state.status, LoaderV4Status::Retracted) {
|
2023-03-23 10:13:47 -07:00
|
|
|
ic_logger_msg!(log_collector, "Destination program is not retracted");
|
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
|
|
|
let buffer = if let Some(ref source_program) = source_program {
|
|
|
|
let source_state = check_program_account(
|
|
|
|
&log_collector,
|
|
|
|
instruction_context,
|
|
|
|
source_program,
|
|
|
|
authority_address,
|
|
|
|
)?;
|
2023-08-28 11:14:01 -07:00
|
|
|
if !matches!(source_state.status, LoaderV4Status::Retracted) {
|
2023-03-23 10:13:47 -07:00
|
|
|
ic_logger_msg!(log_collector, "Source program is not retracted");
|
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
|
|
|
source_program
|
|
|
|
} else {
|
|
|
|
&program
|
|
|
|
};
|
2023-08-16 10:50:23 -07:00
|
|
|
|
|
|
|
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
|
|
|
|
})?;
|
2023-03-23 10:13:47 -07:00
|
|
|
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()?;
|
2023-09-16 13:12:27 -07:00
|
|
|
let required_lamports = rent.minimum_balance(source_program.get_data().len());
|
|
|
|
let transfer_lamports = required_lamports.saturating_sub(program.get_lamports());
|
2023-03-23 10:13:47 -07:00
|
|
|
program.set_data_from_slice(source_program.get_data())?;
|
|
|
|
source_program.set_data_length(0)?;
|
|
|
|
source_program.checked_sub_lamports(transfer_lamports)?;
|
|
|
|
program.checked_add_lamports(transfer_lamports)?;
|
|
|
|
}
|
|
|
|
let state = get_state_mut(program.get_data_mut()?)?;
|
|
|
|
state.slot = current_slot;
|
2023-08-28 11:14:01 -07:00
|
|
|
state.status = LoaderV4Status::Deployed;
|
2023-08-16 10:50:23 -07:00
|
|
|
|
|
|
|
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));
|
2023-03-23 10:13:47 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn process_instruction_retract(
|
|
|
|
invoke_context: &mut InvokeContext,
|
|
|
|
) -> Result<(), InstructionError> {
|
|
|
|
let log_collector = invoke_context.get_log_collector();
|
|
|
|
let transaction_context = &invoke_context.transaction_context;
|
|
|
|
let instruction_context = transaction_context.get_current_instruction_context()?;
|
|
|
|
let mut program = instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
|
|
|
|
let authority_address = instruction_context
|
|
|
|
.get_index_of_instruction_account_in_transaction(1)
|
|
|
|
.and_then(|index| transaction_context.get_key_of_account_at_index(index))?;
|
|
|
|
let state = check_program_account(
|
|
|
|
&log_collector,
|
|
|
|
instruction_context,
|
|
|
|
&program,
|
|
|
|
authority_address,
|
|
|
|
)?;
|
|
|
|
let current_slot = invoke_context.get_sysvar_cache().get_clock()?.slot;
|
|
|
|
if state.slot.saturating_add(DEPLOYMENT_COOLDOWN_IN_SLOTS) > current_slot {
|
|
|
|
ic_logger_msg!(
|
|
|
|
log_collector,
|
|
|
|
"Program was deployed recently, cooldown still in effect"
|
|
|
|
);
|
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
2023-08-28 11:14:01 -07:00
|
|
|
if matches!(state.status, LoaderV4Status::Retracted) {
|
2023-03-23 10:13:47 -07:00
|
|
|
ic_logger_msg!(log_collector, "Program is not deployed");
|
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
|
|
|
let state = get_state_mut(program.get_data_mut()?)?;
|
2023-08-28 11:14:01 -07:00
|
|
|
state.status = LoaderV4Status::Retracted;
|
2023-03-23 10:13:47 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn process_instruction_transfer_authority(
|
|
|
|
invoke_context: &mut InvokeContext,
|
|
|
|
) -> Result<(), InstructionError> {
|
|
|
|
let log_collector = invoke_context.get_log_collector();
|
|
|
|
let transaction_context = &invoke_context.transaction_context;
|
|
|
|
let instruction_context = transaction_context.get_current_instruction_context()?;
|
|
|
|
let mut program = instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
|
|
|
|
let authority_address = instruction_context
|
|
|
|
.get_index_of_instruction_account_in_transaction(1)
|
|
|
|
.and_then(|index| transaction_context.get_key_of_account_at_index(index))?;
|
|
|
|
let new_authority_address = instruction_context
|
|
|
|
.get_index_of_instruction_account_in_transaction(2)
|
|
|
|
.and_then(|index| transaction_context.get_key_of_account_at_index(index))
|
|
|
|
.ok()
|
|
|
|
.cloned();
|
|
|
|
let _state = check_program_account(
|
|
|
|
&log_collector,
|
|
|
|
instruction_context,
|
|
|
|
&program,
|
|
|
|
authority_address,
|
|
|
|
)?;
|
|
|
|
if new_authority_address.is_some() && !instruction_context.is_instruction_account_signer(2)? {
|
|
|
|
ic_logger_msg!(log_collector, "New authority did not sign");
|
|
|
|
return Err(InstructionError::MissingRequiredSignature);
|
|
|
|
}
|
|
|
|
let state = get_state_mut(program.get_data_mut()?)?;
|
2023-08-28 11:14:01 -07:00
|
|
|
if let Some(new_authority_address) = new_authority_address {
|
|
|
|
state.authority_address = new_authority_address;
|
|
|
|
} else if matches!(state.status, LoaderV4Status::Deployed) {
|
|
|
|
state.status = LoaderV4Status::Finalized;
|
|
|
|
} else {
|
|
|
|
ic_logger_msg!(log_collector, "Program must be deployed to be finalized");
|
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
2023-03-23 10:13:47 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-04-05 06:50:34 -07:00
|
|
|
pub fn process_instruction(
|
|
|
|
invoke_context: &mut InvokeContext,
|
2023-04-21 09:08:32 -07:00
|
|
|
_arg0: u64,
|
|
|
|
_arg1: u64,
|
|
|
|
_arg2: u64,
|
|
|
|
_arg3: u64,
|
|
|
|
_arg4: u64,
|
|
|
|
_memory_mapping: &mut MemoryMapping,
|
|
|
|
result: &mut ProgramResult,
|
|
|
|
) {
|
|
|
|
*result = process_instruction_inner(invoke_context).into();
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn process_instruction_inner(
|
|
|
|
invoke_context: &mut InvokeContext,
|
|
|
|
) -> Result<u64, Box<dyn std::error::Error>> {
|
2023-03-23 10:13:47 -07:00
|
|
|
let log_collector = invoke_context.get_log_collector();
|
|
|
|
let transaction_context = &invoke_context.transaction_context;
|
|
|
|
let instruction_context = transaction_context.get_current_instruction_context()?;
|
|
|
|
let instruction_data = instruction_context.get_instruction_data();
|
|
|
|
let program_id = instruction_context.get_last_program_key(transaction_context)?;
|
2023-05-10 23:29:06 -07:00
|
|
|
if loader_v4::check_id(program_id) {
|
2023-03-24 14:45:03 -07:00
|
|
|
if invoke_context
|
|
|
|
.feature_set
|
|
|
|
.is_active(&feature_set::native_programs_consume_cu::id())
|
|
|
|
{
|
2023-06-02 08:22:22 -07:00
|
|
|
invoke_context.consume_checked(DEFAULT_COMPUTE_UNITS)?;
|
2023-03-24 14:45:03 -07:00
|
|
|
}
|
2023-03-23 10:13:47 -07:00
|
|
|
match limited_deserialize(instruction_data)? {
|
2023-05-10 23:29:06 -07:00
|
|
|
LoaderV4Instruction::Write { offset, bytes } => {
|
2023-03-23 10:13:47 -07:00
|
|
|
process_instruction_write(invoke_context, offset, bytes)
|
|
|
|
}
|
2023-08-30 15:44:58 -07:00
|
|
|
LoaderV4Instruction::Truncate { new_size } => {
|
|
|
|
process_instruction_truncate(invoke_context, new_size)
|
2023-03-23 10:13:47 -07:00
|
|
|
}
|
2023-05-10 23:29:06 -07:00
|
|
|
LoaderV4Instruction::Deploy => process_instruction_deploy(invoke_context),
|
|
|
|
LoaderV4Instruction::Retract => process_instruction_retract(invoke_context),
|
|
|
|
LoaderV4Instruction::TransferAuthority => {
|
2023-03-23 10:13:47 -07:00
|
|
|
process_instruction_transfer_authority(invoke_context)
|
|
|
|
}
|
|
|
|
}
|
2023-04-05 06:50:34 -07:00
|
|
|
.map_err(|err| Box::new(err) as Box<dyn std::error::Error>)
|
2023-03-23 10:13:47 -07:00
|
|
|
} else {
|
|
|
|
let program = instruction_context.try_borrow_last_program_account(transaction_context)?;
|
2023-05-10 23:29:06 -07:00
|
|
|
if !loader_v4::check_id(program.get_owner()) {
|
2023-03-23 10:13:47 -07:00
|
|
|
ic_logger_msg!(log_collector, "Program not owned by loader");
|
2023-04-05 06:50:34 -07:00
|
|
|
return Err(Box::new(InstructionError::InvalidAccountOwner));
|
2023-03-23 10:13:47 -07:00
|
|
|
}
|
|
|
|
if program.get_data().is_empty() {
|
|
|
|
ic_logger_msg!(log_collector, "Program is uninitialized");
|
2023-04-05 06:50:34 -07:00
|
|
|
return Err(Box::new(InstructionError::InvalidAccountData));
|
2023-03-23 10:13:47 -07:00
|
|
|
}
|
|
|
|
let state = get_state(program.get_data())?;
|
2023-08-28 11:14:01 -07:00
|
|
|
if matches!(state.status, LoaderV4Status::Retracted) {
|
2023-03-23 10:13:47 -07:00
|
|
|
ic_logger_msg!(log_collector, "Program is not deployed");
|
2023-04-05 06:50:34 -07:00
|
|
|
return Err(Box::new(InstructionError::InvalidArgument));
|
2023-03-23 10:13:47 -07:00
|
|
|
}
|
|
|
|
let mut get_or_create_executor_time = Measure::start("get_or_create_executor_time");
|
2023-08-16 10:50:23 -07:00
|
|
|
let loaded_program = invoke_context
|
|
|
|
.find_program_in_cache(program.get_key())
|
2023-09-05 14:57:25 -07:00
|
|
|
.ok_or_else(|| {
|
|
|
|
ic_logger_msg!(log_collector, "Program is not cached");
|
|
|
|
InstructionError::InvalidAccountData
|
|
|
|
})?;
|
2023-03-23 10:13:47 -07:00
|
|
|
get_or_create_executor_time.stop();
|
|
|
|
saturating_add_assign!(
|
|
|
|
invoke_context.timings.get_or_create_executor_us,
|
|
|
|
get_or_create_executor_time.as_us()
|
|
|
|
);
|
|
|
|
drop(program);
|
2023-06-02 11:26:56 -07:00
|
|
|
loaded_program
|
|
|
|
.ix_usage_counter
|
|
|
|
.fetch_add(1, Ordering::Relaxed);
|
2023-03-23 10:13:47 -07:00
|
|
|
match &loaded_program.program {
|
2023-06-06 15:24:39 -07:00
|
|
|
LoadedProgramType::FailedVerification(_)
|
2023-03-28 11:49:56 -07:00
|
|
|
| LoadedProgramType::Closed
|
2023-04-05 06:50:34 -07:00
|
|
|
| LoadedProgramType::DelayVisibility => {
|
2023-09-05 14:57:25 -07:00
|
|
|
ic_logger_msg!(log_collector, "Program is not deployed");
|
2023-04-21 09:08:32 -07:00
|
|
|
Err(Box::new(InstructionError::InvalidAccountData) as Box<dyn std::error::Error>)
|
2023-04-05 06:50:34 -07:00
|
|
|
}
|
2023-03-23 10:13:47 -07:00
|
|
|
LoadedProgramType::Typed(executable) => execute(invoke_context, executable),
|
2023-04-21 09:08:32 -07:00
|
|
|
_ => Err(Box::new(InstructionError::IncorrectProgramId) as Box<dyn std::error::Error>),
|
2023-03-23 10:13:47 -07:00
|
|
|
}
|
|
|
|
}
|
2023-04-21 09:08:32 -07:00
|
|
|
.map(|_| 0)
|
2023-03-23 10:13:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use {
|
|
|
|
super::*,
|
2023-03-24 14:45:03 -07:00
|
|
|
solana_program_runtime::invoke_context::mock_process_instruction,
|
2023-03-23 10:13:47 -07:00
|
|
|
solana_sdk::{
|
|
|
|
account::{
|
|
|
|
create_account_shared_data_for_test, AccountSharedData, ReadableAccount,
|
|
|
|
WritableAccount,
|
|
|
|
},
|
2023-03-24 14:45:03 -07:00
|
|
|
instruction::AccountMeta,
|
2023-03-23 10:13:47 -07:00
|
|
|
slot_history::Slot,
|
|
|
|
sysvar::{clock, rent},
|
2023-03-24 14:45:03 -07:00
|
|
|
transaction_context::IndexOfAccount,
|
2023-03-23 10:13:47 -07:00
|
|
|
},
|
|
|
|
std::{fs::File, io::Read, path::Path},
|
|
|
|
};
|
|
|
|
|
2023-08-16 10:50:23 -07:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-23 10:13:47 -07:00
|
|
|
fn process_instruction(
|
2023-03-24 14:45:03 -07:00
|
|
|
program_indices: Vec<IndexOfAccount>,
|
2023-03-23 10:13:47 -07:00
|
|
|
instruction_data: &[u8],
|
2023-03-24 14:45:03 -07:00
|
|
|
transaction_accounts: Vec<(Pubkey, AccountSharedData)>,
|
2023-03-23 10:13:47 -07:00
|
|
|
instruction_accounts: &[(IndexOfAccount, bool, bool)],
|
|
|
|
expected_result: Result<(), InstructionError>,
|
|
|
|
) -> Vec<AccountSharedData> {
|
|
|
|
let instruction_accounts = instruction_accounts
|
|
|
|
.iter()
|
|
|
|
.map(
|
2023-03-24 14:45:03 -07:00
|
|
|
|(index_in_transaction, is_signer, is_writable)| AccountMeta {
|
|
|
|
pubkey: transaction_accounts[*index_in_transaction as usize].0,
|
|
|
|
is_signer: *is_signer,
|
|
|
|
is_writable: *is_writable,
|
2023-03-23 10:13:47 -07:00
|
|
|
},
|
|
|
|
)
|
|
|
|
.collect::<Vec<_>>();
|
2023-03-24 14:45:03 -07:00
|
|
|
mock_process_instruction(
|
2023-05-10 23:29:06 -07:00
|
|
|
&loader_v4::id(),
|
2023-03-24 14:45:03 -07:00
|
|
|
program_indices,
|
|
|
|
instruction_data,
|
|
|
|
transaction_accounts,
|
|
|
|
instruction_accounts,
|
|
|
|
expected_result,
|
|
|
|
super::process_instruction,
|
2023-08-16 10:50:23 -07:00
|
|
|
|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);
|
|
|
|
},
|
2023-04-07 03:53:19 -07:00
|
|
|
|_invoke_context| {},
|
2023-03-24 14:45:03 -07:00
|
|
|
)
|
2023-03-23 10:13:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn load_program_account_from_elf(
|
2023-08-28 11:14:01 -07:00
|
|
|
authority_address: Pubkey,
|
|
|
|
status: LoaderV4Status,
|
2023-03-23 10:13:47 -07:00
|
|
|
path: &str,
|
|
|
|
) -> AccountSharedData {
|
|
|
|
let path = Path::new("test_elfs/out/").join(path).with_extension("so");
|
|
|
|
let mut file = File::open(path).expect("file open failed");
|
|
|
|
let mut elf_bytes = Vec::new();
|
|
|
|
file.read_to_end(&mut elf_bytes).unwrap();
|
|
|
|
let rent = rent::Rent::default();
|
|
|
|
let account_size =
|
2023-05-10 23:29:06 -07:00
|
|
|
loader_v4::LoaderV4State::program_data_offset().saturating_add(elf_bytes.len());
|
2023-03-23 10:13:47 -07:00
|
|
|
let mut program_account = AccountSharedData::new(
|
|
|
|
rent.minimum_balance(account_size),
|
|
|
|
account_size,
|
2023-05-10 23:29:06 -07:00
|
|
|
&loader_v4::id(),
|
2023-03-23 10:13:47 -07:00
|
|
|
);
|
2023-08-28 11:14:01 -07:00
|
|
|
let state = get_state_mut(program_account.data_as_mut_slice()).unwrap();
|
|
|
|
state.slot = 0;
|
|
|
|
state.authority_address = authority_address;
|
|
|
|
state.status = status;
|
2023-05-10 23:29:06 -07:00
|
|
|
program_account.data_as_mut_slice()[loader_v4::LoaderV4State::program_data_offset()..]
|
2023-03-23 10:13:47 -07:00
|
|
|
.copy_from_slice(&elf_bytes);
|
|
|
|
program_account
|
|
|
|
}
|
|
|
|
|
|
|
|
fn clock(slot: Slot) -> AccountSharedData {
|
|
|
|
let clock = clock::Clock {
|
|
|
|
slot,
|
|
|
|
..clock::Clock::default()
|
|
|
|
};
|
|
|
|
create_account_shared_data_for_test(&clock)
|
|
|
|
}
|
|
|
|
|
2023-05-10 23:29:06 -07:00
|
|
|
fn test_loader_instruction_general_errors(instruction: LoaderV4Instruction) {
|
2023-03-23 10:13:47 -07:00
|
|
|
let instruction = bincode::serialize(&instruction).unwrap();
|
|
|
|
let authority_address = Pubkey::new_unique();
|
|
|
|
let transaction_accounts = vec![
|
|
|
|
(
|
|
|
|
Pubkey::new_unique(),
|
2023-09-06 01:54:15 -07:00
|
|
|
load_program_account_from_elf(
|
|
|
|
authority_address,
|
|
|
|
LoaderV4Status::Deployed,
|
|
|
|
"relative_call",
|
|
|
|
),
|
2023-03-23 10:13:47 -07:00
|
|
|
),
|
|
|
|
(
|
|
|
|
authority_address,
|
|
|
|
AccountSharedData::new(0, 0, &Pubkey::new_unique()),
|
|
|
|
),
|
|
|
|
(
|
|
|
|
Pubkey::new_unique(),
|
2023-09-06 01:54:15 -07:00
|
|
|
load_program_account_from_elf(
|
|
|
|
authority_address,
|
|
|
|
LoaderV4Status::Finalized,
|
|
|
|
"relative_call",
|
|
|
|
),
|
2023-03-23 10:13:47 -07:00
|
|
|
),
|
|
|
|
(
|
|
|
|
clock::id(),
|
|
|
|
create_account_shared_data_for_test(&clock::Clock::default()),
|
|
|
|
),
|
|
|
|
(
|
|
|
|
rent::id(),
|
|
|
|
create_account_shared_data_for_test(&rent::Rent::default()),
|
|
|
|
),
|
|
|
|
];
|
|
|
|
|
|
|
|
// Error: Missing program account
|
|
|
|
process_instruction(
|
|
|
|
vec![],
|
|
|
|
&instruction,
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[],
|
|
|
|
Err(InstructionError::NotEnoughAccountKeys),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Error: Missing authority account
|
|
|
|
process_instruction(
|
|
|
|
vec![],
|
|
|
|
&instruction,
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(0, false, true)],
|
|
|
|
Err(InstructionError::NotEnoughAccountKeys),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Error: Program not owned by loader
|
|
|
|
process_instruction(
|
|
|
|
vec![],
|
|
|
|
&instruction,
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(1, false, true), (1, true, false), (2, true, true)],
|
|
|
|
Err(InstructionError::InvalidAccountOwner),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Error: Program is not writeable
|
|
|
|
process_instruction(
|
|
|
|
vec![],
|
|
|
|
&instruction,
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(0, false, false), (1, true, false), (2, true, true)],
|
|
|
|
Err(InstructionError::InvalidArgument),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Error: Authority did not sign
|
|
|
|
process_instruction(
|
|
|
|
vec![],
|
|
|
|
&instruction,
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(0, false, true), (1, false, false), (2, true, true)],
|
|
|
|
Err(InstructionError::MissingRequiredSignature),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Error: Program is finalized
|
|
|
|
process_instruction(
|
|
|
|
vec![],
|
|
|
|
&instruction,
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(2, false, true), (1, true, false), (0, true, true)],
|
|
|
|
Err(InstructionError::Immutable),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Error: Incorrect authority provided
|
|
|
|
process_instruction(
|
|
|
|
vec![],
|
|
|
|
&instruction,
|
|
|
|
transaction_accounts,
|
|
|
|
&[(0, false, true), (2, true, false), (2, true, true)],
|
|
|
|
Err(InstructionError::IncorrectAuthority),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_loader_instruction_write() {
|
|
|
|
let authority_address = Pubkey::new_unique();
|
2023-08-30 15:44:58 -07:00
|
|
|
let transaction_accounts = vec![
|
2023-03-23 10:13:47 -07:00
|
|
|
(
|
|
|
|
Pubkey::new_unique(),
|
2023-09-06 01:54:15 -07:00
|
|
|
load_program_account_from_elf(
|
|
|
|
authority_address,
|
|
|
|
LoaderV4Status::Retracted,
|
|
|
|
"relative_call",
|
|
|
|
),
|
2023-03-23 10:13:47 -07:00
|
|
|
),
|
|
|
|
(
|
|
|
|
authority_address,
|
|
|
|
AccountSharedData::new(0, 0, &Pubkey::new_unique()),
|
|
|
|
),
|
|
|
|
(
|
|
|
|
Pubkey::new_unique(),
|
2023-09-06 01:54:15 -07:00
|
|
|
load_program_account_from_elf(
|
|
|
|
authority_address,
|
|
|
|
LoaderV4Status::Deployed,
|
|
|
|
"relative_call",
|
|
|
|
),
|
2023-03-23 10:13:47 -07:00
|
|
|
),
|
|
|
|
(
|
|
|
|
clock::id(),
|
|
|
|
create_account_shared_data_for_test(&clock::Clock::default()),
|
|
|
|
),
|
|
|
|
(
|
|
|
|
rent::id(),
|
|
|
|
create_account_shared_data_for_test(&rent::Rent::default()),
|
|
|
|
),
|
|
|
|
];
|
|
|
|
|
2023-08-30 15:44:58 -07:00
|
|
|
// Overwrite existing data
|
2023-03-23 10:13:47 -07:00
|
|
|
process_instruction(
|
|
|
|
vec![],
|
2023-05-10 23:29:06 -07:00
|
|
|
&bincode::serialize(&LoaderV4Instruction::Write {
|
2023-03-23 10:13:47 -07:00
|
|
|
offset: 2,
|
|
|
|
bytes: vec![8, 8, 8, 8],
|
|
|
|
})
|
|
|
|
.unwrap(),
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(0, false, true), (1, true, false)],
|
|
|
|
Ok(()),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Empty write
|
2023-08-30 15:44:58 -07:00
|
|
|
process_instruction(
|
2023-03-23 10:13:47 -07:00
|
|
|
vec![],
|
2023-05-10 23:29:06 -07:00
|
|
|
&bincode::serialize(&LoaderV4Instruction::Write {
|
2023-03-23 10:13:47 -07:00
|
|
|
offset: 2,
|
|
|
|
bytes: Vec::new(),
|
|
|
|
})
|
|
|
|
.unwrap(),
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(0, false, true), (1, true, false)],
|
|
|
|
Ok(()),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Error: Program is not retracted
|
|
|
|
process_instruction(
|
|
|
|
vec![],
|
2023-05-10 23:29:06 -07:00
|
|
|
&bincode::serialize(&LoaderV4Instruction::Write {
|
2023-03-23 10:13:47 -07:00
|
|
|
offset: 8,
|
|
|
|
bytes: vec![8, 8, 8, 8],
|
|
|
|
})
|
|
|
|
.unwrap(),
|
|
|
|
transaction_accounts.clone(),
|
2023-08-30 15:44:58 -07:00
|
|
|
&[(2, false, true), (1, true, false)],
|
2023-03-23 10:13:47 -07:00
|
|
|
Err(InstructionError::InvalidArgument),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Error: Write out of bounds
|
|
|
|
process_instruction(
|
|
|
|
vec![],
|
2023-05-10 23:29:06 -07:00
|
|
|
&bincode::serialize(&LoaderV4Instruction::Write {
|
2023-08-30 15:44:58 -07:00
|
|
|
offset: transaction_accounts[0]
|
|
|
|
.1
|
|
|
|
.data()
|
|
|
|
.len()
|
|
|
|
.saturating_sub(loader_v4::LoaderV4State::program_data_offset())
|
|
|
|
.saturating_sub(3) as u32,
|
2023-03-23 10:13:47 -07:00
|
|
|
bytes: vec![8, 8, 8, 8],
|
|
|
|
})
|
|
|
|
.unwrap(),
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(0, false, true), (1, true, false)],
|
2023-08-30 15:44:58 -07:00
|
|
|
Err(InstructionError::AccountDataTooSmall),
|
2023-03-23 10:13:47 -07:00
|
|
|
);
|
|
|
|
|
2023-05-10 23:29:06 -07:00
|
|
|
test_loader_instruction_general_errors(LoaderV4Instruction::Write {
|
2023-03-23 10:13:47 -07:00
|
|
|
offset: 0,
|
|
|
|
bytes: Vec::new(),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_loader_instruction_truncate() {
|
|
|
|
let authority_address = Pubkey::new_unique();
|
2023-08-30 15:44:58 -07:00
|
|
|
let mut transaction_accounts = vec![
|
2023-03-23 10:13:47 -07:00
|
|
|
(
|
|
|
|
Pubkey::new_unique(),
|
2023-09-06 01:54:15 -07:00
|
|
|
load_program_account_from_elf(
|
|
|
|
authority_address,
|
|
|
|
LoaderV4Status::Retracted,
|
|
|
|
"relative_call",
|
|
|
|
),
|
2023-03-23 10:13:47 -07:00
|
|
|
),
|
|
|
|
(
|
|
|
|
authority_address,
|
|
|
|
AccountSharedData::new(0, 0, &Pubkey::new_unique()),
|
|
|
|
),
|
|
|
|
(
|
|
|
|
Pubkey::new_unique(),
|
2023-05-10 23:29:06 -07:00
|
|
|
AccountSharedData::new(0, 0, &loader_v4::id()),
|
2023-03-23 10:13:47 -07:00
|
|
|
),
|
2023-08-30 15:44:58 -07:00
|
|
|
(
|
|
|
|
Pubkey::new_unique(),
|
2023-09-06 01:54:15 -07:00
|
|
|
AccountSharedData::new(40000000, 0, &loader_v4::id()),
|
2023-08-30 15:44:58 -07:00
|
|
|
),
|
|
|
|
(
|
|
|
|
Pubkey::new_unique(),
|
|
|
|
load_program_account_from_elf(
|
|
|
|
authority_address,
|
|
|
|
LoaderV4Status::Retracted,
|
2023-09-06 01:54:15 -07:00
|
|
|
"rodata_section",
|
2023-08-30 15:44:58 -07:00
|
|
|
),
|
|
|
|
),
|
2023-03-23 10:13:47 -07:00
|
|
|
(
|
|
|
|
Pubkey::new_unique(),
|
2023-09-06 01:54:15 -07:00
|
|
|
load_program_account_from_elf(
|
|
|
|
authority_address,
|
|
|
|
LoaderV4Status::Deployed,
|
|
|
|
"relative_call",
|
|
|
|
),
|
2023-03-23 10:13:47 -07:00
|
|
|
),
|
|
|
|
(
|
|
|
|
clock::id(),
|
|
|
|
create_account_shared_data_for_test(&clock::Clock::default()),
|
|
|
|
),
|
|
|
|
(
|
|
|
|
rent::id(),
|
|
|
|
create_account_shared_data_for_test(&rent::Rent::default()),
|
|
|
|
),
|
|
|
|
];
|
|
|
|
|
2023-08-30 15:44:58 -07:00
|
|
|
// No change
|
2023-03-23 10:13:47 -07:00
|
|
|
let accounts = process_instruction(
|
|
|
|
vec![],
|
2023-08-30 15:44:58 -07:00
|
|
|
&bincode::serialize(&LoaderV4Instruction::Truncate {
|
|
|
|
new_size: transaction_accounts[0]
|
|
|
|
.1
|
|
|
|
.data()
|
|
|
|
.len()
|
|
|
|
.saturating_sub(loader_v4::LoaderV4State::program_data_offset())
|
|
|
|
as u32,
|
|
|
|
})
|
|
|
|
.unwrap(),
|
2023-03-23 10:13:47 -07:00
|
|
|
transaction_accounts.clone(),
|
2023-08-30 15:44:58 -07:00
|
|
|
&[(0, false, true), (1, true, false)],
|
|
|
|
Ok(()),
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
accounts[0].data().len(),
|
|
|
|
transaction_accounts[0].1.data().len(),
|
|
|
|
);
|
|
|
|
assert_eq!(accounts[2].lamports(), transaction_accounts[2].1.lamports());
|
|
|
|
let lamports = transaction_accounts[4].1.lamports();
|
|
|
|
transaction_accounts[0].1.set_lamports(lamports);
|
|
|
|
|
|
|
|
// Initialize program account
|
|
|
|
let accounts = process_instruction(
|
|
|
|
vec![],
|
|
|
|
&bincode::serialize(&LoaderV4Instruction::Truncate {
|
|
|
|
new_size: transaction_accounts[0]
|
|
|
|
.1
|
|
|
|
.data()
|
|
|
|
.len()
|
|
|
|
.saturating_sub(loader_v4::LoaderV4State::program_data_offset())
|
|
|
|
as u32,
|
|
|
|
})
|
|
|
|
.unwrap(),
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(3, true, true), (1, true, false), (2, false, true)],
|
|
|
|
Ok(()),
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
accounts[3].data().len(),
|
|
|
|
transaction_accounts[0].1.data().len(),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Increase program account size
|
|
|
|
let accounts = process_instruction(
|
|
|
|
vec![],
|
|
|
|
&bincode::serialize(&LoaderV4Instruction::Truncate {
|
|
|
|
new_size: transaction_accounts[4]
|
|
|
|
.1
|
|
|
|
.data()
|
|
|
|
.len()
|
|
|
|
.saturating_sub(loader_v4::LoaderV4State::program_data_offset())
|
|
|
|
as u32,
|
|
|
|
})
|
|
|
|
.unwrap(),
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(0, false, true), (1, true, false)],
|
2023-03-23 10:13:47 -07:00
|
|
|
Ok(()),
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
accounts[0].data().len(),
|
2023-08-30 15:44:58 -07:00
|
|
|
transaction_accounts[4].1.data().len(),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Decrease program account size
|
|
|
|
let accounts = process_instruction(
|
|
|
|
vec![],
|
|
|
|
&bincode::serialize(&LoaderV4Instruction::Truncate {
|
|
|
|
new_size: transaction_accounts[0]
|
|
|
|
.1
|
|
|
|
.data()
|
|
|
|
.len()
|
|
|
|
.saturating_sub(loader_v4::LoaderV4State::program_data_offset())
|
|
|
|
as u32,
|
|
|
|
})
|
|
|
|
.unwrap(),
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(4, false, true), (1, true, false), (2, false, true)],
|
|
|
|
Ok(()),
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
accounts[4].data().len(),
|
|
|
|
transaction_accounts[0].1.data().len(),
|
2023-03-23 10:13:47 -07:00
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
accounts[2].lamports(),
|
2023-08-30 15:44:58 -07:00
|
|
|
transaction_accounts[2].1.lamports().saturating_add(
|
|
|
|
transaction_accounts[4]
|
|
|
|
.1
|
|
|
|
.lamports()
|
|
|
|
.saturating_sub(accounts[4].lamports())
|
|
|
|
),
|
2023-03-23 10:13:47 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
// Close program account
|
|
|
|
let accounts = process_instruction(
|
|
|
|
vec![],
|
2023-08-30 15:44:58 -07:00
|
|
|
&bincode::serialize(&LoaderV4Instruction::Truncate { new_size: 0 }).unwrap(),
|
2023-03-23 10:13:47 -07:00
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(0, false, true), (1, true, false), (2, false, true)],
|
|
|
|
Ok(()),
|
|
|
|
);
|
|
|
|
assert_eq!(accounts[0].data().len(), 0);
|
|
|
|
assert_eq!(
|
|
|
|
accounts[2].lamports(),
|
2023-08-30 15:44:58 -07:00
|
|
|
transaction_accounts[2].1.lamports().saturating_add(
|
|
|
|
transaction_accounts[0]
|
|
|
|
.1
|
|
|
|
.lamports()
|
|
|
|
.saturating_sub(accounts[0].lamports())
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Error: Program not owned by loader
|
|
|
|
process_instruction(
|
|
|
|
vec![],
|
|
|
|
&bincode::serialize(&LoaderV4Instruction::Truncate { new_size: 8 }).unwrap(),
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(1, false, true), (1, true, false), (2, true, true)],
|
|
|
|
Err(InstructionError::InvalidAccountOwner),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Error: Program is not writeable
|
|
|
|
process_instruction(
|
|
|
|
vec![],
|
|
|
|
&bincode::serialize(&LoaderV4Instruction::Truncate { new_size: 8 }).unwrap(),
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(3, false, false), (1, true, false), (2, true, true)],
|
|
|
|
Err(InstructionError::InvalidArgument),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Error: Program did not sign
|
|
|
|
process_instruction(
|
|
|
|
vec![],
|
|
|
|
&bincode::serialize(&LoaderV4Instruction::Truncate { new_size: 8 }).unwrap(),
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(3, false, true), (1, true, false), (2, true, true)],
|
|
|
|
Err(InstructionError::MissingRequiredSignature),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Error: Authority did not sign
|
|
|
|
process_instruction(
|
|
|
|
vec![],
|
|
|
|
&bincode::serialize(&LoaderV4Instruction::Truncate { new_size: 8 }).unwrap(),
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(3, true, true), (1, false, false), (2, true, true)],
|
|
|
|
Err(InstructionError::MissingRequiredSignature),
|
2023-03-23 10:13:47 -07:00
|
|
|
);
|
|
|
|
|
2023-08-30 15:44:58 -07:00
|
|
|
// Error: Program is and stays uninitialized
|
2023-03-23 10:13:47 -07:00
|
|
|
process_instruction(
|
|
|
|
vec![],
|
2023-08-30 15:44:58 -07:00
|
|
|
&bincode::serialize(&LoaderV4Instruction::Truncate { new_size: 0 }).unwrap(),
|
2023-03-23 10:13:47 -07:00
|
|
|
transaction_accounts.clone(),
|
2023-08-30 15:44:58 -07:00
|
|
|
&[(3, false, true), (1, true, false), (2, true, true)],
|
2023-03-23 10:13:47 -07:00
|
|
|
Err(InstructionError::InvalidAccountData),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Error: Program is not retracted
|
|
|
|
process_instruction(
|
|
|
|
vec![],
|
2023-08-30 15:44:58 -07:00
|
|
|
&bincode::serialize(&LoaderV4Instruction::Truncate { new_size: 8 }).unwrap(),
|
2023-03-23 10:13:47 -07:00
|
|
|
transaction_accounts.clone(),
|
2023-08-30 15:44:58 -07:00
|
|
|
&[(5, false, true), (1, true, false), (2, false, true)],
|
2023-03-23 10:13:47 -07:00
|
|
|
Err(InstructionError::InvalidArgument),
|
|
|
|
);
|
|
|
|
|
2023-08-30 15:44:58 -07:00
|
|
|
// Error: Missing recipient account
|
2023-03-23 10:13:47 -07:00
|
|
|
process_instruction(
|
|
|
|
vec![],
|
2023-08-30 15:44:58 -07:00
|
|
|
&bincode::serialize(&LoaderV4Instruction::Truncate { new_size: 0 }).unwrap(),
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(0, true, true), (1, true, false)],
|
|
|
|
Err(InstructionError::NotEnoughAccountKeys),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Error: Recipient is not writeable
|
|
|
|
process_instruction(
|
|
|
|
vec![],
|
|
|
|
&bincode::serialize(&LoaderV4Instruction::Truncate { new_size: 0 }).unwrap(),
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(0, false, true), (1, true, false), (2, false, false)],
|
|
|
|
Err(InstructionError::InvalidArgument),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Error: Insufficient funds
|
|
|
|
process_instruction(
|
|
|
|
vec![],
|
|
|
|
&bincode::serialize(&LoaderV4Instruction::Truncate {
|
|
|
|
new_size: transaction_accounts[4]
|
|
|
|
.1
|
|
|
|
.data()
|
|
|
|
.len()
|
|
|
|
.saturating_sub(loader_v4::LoaderV4State::program_data_offset())
|
|
|
|
.saturating_add(1) as u32,
|
|
|
|
})
|
|
|
|
.unwrap(),
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(0, false, true), (1, true, false)],
|
|
|
|
Err(InstructionError::InsufficientFunds),
|
2023-03-23 10:13:47 -07:00
|
|
|
);
|
|
|
|
|
2023-08-30 15:44:58 -07:00
|
|
|
test_loader_instruction_general_errors(LoaderV4Instruction::Truncate { new_size: 0 });
|
2023-03-23 10:13:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_loader_instruction_deploy() {
|
|
|
|
let authority_address = Pubkey::new_unique();
|
|
|
|
let mut transaction_accounts = vec![
|
|
|
|
(
|
|
|
|
Pubkey::new_unique(),
|
2023-08-28 11:14:01 -07:00
|
|
|
load_program_account_from_elf(
|
|
|
|
authority_address,
|
|
|
|
LoaderV4Status::Retracted,
|
2023-09-06 01:54:15 -07:00
|
|
|
"rodata_section",
|
2023-08-28 11:14:01 -07:00
|
|
|
),
|
2023-03-23 10:13:47 -07:00
|
|
|
),
|
|
|
|
(
|
|
|
|
authority_address,
|
|
|
|
AccountSharedData::new(0, 0, &Pubkey::new_unique()),
|
|
|
|
),
|
|
|
|
(
|
|
|
|
Pubkey::new_unique(),
|
2023-09-06 01:54:15 -07:00
|
|
|
load_program_account_from_elf(
|
|
|
|
authority_address,
|
|
|
|
LoaderV4Status::Retracted,
|
|
|
|
"relative_call",
|
|
|
|
),
|
2023-03-23 10:13:47 -07:00
|
|
|
),
|
|
|
|
(
|
|
|
|
Pubkey::new_unique(),
|
2023-05-10 23:29:06 -07:00
|
|
|
AccountSharedData::new(0, 0, &loader_v4::id()),
|
2023-03-23 10:13:47 -07:00
|
|
|
),
|
|
|
|
(
|
|
|
|
Pubkey::new_unique(),
|
2023-08-28 11:14:01 -07:00
|
|
|
load_program_account_from_elf(
|
|
|
|
authority_address,
|
|
|
|
LoaderV4Status::Retracted,
|
|
|
|
"invalid",
|
|
|
|
),
|
2023-03-23 10:13:47 -07:00
|
|
|
),
|
|
|
|
(clock::id(), clock(1000)),
|
|
|
|
(
|
|
|
|
rent::id(),
|
|
|
|
create_account_shared_data_for_test(&rent::Rent::default()),
|
|
|
|
),
|
|
|
|
];
|
|
|
|
|
|
|
|
// Deploy from its own data
|
|
|
|
let accounts = process_instruction(
|
|
|
|
vec![],
|
2023-05-10 23:29:06 -07:00
|
|
|
&bincode::serialize(&LoaderV4Instruction::Deploy).unwrap(),
|
2023-03-23 10:13:47 -07:00
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(0, false, true), (1, true, false)],
|
|
|
|
Ok(()),
|
|
|
|
);
|
|
|
|
transaction_accounts[0].1 = accounts[0].clone();
|
|
|
|
transaction_accounts[5].1 = clock(2000);
|
|
|
|
assert_eq!(
|
|
|
|
accounts[0].data().len(),
|
|
|
|
transaction_accounts[0].1.data().len(),
|
|
|
|
);
|
|
|
|
assert_eq!(accounts[0].lamports(), transaction_accounts[0].1.lamports());
|
|
|
|
|
|
|
|
// Error: Source program is not writable
|
|
|
|
process_instruction(
|
|
|
|
vec![],
|
2023-05-10 23:29:06 -07:00
|
|
|
&bincode::serialize(&LoaderV4Instruction::Deploy).unwrap(),
|
2023-03-23 10:13:47 -07:00
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(0, false, true), (1, true, false), (2, false, false)],
|
|
|
|
Err(InstructionError::InvalidArgument),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Error: Source program is not retracted
|
|
|
|
process_instruction(
|
|
|
|
vec![],
|
2023-05-10 23:29:06 -07:00
|
|
|
&bincode::serialize(&LoaderV4Instruction::Deploy).unwrap(),
|
2023-03-23 10:13:47 -07:00
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(2, false, true), (1, true, false), (0, false, true)],
|
|
|
|
Err(InstructionError::InvalidArgument),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Redeploy: Retract, then replace data by other source
|
|
|
|
let accounts = process_instruction(
|
|
|
|
vec![],
|
2023-05-10 23:29:06 -07:00
|
|
|
&bincode::serialize(&LoaderV4Instruction::Retract).unwrap(),
|
2023-03-23 10:13:47 -07:00
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(0, false, true), (1, true, false)],
|
|
|
|
Ok(()),
|
|
|
|
);
|
|
|
|
transaction_accounts[0].1 = accounts[0].clone();
|
|
|
|
let accounts = process_instruction(
|
|
|
|
vec![],
|
2023-05-10 23:29:06 -07:00
|
|
|
&bincode::serialize(&LoaderV4Instruction::Deploy).unwrap(),
|
2023-03-23 10:13:47 -07:00
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(0, false, true), (1, true, false), (2, false, true)],
|
|
|
|
Ok(()),
|
|
|
|
);
|
|
|
|
transaction_accounts[0].1 = accounts[0].clone();
|
|
|
|
assert_eq!(
|
|
|
|
accounts[0].data().len(),
|
|
|
|
transaction_accounts[2].1.data().len(),
|
|
|
|
);
|
|
|
|
assert_eq!(accounts[2].data().len(), 0,);
|
|
|
|
assert_eq!(
|
|
|
|
accounts[2].lamports(),
|
|
|
|
transaction_accounts[2].1.lamports().saturating_sub(
|
|
|
|
accounts[0]
|
|
|
|
.lamports()
|
|
|
|
.saturating_sub(transaction_accounts[0].1.lamports())
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Error: Program was deployed recently, cooldown still in effect
|
|
|
|
process_instruction(
|
|
|
|
vec![],
|
2023-05-10 23:29:06 -07:00
|
|
|
&bincode::serialize(&LoaderV4Instruction::Deploy).unwrap(),
|
2023-03-23 10:13:47 -07:00
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(0, false, true), (1, true, false)],
|
|
|
|
Err(InstructionError::InvalidArgument),
|
|
|
|
);
|
|
|
|
transaction_accounts[5].1 = clock(3000);
|
|
|
|
|
|
|
|
// Error: Program is uninitialized
|
|
|
|
process_instruction(
|
|
|
|
vec![],
|
2023-05-10 23:29:06 -07:00
|
|
|
&bincode::serialize(&LoaderV4Instruction::Deploy).unwrap(),
|
2023-03-23 10:13:47 -07:00
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(3, false, true), (1, true, false)],
|
|
|
|
Err(InstructionError::InvalidAccountData),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Error: Program fails verification
|
|
|
|
process_instruction(
|
|
|
|
vec![],
|
2023-05-10 23:29:06 -07:00
|
|
|
&bincode::serialize(&LoaderV4Instruction::Deploy).unwrap(),
|
2023-03-23 10:13:47 -07:00
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(4, false, true), (1, true, false)],
|
|
|
|
Err(InstructionError::InvalidAccountData),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Error: Program is deployed already
|
|
|
|
process_instruction(
|
|
|
|
vec![],
|
2023-05-10 23:29:06 -07:00
|
|
|
&bincode::serialize(&LoaderV4Instruction::Deploy).unwrap(),
|
2023-03-23 10:13:47 -07:00
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(0, false, true), (1, true, false)],
|
|
|
|
Err(InstructionError::InvalidArgument),
|
|
|
|
);
|
|
|
|
|
2023-05-10 23:29:06 -07:00
|
|
|
test_loader_instruction_general_errors(LoaderV4Instruction::Deploy);
|
2023-03-23 10:13:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_loader_instruction_retract() {
|
|
|
|
let authority_address = Pubkey::new_unique();
|
|
|
|
let mut transaction_accounts = vec![
|
|
|
|
(
|
|
|
|
Pubkey::new_unique(),
|
2023-08-28 11:14:01 -07:00
|
|
|
load_program_account_from_elf(
|
|
|
|
authority_address,
|
|
|
|
LoaderV4Status::Deployed,
|
2023-09-06 01:54:15 -07:00
|
|
|
"rodata_section",
|
2023-08-28 11:14:01 -07:00
|
|
|
),
|
2023-03-23 10:13:47 -07:00
|
|
|
),
|
|
|
|
(
|
|
|
|
authority_address,
|
|
|
|
AccountSharedData::new(0, 0, &Pubkey::new_unique()),
|
|
|
|
),
|
|
|
|
(
|
|
|
|
Pubkey::new_unique(),
|
2023-05-10 23:29:06 -07:00
|
|
|
AccountSharedData::new(0, 0, &loader_v4::id()),
|
2023-03-23 10:13:47 -07:00
|
|
|
),
|
|
|
|
(
|
|
|
|
Pubkey::new_unique(),
|
2023-08-28 11:14:01 -07:00
|
|
|
load_program_account_from_elf(
|
|
|
|
authority_address,
|
|
|
|
LoaderV4Status::Retracted,
|
2023-09-06 01:54:15 -07:00
|
|
|
"rodata_section",
|
2023-08-28 11:14:01 -07:00
|
|
|
),
|
2023-03-23 10:13:47 -07:00
|
|
|
),
|
|
|
|
(clock::id(), clock(1000)),
|
|
|
|
(
|
|
|
|
rent::id(),
|
|
|
|
create_account_shared_data_for_test(&rent::Rent::default()),
|
|
|
|
),
|
|
|
|
];
|
|
|
|
|
|
|
|
// Retract program
|
|
|
|
let accounts = process_instruction(
|
|
|
|
vec![],
|
2023-05-10 23:29:06 -07:00
|
|
|
&bincode::serialize(&LoaderV4Instruction::Retract).unwrap(),
|
2023-03-23 10:13:47 -07:00
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(0, false, true), (1, true, false)],
|
|
|
|
Ok(()),
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
accounts[0].data().len(),
|
|
|
|
transaction_accounts[0].1.data().len(),
|
|
|
|
);
|
|
|
|
assert_eq!(accounts[0].lamports(), transaction_accounts[0].1.lamports());
|
|
|
|
|
|
|
|
// Error: Program is uninitialized
|
|
|
|
process_instruction(
|
|
|
|
vec![],
|
2023-05-10 23:29:06 -07:00
|
|
|
&bincode::serialize(&LoaderV4Instruction::Retract).unwrap(),
|
2023-03-23 10:13:47 -07:00
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(2, false, true), (1, true, false)],
|
|
|
|
Err(InstructionError::InvalidAccountData),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Error: Program is not deployed
|
|
|
|
process_instruction(
|
|
|
|
vec![],
|
2023-05-10 23:29:06 -07:00
|
|
|
&bincode::serialize(&LoaderV4Instruction::Retract).unwrap(),
|
2023-03-23 10:13:47 -07:00
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(3, false, true), (1, true, false)],
|
|
|
|
Err(InstructionError::InvalidArgument),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Error: Program was deployed recently, cooldown still in effect
|
|
|
|
transaction_accounts[4].1 = clock(0);
|
|
|
|
process_instruction(
|
|
|
|
vec![],
|
2023-05-10 23:29:06 -07:00
|
|
|
&bincode::serialize(&LoaderV4Instruction::Retract).unwrap(),
|
2023-03-23 10:13:47 -07:00
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(0, false, true), (1, true, false)],
|
|
|
|
Err(InstructionError::InvalidArgument),
|
|
|
|
);
|
|
|
|
|
2023-05-10 23:29:06 -07:00
|
|
|
test_loader_instruction_general_errors(LoaderV4Instruction::Retract);
|
2023-03-23 10:13:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_loader_instruction_transfer_authority() {
|
|
|
|
let authority_address = Pubkey::new_unique();
|
|
|
|
let transaction_accounts = vec![
|
|
|
|
(
|
|
|
|
Pubkey::new_unique(),
|
2023-08-28 11:14:01 -07:00
|
|
|
load_program_account_from_elf(
|
|
|
|
authority_address,
|
|
|
|
LoaderV4Status::Deployed,
|
2023-09-06 01:54:15 -07:00
|
|
|
"rodata_section",
|
2023-08-28 11:14:01 -07:00
|
|
|
),
|
2023-03-23 10:13:47 -07:00
|
|
|
),
|
|
|
|
(
|
2023-08-28 11:14:01 -07:00
|
|
|
Pubkey::new_unique(),
|
|
|
|
load_program_account_from_elf(
|
|
|
|
authority_address,
|
|
|
|
LoaderV4Status::Retracted,
|
2023-09-06 01:54:15 -07:00
|
|
|
"rodata_section",
|
2023-08-28 11:14:01 -07:00
|
|
|
),
|
2023-03-23 10:13:47 -07:00
|
|
|
),
|
|
|
|
(
|
|
|
|
Pubkey::new_unique(),
|
2023-08-28 11:14:01 -07:00
|
|
|
AccountSharedData::new(0, 0, &loader_v4::id()),
|
|
|
|
),
|
|
|
|
(
|
|
|
|
authority_address,
|
2023-03-23 10:13:47 -07:00
|
|
|
AccountSharedData::new(0, 0, &Pubkey::new_unique()),
|
|
|
|
),
|
|
|
|
(
|
|
|
|
Pubkey::new_unique(),
|
2023-08-28 11:14:01 -07:00
|
|
|
AccountSharedData::new(0, 0, &Pubkey::new_unique()),
|
2023-03-23 10:13:47 -07:00
|
|
|
),
|
|
|
|
(
|
|
|
|
clock::id(),
|
|
|
|
create_account_shared_data_for_test(&clock::Clock::default()),
|
|
|
|
),
|
|
|
|
(
|
|
|
|
rent::id(),
|
|
|
|
create_account_shared_data_for_test(&rent::Rent::default()),
|
|
|
|
),
|
|
|
|
];
|
|
|
|
|
|
|
|
// Transfer authority
|
|
|
|
let accounts = process_instruction(
|
|
|
|
vec![],
|
2023-05-10 23:29:06 -07:00
|
|
|
&bincode::serialize(&LoaderV4Instruction::TransferAuthority).unwrap(),
|
2023-03-23 10:13:47 -07:00
|
|
|
transaction_accounts.clone(),
|
2023-08-28 11:14:01 -07:00
|
|
|
&[(0, false, true), (3, true, false), (4, true, false)],
|
2023-03-23 10:13:47 -07:00
|
|
|
Ok(()),
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
accounts[0].data().len(),
|
|
|
|
transaction_accounts[0].1.data().len(),
|
|
|
|
);
|
|
|
|
assert_eq!(accounts[0].lamports(), transaction_accounts[0].1.lamports());
|
|
|
|
|
|
|
|
// Finalize program
|
|
|
|
let accounts = process_instruction(
|
|
|
|
vec![],
|
2023-05-10 23:29:06 -07:00
|
|
|
&bincode::serialize(&LoaderV4Instruction::TransferAuthority).unwrap(),
|
2023-03-23 10:13:47 -07:00
|
|
|
transaction_accounts.clone(),
|
2023-08-28 11:14:01 -07:00
|
|
|
&[(0, false, true), (3, true, false)],
|
2023-03-23 10:13:47 -07:00
|
|
|
Ok(()),
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
accounts[0].data().len(),
|
|
|
|
transaction_accounts[0].1.data().len(),
|
|
|
|
);
|
|
|
|
assert_eq!(accounts[0].lamports(), transaction_accounts[0].1.lamports());
|
|
|
|
|
2023-08-28 11:14:01 -07:00
|
|
|
// Error: Program must be deployed to be finalized
|
|
|
|
process_instruction(
|
|
|
|
vec![],
|
|
|
|
&bincode::serialize(&LoaderV4Instruction::TransferAuthority).unwrap(),
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(1, false, true), (3, true, false)],
|
|
|
|
Err(InstructionError::InvalidArgument),
|
|
|
|
);
|
|
|
|
|
2023-03-23 10:13:47 -07:00
|
|
|
// Error: Program is uninitialized
|
|
|
|
process_instruction(
|
|
|
|
vec![],
|
2023-05-10 23:29:06 -07:00
|
|
|
&bincode::serialize(&LoaderV4Instruction::TransferAuthority).unwrap(),
|
2023-03-23 10:13:47 -07:00
|
|
|
transaction_accounts.clone(),
|
2023-08-28 11:14:01 -07:00
|
|
|
&[(2, false, true), (3, true, false), (4, true, false)],
|
2023-03-23 10:13:47 -07:00
|
|
|
Err(InstructionError::InvalidAccountData),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Error: New authority did not sign
|
|
|
|
process_instruction(
|
|
|
|
vec![],
|
2023-05-10 23:29:06 -07:00
|
|
|
&bincode::serialize(&LoaderV4Instruction::TransferAuthority).unwrap(),
|
2023-03-23 10:13:47 -07:00
|
|
|
transaction_accounts,
|
2023-08-28 11:14:01 -07:00
|
|
|
&[(0, false, true), (3, true, false), (4, false, false)],
|
2023-03-23 10:13:47 -07:00
|
|
|
Err(InstructionError::MissingRequiredSignature),
|
|
|
|
);
|
|
|
|
|
2023-05-10 23:29:06 -07:00
|
|
|
test_loader_instruction_general_errors(LoaderV4Instruction::TransferAuthority);
|
2023-03-23 10:13:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_execute_program() {
|
|
|
|
let program_address = Pubkey::new_unique();
|
2023-08-28 11:14:01 -07:00
|
|
|
let authority_address = Pubkey::new_unique();
|
2023-03-23 10:13:47 -07:00
|
|
|
let transaction_accounts = vec![
|
|
|
|
(
|
|
|
|
program_address,
|
2023-08-28 11:14:01 -07:00
|
|
|
load_program_account_from_elf(
|
|
|
|
authority_address,
|
|
|
|
LoaderV4Status::Finalized,
|
2023-09-06 01:54:15 -07:00
|
|
|
"rodata_section",
|
2023-08-28 11:14:01 -07:00
|
|
|
),
|
2023-03-23 10:13:47 -07:00
|
|
|
),
|
|
|
|
(
|
|
|
|
Pubkey::new_unique(),
|
|
|
|
AccountSharedData::new(10000000, 32, &program_address),
|
|
|
|
),
|
|
|
|
(
|
|
|
|
Pubkey::new_unique(),
|
2023-05-10 23:29:06 -07:00
|
|
|
AccountSharedData::new(0, 0, &loader_v4::id()),
|
2023-03-23 10:13:47 -07:00
|
|
|
),
|
|
|
|
(
|
|
|
|
Pubkey::new_unique(),
|
2023-08-28 11:14:01 -07:00
|
|
|
load_program_account_from_elf(
|
|
|
|
authority_address,
|
|
|
|
LoaderV4Status::Retracted,
|
2023-09-06 01:54:15 -07:00
|
|
|
"rodata_section",
|
2023-08-28 11:14:01 -07:00
|
|
|
),
|
2023-03-23 10:13:47 -07:00
|
|
|
),
|
|
|
|
(
|
2023-08-16 10:50:23 -07:00
|
|
|
Pubkey::new_unique(),
|
2023-08-28 11:14:01 -07:00
|
|
|
load_program_account_from_elf(
|
|
|
|
authority_address,
|
|
|
|
LoaderV4Status::Finalized,
|
|
|
|
"invalid",
|
|
|
|
),
|
2023-03-23 10:13:47 -07:00
|
|
|
),
|
|
|
|
];
|
|
|
|
|
|
|
|
// Execute program
|
|
|
|
process_instruction(
|
|
|
|
vec![0],
|
|
|
|
&[0, 1, 2, 3],
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(1, false, true)],
|
|
|
|
Err(InstructionError::Custom(42)),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Error: Program not owned by loader
|
|
|
|
process_instruction(
|
|
|
|
vec![1],
|
|
|
|
&[0, 1, 2, 3],
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(1, false, true)],
|
|
|
|
Err(InstructionError::InvalidAccountOwner),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Error: Program is uninitialized
|
|
|
|
process_instruction(
|
|
|
|
vec![2],
|
|
|
|
&[0, 1, 2, 3],
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(1, false, true)],
|
|
|
|
Err(InstructionError::InvalidAccountData),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Error: Program is not deployed
|
|
|
|
process_instruction(
|
|
|
|
vec![3],
|
|
|
|
&[0, 1, 2, 3],
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
&[(1, false, true)],
|
|
|
|
Err(InstructionError::InvalidArgument),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Error: Program fails verification
|
|
|
|
process_instruction(
|
|
|
|
vec![4],
|
|
|
|
&[0, 1, 2, 3],
|
|
|
|
transaction_accounts,
|
|
|
|
&[(1, false, true)],
|
|
|
|
Err(InstructionError::InvalidAccountData),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|