1use {
2 miraland_measure::measure::Measure,
3 miraland_program_runtime::{
4 compute_budget::ComputeBudget,
5 ic_logger_msg,
6 invoke_context::InvokeContext,
7 loaded_programs::{
8 LoadProgramMetrics, LoadedProgram, LoadedProgramType, DELAY_VISIBILITY_SLOT_OFFSET,
9 },
10 log_collector::LogCollector,
11 stable_log,
12 },
13 solana_rbpf::{
14 aligned_memory::AlignedMemory,
15 declare_builtin_function, ebpf,
16 elf::Executable,
17 error::ProgramResult,
18 memory_region::{MemoryMapping, MemoryRegion},
19 program::{BuiltinProgram, FunctionRegistry},
20 vm::{Config, ContextObject, EbpfVm},
21 },
22 miraland_sdk::{
23 entrypoint::SUCCESS,
24 instruction::InstructionError,
25 loader_v4::{self, LoaderV4State, LoaderV4Status, DEPLOYMENT_COOLDOWN_IN_SLOTS},
26 loader_v4_instruction::LoaderV4Instruction,
27 program_utils::limited_deserialize,
28 pubkey::Pubkey,
29 saturating_add_assign,
30 transaction_context::{BorrowedAccount, InstructionContext},
31 },
32 std::{
33 cell::RefCell,
34 rc::Rc,
35 sync::{atomic::Ordering, Arc},
36 },
37};
38
39pub const DEFAULT_COMPUTE_UNITS: u64 = 2_000;
40
41pub fn get_state(data: &[u8]) -> Result<&LoaderV4State, InstructionError> {
42 unsafe {
43 let data = data
44 .get(0..LoaderV4State::program_data_offset())
45 .ok_or(InstructionError::AccountDataTooSmall)?
46 .try_into()
47 .unwrap();
48 Ok(std::mem::transmute::<
49 &[u8; LoaderV4State::program_data_offset()],
50 &LoaderV4State,
51 >(data))
52 }
53}
54
55fn get_state_mut(data: &mut [u8]) -> Result<&mut LoaderV4State, InstructionError> {
56 unsafe {
57 let data = data
58 .get_mut(0..LoaderV4State::program_data_offset())
59 .ok_or(InstructionError::AccountDataTooSmall)?
60 .try_into()
61 .unwrap();
62 Ok(std::mem::transmute::<
63 &mut [u8; LoaderV4State::program_data_offset()],
64 &mut LoaderV4State,
65 >(data))
66 }
67}
68
69pub fn create_program_runtime_environment_v2<'a>(
70 compute_budget: &ComputeBudget,
71 debugging_features: bool,
72) -> BuiltinProgram<InvokeContext<'a>> {
73 let config = Config {
74 max_call_depth: compute_budget.max_call_depth,
75 stack_frame_size: compute_budget.stack_frame_size,
76 enable_address_translation: true, enable_stack_frame_gaps: false,
78 instruction_meter_checkpoint_distance: 10000,
79 enable_instruction_meter: true,
80 enable_instruction_tracing: debugging_features,
81 enable_symbol_and_section_labels: debugging_features,
82 reject_broken_elfs: true,
83 noop_instruction_rate: 256,
84 sanitize_user_provided_values: true,
85 external_internal_function_hash_collision: true,
86 reject_callx_r10: true,
87 enable_sbpf_v1: false,
88 enable_sbpf_v2: true,
89 optimize_rodata: true,
90 new_elf_parser: true,
91 aligned_memory_mapping: true,
92 };
94 BuiltinProgram::new_loader(config, FunctionRegistry::default())
95}
96
97fn calculate_heap_cost(heap_size: u32, heap_cost: u64) -> u64 {
98 const KIBIBYTE: u64 = 1024;
99 const PAGE_SIZE_KB: u64 = 32;
100 u64::from(heap_size)
101 .saturating_add(PAGE_SIZE_KB.saturating_mul(KIBIBYTE).saturating_sub(1))
102 .checked_div(PAGE_SIZE_KB.saturating_mul(KIBIBYTE))
103 .expect("PAGE_SIZE_KB * KIBIBYTE > 0")
104 .saturating_sub(1)
105 .saturating_mul(heap_cost)
106}
107
108pub fn create_vm<'a, 'b>(
110 invoke_context: &'a mut InvokeContext<'b>,
111 program: &'a Executable<InvokeContext<'b>>,
112) -> Result<EbpfVm<'a, InvokeContext<'b>>, Box<dyn std::error::Error>> {
113 let config = program.get_config();
114 let sbpf_version = program.get_sbpf_version();
115 let compute_budget = invoke_context.get_compute_budget();
116 let heap_size = compute_budget.heap_size;
117 invoke_context.consume_checked(calculate_heap_cost(heap_size, compute_budget.heap_cost))?;
118 let mut stack = AlignedMemory::<{ ebpf::HOST_ALIGN }>::zero_filled(config.stack_size());
119 let mut heap = AlignedMemory::<{ ebpf::HOST_ALIGN }>::zero_filled(
120 usize::try_from(compute_budget.heap_size).unwrap(),
121 );
122 let stack_len = stack.len();
123 let regions: Vec<MemoryRegion> = vec![
124 program.get_ro_region(),
125 MemoryRegion::new_writable_gapped(stack.as_slice_mut(), ebpf::MM_STACK_START, 0),
126 MemoryRegion::new_writable(heap.as_slice_mut(), ebpf::MM_HEAP_START),
127 ];
128 let log_collector = invoke_context.get_log_collector();
129 let memory_mapping = MemoryMapping::new(regions, config, sbpf_version).map_err(|err| {
130 ic_logger_msg!(log_collector, "Failed to create SBF VM: {}", err);
131 Box::new(InstructionError::ProgramEnvironmentSetupFailure)
132 })?;
133 Ok(EbpfVm::new(
134 program.get_loader().clone(),
135 sbpf_version,
136 invoke_context,
137 memory_mapping,
138 stack_len,
139 ))
140}
141
142fn execute<'a, 'b: 'a>(
143 invoke_context: &'a mut InvokeContext<'b>,
144 executable: &'a Executable<InvokeContext<'static>>,
145) -> Result<(), Box<dyn std::error::Error>> {
146 let executable =
149 unsafe { std::mem::transmute::<_, &'a Executable<InvokeContext<'b>>>(executable) };
150 let log_collector = invoke_context.get_log_collector();
151 let stack_height = invoke_context.get_stack_height();
152 let transaction_context = &invoke_context.transaction_context;
153 let instruction_context = transaction_context.get_current_instruction_context()?;
154 let program_id = *instruction_context.get_last_program_key(transaction_context)?;
155 #[cfg(any(target_os = "windows", not(target_arch = "x86_64")))]
156 let use_jit = false;
157 #[cfg(all(not(target_os = "windows"), target_arch = "x86_64"))]
158 let use_jit = executable.get_compiled_program().is_some();
159
160 let compute_meter_prev = invoke_context.get_remaining();
161 let mut create_vm_time = Measure::start("create_vm");
162 let mut vm = create_vm(invoke_context, executable)?;
163 create_vm_time.stop();
164
165 let mut execute_time = Measure::start("execute");
166 stable_log::program_invoke(&log_collector, &program_id, stack_height);
167 let (compute_units_consumed, result) = vm.execute_program(executable, !use_jit);
168 drop(vm);
169 ic_logger_msg!(
170 log_collector,
171 "Program {} consumed {} of {} compute units",
172 &program_id,
173 compute_units_consumed,
174 compute_meter_prev
175 );
176 execute_time.stop();
177
178 let timings = &mut invoke_context.timings;
179 timings.create_vm_us = timings.create_vm_us.saturating_add(create_vm_time.as_us());
180 timings.execute_us = timings.execute_us.saturating_add(execute_time.as_us());
181
182 match result {
183 ProgramResult::Ok(status) if status != SUCCESS => {
184 let error: InstructionError = status.into();
185 Err(error.into())
186 }
187 ProgramResult::Err(error) => Err(error.into()),
188 _ => Ok(()),
189 }
190}
191
192fn check_program_account(
193 log_collector: &Option<Rc<RefCell<LogCollector>>>,
194 instruction_context: &InstructionContext,
195 program: &BorrowedAccount,
196 authority_address: &Pubkey,
197) -> Result<LoaderV4State, InstructionError> {
198 if !loader_v4::check_id(program.get_owner()) {
199 ic_logger_msg!(log_collector, "Program not owned by loader");
200 return Err(InstructionError::InvalidAccountOwner);
201 }
202 if program.get_data().is_empty() {
203 ic_logger_msg!(log_collector, "Program is uninitialized");
204 return Err(InstructionError::InvalidAccountData);
205 }
206 let state = get_state(program.get_data())?;
207 if !program.is_writable() {
208 ic_logger_msg!(log_collector, "Program is not writeable");
209 return Err(InstructionError::InvalidArgument);
210 }
211 if !instruction_context.is_instruction_account_signer(1)? {
212 ic_logger_msg!(log_collector, "Authority did not sign");
213 return Err(InstructionError::MissingRequiredSignature);
214 }
215 if state.authority_address != *authority_address {
216 ic_logger_msg!(log_collector, "Incorrect authority provided");
217 return Err(InstructionError::IncorrectAuthority);
218 }
219 if matches!(state.status, LoaderV4Status::Finalized) {
220 ic_logger_msg!(log_collector, "Program is finalized");
221 return Err(InstructionError::Immutable);
222 }
223 Ok(*state)
224}
225
226pub fn process_instruction_write(
227 invoke_context: &mut InvokeContext,
228 offset: u32,
229 bytes: Vec<u8>,
230) -> Result<(), InstructionError> {
231 let log_collector = invoke_context.get_log_collector();
232 let transaction_context = &invoke_context.transaction_context;
233 let instruction_context = transaction_context.get_current_instruction_context()?;
234 let mut program = instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
235 let authority_address = instruction_context
236 .get_index_of_instruction_account_in_transaction(1)
237 .and_then(|index| transaction_context.get_key_of_account_at_index(index))?;
238 let state = check_program_account(
239 &log_collector,
240 instruction_context,
241 &program,
242 authority_address,
243 )?;
244 if !matches!(state.status, LoaderV4Status::Retracted) {
245 ic_logger_msg!(log_collector, "Program is not retracted");
246 return Err(InstructionError::InvalidArgument);
247 }
248 let end_offset = (offset as usize).saturating_add(bytes.len());
249 program
250 .get_data_mut(&invoke_context.feature_set)?
251 .get_mut(
252 LoaderV4State::program_data_offset().saturating_add(offset as usize)
253 ..LoaderV4State::program_data_offset().saturating_add(end_offset),
254 )
255 .ok_or_else(|| {
256 ic_logger_msg!(log_collector, "Write out of bounds");
257 InstructionError::AccountDataTooSmall
258 })?
259 .copy_from_slice(&bytes);
260 Ok(())
261}
262
263pub fn process_instruction_truncate(
264 invoke_context: &mut InvokeContext,
265 new_size: u32,
266) -> Result<(), InstructionError> {
267 let log_collector = invoke_context.get_log_collector();
268 let transaction_context = &invoke_context.transaction_context;
269 let instruction_context = transaction_context.get_current_instruction_context()?;
270 let mut program = instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
271 let authority_address = instruction_context
272 .get_index_of_instruction_account_in_transaction(1)
273 .and_then(|index| transaction_context.get_key_of_account_at_index(index))?;
274 let is_initialization =
275 new_size > 0 && program.get_data().len() < LoaderV4State::program_data_offset();
276 if is_initialization {
277 if !loader_v4::check_id(program.get_owner()) {
278 ic_logger_msg!(log_collector, "Program not owned by loader");
279 return Err(InstructionError::InvalidAccountOwner);
280 }
281 if !program.is_writable() {
282 ic_logger_msg!(log_collector, "Program is not writeable");
283 return Err(InstructionError::InvalidArgument);
284 }
285 if !program.is_signer() {
286 ic_logger_msg!(log_collector, "Program did not sign");
287 return Err(InstructionError::MissingRequiredSignature);
288 }
289 if !instruction_context.is_instruction_account_signer(1)? {
290 ic_logger_msg!(log_collector, "Authority did not sign");
291 return Err(InstructionError::MissingRequiredSignature);
292 }
293 } else {
294 let state = check_program_account(
295 &log_collector,
296 instruction_context,
297 &program,
298 authority_address,
299 )?;
300 if !matches!(state.status, LoaderV4Status::Retracted) {
301 ic_logger_msg!(log_collector, "Program is not retracted");
302 return Err(InstructionError::InvalidArgument);
303 }
304 }
305 let required_lamports = if new_size == 0 {
306 0
307 } else {
308 let rent = invoke_context.get_sysvar_cache().get_rent()?;
309 rent.minimum_balance(LoaderV4State::program_data_offset().saturating_add(new_size as usize))
310 };
311 match program.get_lamports().cmp(&required_lamports) {
312 std::cmp::Ordering::Less => {
313 ic_logger_msg!(
314 log_collector,
315 "Insufficient lamports, {} are required",
316 required_lamports
317 );
318 return Err(InstructionError::InsufficientFunds);
319 }
320 std::cmp::Ordering::Greater => {
321 let mut recipient =
322 instruction_context.try_borrow_instruction_account(transaction_context, 2)?;
323 if !instruction_context.is_instruction_account_writable(2)? {
324 ic_logger_msg!(log_collector, "Recipient is not writeable");
325 return Err(InstructionError::InvalidArgument);
326 }
327 let lamports_to_receive = program.get_lamports().saturating_sub(required_lamports);
328 program.checked_sub_lamports(lamports_to_receive, &invoke_context.feature_set)?;
329 recipient.checked_add_lamports(lamports_to_receive, &invoke_context.feature_set)?;
330 }
331 std::cmp::Ordering::Equal => {}
332 }
333 if new_size == 0 {
334 program.set_data_length(0, &invoke_context.feature_set)?;
335 } else {
336 program.set_data_length(
337 LoaderV4State::program_data_offset().saturating_add(new_size as usize),
338 &invoke_context.feature_set,
339 )?;
340 if is_initialization {
341 let state = get_state_mut(program.get_data_mut(&invoke_context.feature_set)?)?;
342 state.slot = 0;
343 state.status = LoaderV4Status::Retracted;
344 state.authority_address = *authority_address;
345 }
346 }
347 Ok(())
348}
349
350pub fn process_instruction_deploy(
351 invoke_context: &mut InvokeContext,
352) -> Result<(), InstructionError> {
353 let log_collector = invoke_context.get_log_collector();
354 let transaction_context = &invoke_context.transaction_context;
355 let instruction_context = transaction_context.get_current_instruction_context()?;
356 let mut program = instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
357 let authority_address = instruction_context
358 .get_index_of_instruction_account_in_transaction(1)
359 .and_then(|index| transaction_context.get_key_of_account_at_index(index))?;
360 let source_program = instruction_context
361 .try_borrow_instruction_account(transaction_context, 2)
362 .ok();
363 let state = check_program_account(
364 &log_collector,
365 instruction_context,
366 &program,
367 authority_address,
368 )?;
369 let current_slot = invoke_context.get_sysvar_cache().get_clock()?.slot;
370
371 if state.slot != 0 && state.slot.saturating_add(DEPLOYMENT_COOLDOWN_IN_SLOTS) > current_slot {
375 ic_logger_msg!(
376 log_collector,
377 "Program was deployed recently, cooldown still in effect"
378 );
379 return Err(InstructionError::InvalidArgument);
380 }
381 if !matches!(state.status, LoaderV4Status::Retracted) {
382 ic_logger_msg!(log_collector, "Destination program is not retracted");
383 return Err(InstructionError::InvalidArgument);
384 }
385 let buffer = if let Some(ref source_program) = source_program {
386 let source_state = check_program_account(
387 &log_collector,
388 instruction_context,
389 source_program,
390 authority_address,
391 )?;
392 if !matches!(source_state.status, LoaderV4Status::Retracted) {
393 ic_logger_msg!(log_collector, "Source program is not retracted");
394 return Err(InstructionError::InvalidArgument);
395 }
396 source_program
397 } else {
398 &program
399 };
400
401 let programdata = buffer
402 .get_data()
403 .get(LoaderV4State::program_data_offset()..)
404 .ok_or(InstructionError::AccountDataTooSmall)?;
405
406 let deployment_slot = state.slot;
407 let effective_slot = deployment_slot.saturating_add(DELAY_VISIBILITY_SLOT_OFFSET);
408
409 let mut load_program_metrics = LoadProgramMetrics {
410 program_id: buffer.get_key().to_string(),
411 ..LoadProgramMetrics::default()
412 };
413 let executor = LoadedProgram::new(
414 &loader_v4::id(),
415 invoke_context
416 .programs_modified_by_tx
417 .environments
418 .program_runtime_v2
419 .clone(),
420 deployment_slot,
421 effective_slot,
422 programdata,
423 buffer.get_data().len(),
424 &mut load_program_metrics,
425 )
426 .map_err(|err| {
427 ic_logger_msg!(log_collector, "{}", err);
428 InstructionError::InvalidAccountData
429 })?;
430 load_program_metrics.submit_datapoint(&mut invoke_context.timings);
431 if let Some(mut source_program) = source_program {
432 let rent = invoke_context.get_sysvar_cache().get_rent()?;
433 let required_lamports = rent.minimum_balance(source_program.get_data().len());
434 let transfer_lamports = required_lamports.saturating_sub(program.get_lamports());
435 program.set_data_from_slice(source_program.get_data(), &invoke_context.feature_set)?;
436 source_program.set_data_length(0, &invoke_context.feature_set)?;
437 source_program.checked_sub_lamports(transfer_lamports, &invoke_context.feature_set)?;
438 program.checked_add_lamports(transfer_lamports, &invoke_context.feature_set)?;
439 }
440 let state = get_state_mut(program.get_data_mut(&invoke_context.feature_set)?)?;
441 state.slot = current_slot;
442 state.status = LoaderV4Status::Deployed;
443
444 if let Some(old_entry) = invoke_context.find_program_in_cache(program.get_key()) {
445 executor.tx_usage_counter.store(
446 old_entry.tx_usage_counter.load(Ordering::Relaxed),
447 Ordering::Relaxed,
448 );
449 executor.ix_usage_counter.store(
450 old_entry.ix_usage_counter.load(Ordering::Relaxed),
451 Ordering::Relaxed,
452 );
453 }
454 invoke_context
455 .programs_modified_by_tx
456 .replenish(*program.get_key(), Arc::new(executor));
457 Ok(())
458}
459
460pub fn process_instruction_retract(
461 invoke_context: &mut InvokeContext,
462) -> Result<(), InstructionError> {
463 let log_collector = invoke_context.get_log_collector();
464 let transaction_context = &invoke_context.transaction_context;
465 let instruction_context = transaction_context.get_current_instruction_context()?;
466 let mut program = instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
467
468 let authority_address = instruction_context
469 .get_index_of_instruction_account_in_transaction(1)
470 .and_then(|index| transaction_context.get_key_of_account_at_index(index))?;
471 let state = check_program_account(
472 &log_collector,
473 instruction_context,
474 &program,
475 authority_address,
476 )?;
477 let current_slot = invoke_context.get_sysvar_cache().get_clock()?.slot;
478 if state.slot.saturating_add(DEPLOYMENT_COOLDOWN_IN_SLOTS) > current_slot {
479 ic_logger_msg!(
480 log_collector,
481 "Program was deployed recently, cooldown still in effect"
482 );
483 return Err(InstructionError::InvalidArgument);
484 }
485 if matches!(state.status, LoaderV4Status::Retracted) {
486 ic_logger_msg!(log_collector, "Program is not deployed");
487 return Err(InstructionError::InvalidArgument);
488 }
489 let state = get_state_mut(program.get_data_mut(&invoke_context.feature_set)?)?;
490 state.status = LoaderV4Status::Retracted;
491 Ok(())
492}
493
494pub fn process_instruction_transfer_authority(
495 invoke_context: &mut InvokeContext,
496) -> Result<(), InstructionError> {
497 let log_collector = invoke_context.get_log_collector();
498 let transaction_context = &invoke_context.transaction_context;
499 let instruction_context = transaction_context.get_current_instruction_context()?;
500 let mut program = instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
501 let authority_address = instruction_context
502 .get_index_of_instruction_account_in_transaction(1)
503 .and_then(|index| transaction_context.get_key_of_account_at_index(index))?;
504 let new_authority_address = instruction_context
505 .get_index_of_instruction_account_in_transaction(2)
506 .and_then(|index| transaction_context.get_key_of_account_at_index(index))
507 .ok()
508 .cloned();
509 let _state = check_program_account(
510 &log_collector,
511 instruction_context,
512 &program,
513 authority_address,
514 )?;
515 if new_authority_address.is_some() && !instruction_context.is_instruction_account_signer(2)? {
516 ic_logger_msg!(log_collector, "New authority did not sign");
517 return Err(InstructionError::MissingRequiredSignature);
518 }
519 let state = get_state_mut(program.get_data_mut(&invoke_context.feature_set)?)?;
520 if let Some(new_authority_address) = new_authority_address {
521 state.authority_address = new_authority_address;
522 } else if matches!(state.status, LoaderV4Status::Deployed) {
523 state.status = LoaderV4Status::Finalized;
524 } else {
525 ic_logger_msg!(log_collector, "Program must be deployed to be finalized");
526 return Err(InstructionError::InvalidArgument);
527 }
528 Ok(())
529}
530
531declare_builtin_function!(
532 Entrypoint,
533 fn rust(
534 invoke_context: &mut InvokeContext,
535 _arg0: u64,
536 _arg1: u64,
537 _arg2: u64,
538 _arg3: u64,
539 _arg4: u64,
540 _memory_mapping: &mut MemoryMapping,
541 ) -> Result<u64, Box<dyn std::error::Error>> {
542 process_instruction_inner(invoke_context)
543 }
544);
545
546pub fn process_instruction_inner(
547 invoke_context: &mut InvokeContext,
548) -> Result<u64, Box<dyn std::error::Error>> {
549 let log_collector = invoke_context.get_log_collector();
550 let transaction_context = &invoke_context.transaction_context;
551 let instruction_context = transaction_context.get_current_instruction_context()?;
552 let instruction_data = instruction_context.get_instruction_data();
553 let program_id = instruction_context.get_last_program_key(transaction_context)?;
554 if loader_v4::check_id(program_id) {
555 invoke_context.consume_checked(DEFAULT_COMPUTE_UNITS)?;
556 match limited_deserialize(instruction_data)? {
557 LoaderV4Instruction::Write { offset, bytes } => {
558 process_instruction_write(invoke_context, offset, bytes)
559 }
560 LoaderV4Instruction::Truncate { new_size } => {
561 process_instruction_truncate(invoke_context, new_size)
562 }
563 LoaderV4Instruction::Deploy => process_instruction_deploy(invoke_context),
564 LoaderV4Instruction::Retract => process_instruction_retract(invoke_context),
565 LoaderV4Instruction::TransferAuthority => {
566 process_instruction_transfer_authority(invoke_context)
567 }
568 }
569 .map_err(|err| Box::new(err) as Box<dyn std::error::Error>)
570 } else {
571 let program = instruction_context.try_borrow_last_program_account(transaction_context)?;
572 if !loader_v4::check_id(program.get_owner()) {
573 ic_logger_msg!(log_collector, "Program not owned by loader");
574 return Err(Box::new(InstructionError::InvalidAccountOwner));
575 }
576 if program.get_data().is_empty() {
577 ic_logger_msg!(log_collector, "Program is uninitialized");
578 return Err(Box::new(InstructionError::InvalidAccountData));
579 }
580 let state = get_state(program.get_data())?;
581 if matches!(state.status, LoaderV4Status::Retracted) {
582 ic_logger_msg!(log_collector, "Program is not deployed");
583 return Err(Box::new(InstructionError::InvalidArgument));
584 }
585 let mut get_or_create_executor_time = Measure::start("get_or_create_executor_time");
586 let loaded_program = invoke_context
587 .find_program_in_cache(program.get_key())
588 .ok_or_else(|| {
589 ic_logger_msg!(log_collector, "Program is not cached");
590 InstructionError::InvalidAccountData
591 })?;
592 get_or_create_executor_time.stop();
593 saturating_add_assign!(
594 invoke_context.timings.get_or_create_executor_us,
595 get_or_create_executor_time.as_us()
596 );
597 drop(program);
598 loaded_program
599 .ix_usage_counter
600 .fetch_add(1, Ordering::Relaxed);
601 match &loaded_program.program {
602 LoadedProgramType::FailedVerification(_)
603 | LoadedProgramType::Closed
604 | LoadedProgramType::DelayVisibility => {
605 ic_logger_msg!(log_collector, "Program is not deployed");
606 Err(Box::new(InstructionError::InvalidAccountData) as Box<dyn std::error::Error>)
607 }
608 LoadedProgramType::Typed(executable) => execute(invoke_context, executable),
609 _ => Err(Box::new(InstructionError::IncorrectProgramId) as Box<dyn std::error::Error>),
610 }
611 }
612 .map(|_| 0)
613}
614
615#[cfg(test)]
616mod tests {
617 use {
618 super::*,
619 miraland_program_runtime::invoke_context::mock_process_instruction,
620 miraland_sdk::{
621 account::{
622 create_account_shared_data_for_test, AccountSharedData, ReadableAccount,
623 WritableAccount,
624 },
625 instruction::AccountMeta,
626 slot_history::Slot,
627 sysvar::{clock, rent},
628 transaction_context::IndexOfAccount,
629 },
630 std::{fs::File, io::Read, path::Path},
631 };
632
633 pub fn load_all_invoked_programs(invoke_context: &mut InvokeContext) {
634 let mut load_program_metrics = LoadProgramMetrics::default();
635 let num_accounts = invoke_context.transaction_context.get_number_of_accounts();
636 for index in 0..num_accounts {
637 let account = invoke_context
638 .transaction_context
639 .get_account_at_index(index)
640 .expect("Failed to get the account")
641 .borrow();
642
643 let owner = account.owner();
644 if loader_v4::check_id(owner) {
645 let pubkey = invoke_context
646 .transaction_context
647 .get_key_of_account_at_index(index)
648 .expect("Failed to get account key");
649
650 if let Some(programdata) =
651 account.data().get(LoaderV4State::program_data_offset()..)
652 {
653 if let Ok(loaded_program) = LoadedProgram::new(
654 &loader_v4::id(),
655 invoke_context
656 .programs_modified_by_tx
657 .environments
658 .program_runtime_v2
659 .clone(),
660 0,
661 0,
662 programdata,
663 account.data().len(),
664 &mut load_program_metrics,
665 ) {
666 invoke_context.programs_modified_by_tx.set_slot_for_tests(0);
667 invoke_context
668 .programs_modified_by_tx
669 .replenish(*pubkey, Arc::new(loaded_program));
670 }
671 }
672 }
673 }
674 }
675
676 fn process_instruction(
677 program_indices: Vec<IndexOfAccount>,
678 instruction_data: &[u8],
679 transaction_accounts: Vec<(Pubkey, AccountSharedData)>,
680 instruction_accounts: &[(IndexOfAccount, bool, bool)],
681 expected_result: Result<(), InstructionError>,
682 ) -> Vec<AccountSharedData> {
683 let instruction_accounts = instruction_accounts
684 .iter()
685 .map(
686 |(index_in_transaction, is_signer, is_writable)| AccountMeta {
687 pubkey: transaction_accounts[*index_in_transaction as usize].0,
688 is_signer: *is_signer,
689 is_writable: *is_writable,
690 },
691 )
692 .collect::<Vec<_>>();
693 mock_process_instruction(
694 &loader_v4::id(),
695 program_indices,
696 instruction_data,
697 transaction_accounts,
698 instruction_accounts,
699 expected_result,
700 Entrypoint::vm,
701 |invoke_context| {
702 invoke_context
703 .programs_modified_by_tx
704 .environments
705 .program_runtime_v2 = Arc::new(create_program_runtime_environment_v2(
706 &ComputeBudget::default(),
707 false,
708 ));
709 load_all_invoked_programs(invoke_context);
710 },
711 |_invoke_context| {},
712 )
713 }
714
715 fn load_program_account_from_elf(
716 authority_address: Pubkey,
717 status: LoaderV4Status,
718 path: &str,
719 ) -> AccountSharedData {
720 let path = Path::new("test_elfs/out/").join(path).with_extension("so");
721 let mut file = File::open(path).expect("file open failed");
722 let mut elf_bytes = Vec::new();
723 file.read_to_end(&mut elf_bytes).unwrap();
724 let rent = rent::Rent::default();
725 let account_size =
726 loader_v4::LoaderV4State::program_data_offset().saturating_add(elf_bytes.len());
727 let mut program_account = AccountSharedData::new(
728 rent.minimum_balance(account_size),
729 account_size,
730 &loader_v4::id(),
731 );
732 let state = get_state_mut(program_account.data_as_mut_slice()).unwrap();
733 state.slot = 0;
734 state.authority_address = authority_address;
735 state.status = status;
736 program_account.data_as_mut_slice()[loader_v4::LoaderV4State::program_data_offset()..]
737 .copy_from_slice(&elf_bytes);
738 program_account
739 }
740
741 fn clock(slot: Slot) -> AccountSharedData {
742 let clock = clock::Clock {
743 slot,
744 ..clock::Clock::default()
745 };
746 create_account_shared_data_for_test(&clock)
747 }
748
749 fn test_loader_instruction_general_errors(instruction: LoaderV4Instruction) {
750 let instruction = bincode::serialize(&instruction).unwrap();
751 let authority_address = Pubkey::new_unique();
752 let transaction_accounts = vec![
753 (
754 Pubkey::new_unique(),
755 load_program_account_from_elf(
756 authority_address,
757 LoaderV4Status::Deployed,
758 "relative_call",
759 ),
760 ),
761 (
762 authority_address,
763 AccountSharedData::new(0, 0, &Pubkey::new_unique()),
764 ),
765 (
766 Pubkey::new_unique(),
767 load_program_account_from_elf(
768 authority_address,
769 LoaderV4Status::Finalized,
770 "relative_call",
771 ),
772 ),
773 (
774 clock::id(),
775 create_account_shared_data_for_test(&clock::Clock::default()),
776 ),
777 (
778 rent::id(),
779 create_account_shared_data_for_test(&rent::Rent::default()),
780 ),
781 ];
782
783 process_instruction(
785 vec![],
786 &instruction,
787 transaction_accounts.clone(),
788 &[],
789 Err(InstructionError::NotEnoughAccountKeys),
790 );
791
792 process_instruction(
794 vec![],
795 &instruction,
796 transaction_accounts.clone(),
797 &[(0, false, true)],
798 Err(InstructionError::NotEnoughAccountKeys),
799 );
800
801 process_instruction(
803 vec![],
804 &instruction,
805 transaction_accounts.clone(),
806 &[(1, false, true), (1, true, false), (2, true, true)],
807 Err(InstructionError::InvalidAccountOwner),
808 );
809
810 process_instruction(
812 vec![],
813 &instruction,
814 transaction_accounts.clone(),
815 &[(0, false, false), (1, true, false), (2, true, true)],
816 Err(InstructionError::InvalidArgument),
817 );
818
819 process_instruction(
821 vec![],
822 &instruction,
823 transaction_accounts.clone(),
824 &[(0, false, true), (1, false, false), (2, true, true)],
825 Err(InstructionError::MissingRequiredSignature),
826 );
827
828 process_instruction(
830 vec![],
831 &instruction,
832 transaction_accounts.clone(),
833 &[(2, false, true), (1, true, false), (0, true, true)],
834 Err(InstructionError::Immutable),
835 );
836
837 process_instruction(
839 vec![],
840 &instruction,
841 transaction_accounts,
842 &[(0, false, true), (2, true, false), (2, true, true)],
843 Err(InstructionError::IncorrectAuthority),
844 );
845 }
846
847 #[test]
848 fn test_loader_instruction_write() {
849 let authority_address = Pubkey::new_unique();
850 let transaction_accounts = vec![
851 (
852 Pubkey::new_unique(),
853 load_program_account_from_elf(
854 authority_address,
855 LoaderV4Status::Retracted,
856 "relative_call",
857 ),
858 ),
859 (
860 authority_address,
861 AccountSharedData::new(0, 0, &Pubkey::new_unique()),
862 ),
863 (
864 Pubkey::new_unique(),
865 load_program_account_from_elf(
866 authority_address,
867 LoaderV4Status::Deployed,
868 "relative_call",
869 ),
870 ),
871 (
872 clock::id(),
873 create_account_shared_data_for_test(&clock::Clock::default()),
874 ),
875 (
876 rent::id(),
877 create_account_shared_data_for_test(&rent::Rent::default()),
878 ),
879 ];
880
881 process_instruction(
883 vec![],
884 &bincode::serialize(&LoaderV4Instruction::Write {
885 offset: 2,
886 bytes: vec![8, 8, 8, 8],
887 })
888 .unwrap(),
889 transaction_accounts.clone(),
890 &[(0, false, true), (1, true, false)],
891 Ok(()),
892 );
893
894 process_instruction(
896 vec![],
897 &bincode::serialize(&LoaderV4Instruction::Write {
898 offset: 2,
899 bytes: Vec::new(),
900 })
901 .unwrap(),
902 transaction_accounts.clone(),
903 &[(0, false, true), (1, true, false)],
904 Ok(()),
905 );
906
907 process_instruction(
909 vec![],
910 &bincode::serialize(&LoaderV4Instruction::Write {
911 offset: 8,
912 bytes: vec![8, 8, 8, 8],
913 })
914 .unwrap(),
915 transaction_accounts.clone(),
916 &[(2, false, true), (1, true, false)],
917 Err(InstructionError::InvalidArgument),
918 );
919
920 process_instruction(
922 vec![],
923 &bincode::serialize(&LoaderV4Instruction::Write {
924 offset: transaction_accounts[0]
925 .1
926 .data()
927 .len()
928 .saturating_sub(loader_v4::LoaderV4State::program_data_offset())
929 .saturating_sub(3) as u32,
930 bytes: vec![8, 8, 8, 8],
931 })
932 .unwrap(),
933 transaction_accounts.clone(),
934 &[(0, false, true), (1, true, false)],
935 Err(InstructionError::AccountDataTooSmall),
936 );
937
938 test_loader_instruction_general_errors(LoaderV4Instruction::Write {
939 offset: 0,
940 bytes: Vec::new(),
941 });
942 }
943
944 #[test]
945 fn test_loader_instruction_truncate() {
946 let authority_address = Pubkey::new_unique();
947 let mut transaction_accounts = vec![
948 (
949 Pubkey::new_unique(),
950 load_program_account_from_elf(
951 authority_address,
952 LoaderV4Status::Retracted,
953 "relative_call",
954 ),
955 ),
956 (
957 authority_address,
958 AccountSharedData::new(0, 0, &Pubkey::new_unique()),
959 ),
960 (
961 Pubkey::new_unique(),
962 AccountSharedData::new(0, 0, &loader_v4::id()),
963 ),
964 (
965 Pubkey::new_unique(),
966 AccountSharedData::new(40000000, 0, &loader_v4::id()),
967 ),
968 (
969 Pubkey::new_unique(),
970 load_program_account_from_elf(
971 authority_address,
972 LoaderV4Status::Retracted,
973 "rodata_section",
974 ),
975 ),
976 (
977 Pubkey::new_unique(),
978 load_program_account_from_elf(
979 authority_address,
980 LoaderV4Status::Deployed,
981 "relative_call",
982 ),
983 ),
984 (
985 clock::id(),
986 create_account_shared_data_for_test(&clock::Clock::default()),
987 ),
988 (
989 rent::id(),
990 create_account_shared_data_for_test(&rent::Rent::default()),
991 ),
992 ];
993
994 let accounts = process_instruction(
996 vec![],
997 &bincode::serialize(&LoaderV4Instruction::Truncate {
998 new_size: transaction_accounts[0]
999 .1
1000 .data()
1001 .len()
1002 .saturating_sub(loader_v4::LoaderV4State::program_data_offset())
1003 as u32,
1004 })
1005 .unwrap(),
1006 transaction_accounts.clone(),
1007 &[(0, false, true), (1, true, false)],
1008 Ok(()),
1009 );
1010 assert_eq!(
1011 accounts[0].data().len(),
1012 transaction_accounts[0].1.data().len(),
1013 );
1014 assert_eq!(accounts[2].lamports(), transaction_accounts[2].1.lamports());
1015 let lamports = transaction_accounts[4].1.lamports();
1016 transaction_accounts[0].1.set_lamports(lamports);
1017
1018 let accounts = process_instruction(
1020 vec![],
1021 &bincode::serialize(&LoaderV4Instruction::Truncate {
1022 new_size: transaction_accounts[0]
1023 .1
1024 .data()
1025 .len()
1026 .saturating_sub(loader_v4::LoaderV4State::program_data_offset())
1027 as u32,
1028 })
1029 .unwrap(),
1030 transaction_accounts.clone(),
1031 &[(3, true, true), (1, true, false), (2, false, true)],
1032 Ok(()),
1033 );
1034 assert_eq!(
1035 accounts[3].data().len(),
1036 transaction_accounts[0].1.data().len(),
1037 );
1038
1039 let accounts = process_instruction(
1041 vec![],
1042 &bincode::serialize(&LoaderV4Instruction::Truncate {
1043 new_size: transaction_accounts[4]
1044 .1
1045 .data()
1046 .len()
1047 .saturating_sub(loader_v4::LoaderV4State::program_data_offset())
1048 as u32,
1049 })
1050 .unwrap(),
1051 transaction_accounts.clone(),
1052 &[(0, false, true), (1, true, false)],
1053 Ok(()),
1054 );
1055 assert_eq!(
1056 accounts[0].data().len(),
1057 transaction_accounts[4].1.data().len(),
1058 );
1059
1060 let accounts = process_instruction(
1062 vec![],
1063 &bincode::serialize(&LoaderV4Instruction::Truncate {
1064 new_size: transaction_accounts[0]
1065 .1
1066 .data()
1067 .len()
1068 .saturating_sub(loader_v4::LoaderV4State::program_data_offset())
1069 as u32,
1070 })
1071 .unwrap(),
1072 transaction_accounts.clone(),
1073 &[(4, false, true), (1, true, false), (2, false, true)],
1074 Ok(()),
1075 );
1076 assert_eq!(
1077 accounts[4].data().len(),
1078 transaction_accounts[0].1.data().len(),
1079 );
1080 assert_eq!(
1081 accounts[2].lamports(),
1082 transaction_accounts[2].1.lamports().saturating_add(
1083 transaction_accounts[4]
1084 .1
1085 .lamports()
1086 .saturating_sub(accounts[4].lamports())
1087 ),
1088 );
1089
1090 let accounts = process_instruction(
1092 vec![],
1093 &bincode::serialize(&LoaderV4Instruction::Truncate { new_size: 0 }).unwrap(),
1094 transaction_accounts.clone(),
1095 &[(0, false, true), (1, true, false), (2, false, true)],
1096 Ok(()),
1097 );
1098 assert_eq!(accounts[0].data().len(), 0);
1099 assert_eq!(
1100 accounts[2].lamports(),
1101 transaction_accounts[2].1.lamports().saturating_add(
1102 transaction_accounts[0]
1103 .1
1104 .lamports()
1105 .saturating_sub(accounts[0].lamports())
1106 ),
1107 );
1108
1109 process_instruction(
1111 vec![],
1112 &bincode::serialize(&LoaderV4Instruction::Truncate { new_size: 8 }).unwrap(),
1113 transaction_accounts.clone(),
1114 &[(1, false, true), (1, true, false), (2, true, true)],
1115 Err(InstructionError::InvalidAccountOwner),
1116 );
1117
1118 process_instruction(
1120 vec![],
1121 &bincode::serialize(&LoaderV4Instruction::Truncate { new_size: 8 }).unwrap(),
1122 transaction_accounts.clone(),
1123 &[(3, false, false), (1, true, false), (2, true, true)],
1124 Err(InstructionError::InvalidArgument),
1125 );
1126
1127 process_instruction(
1129 vec![],
1130 &bincode::serialize(&LoaderV4Instruction::Truncate { new_size: 8 }).unwrap(),
1131 transaction_accounts.clone(),
1132 &[(3, false, true), (1, true, false), (2, true, true)],
1133 Err(InstructionError::MissingRequiredSignature),
1134 );
1135
1136 process_instruction(
1138 vec![],
1139 &bincode::serialize(&LoaderV4Instruction::Truncate { new_size: 8 }).unwrap(),
1140 transaction_accounts.clone(),
1141 &[(3, true, true), (1, false, false), (2, true, true)],
1142 Err(InstructionError::MissingRequiredSignature),
1143 );
1144
1145 process_instruction(
1147 vec![],
1148 &bincode::serialize(&LoaderV4Instruction::Truncate { new_size: 0 }).unwrap(),
1149 transaction_accounts.clone(),
1150 &[(3, false, true), (1, true, false), (2, true, true)],
1151 Err(InstructionError::InvalidAccountData),
1152 );
1153
1154 process_instruction(
1156 vec![],
1157 &bincode::serialize(&LoaderV4Instruction::Truncate { new_size: 8 }).unwrap(),
1158 transaction_accounts.clone(),
1159 &[(5, false, true), (1, true, false), (2, false, true)],
1160 Err(InstructionError::InvalidArgument),
1161 );
1162
1163 process_instruction(
1165 vec![],
1166 &bincode::serialize(&LoaderV4Instruction::Truncate { new_size: 0 }).unwrap(),
1167 transaction_accounts.clone(),
1168 &[(0, true, true), (1, true, false)],
1169 Err(InstructionError::NotEnoughAccountKeys),
1170 );
1171
1172 process_instruction(
1174 vec![],
1175 &bincode::serialize(&LoaderV4Instruction::Truncate { new_size: 0 }).unwrap(),
1176 transaction_accounts.clone(),
1177 &[(0, false, true), (1, true, false), (2, false, false)],
1178 Err(InstructionError::InvalidArgument),
1179 );
1180
1181 process_instruction(
1183 vec![],
1184 &bincode::serialize(&LoaderV4Instruction::Truncate {
1185 new_size: transaction_accounts[4]
1186 .1
1187 .data()
1188 .len()
1189 .saturating_sub(loader_v4::LoaderV4State::program_data_offset())
1190 .saturating_add(1) as u32,
1191 })
1192 .unwrap(),
1193 transaction_accounts.clone(),
1194 &[(0, false, true), (1, true, false)],
1195 Err(InstructionError::InsufficientFunds),
1196 );
1197
1198 test_loader_instruction_general_errors(LoaderV4Instruction::Truncate { new_size: 0 });
1199 }
1200
1201 #[test]
1202 fn test_loader_instruction_deploy() {
1203 let authority_address = Pubkey::new_unique();
1204 let mut transaction_accounts = vec![
1205 (
1206 Pubkey::new_unique(),
1207 load_program_account_from_elf(
1208 authority_address,
1209 LoaderV4Status::Retracted,
1210 "rodata_section",
1211 ),
1212 ),
1213 (
1214 authority_address,
1215 AccountSharedData::new(0, 0, &Pubkey::new_unique()),
1216 ),
1217 (
1218 Pubkey::new_unique(),
1219 load_program_account_from_elf(
1220 authority_address,
1221 LoaderV4Status::Retracted,
1222 "relative_call",
1223 ),
1224 ),
1225 (
1226 Pubkey::new_unique(),
1227 AccountSharedData::new(0, 0, &loader_v4::id()),
1228 ),
1229 (
1230 Pubkey::new_unique(),
1231 load_program_account_from_elf(
1232 authority_address,
1233 LoaderV4Status::Retracted,
1234 "invalid",
1235 ),
1236 ),
1237 (clock::id(), clock(1000)),
1238 (
1239 rent::id(),
1240 create_account_shared_data_for_test(&rent::Rent::default()),
1241 ),
1242 ];
1243
1244 let accounts = process_instruction(
1246 vec![],
1247 &bincode::serialize(&LoaderV4Instruction::Deploy).unwrap(),
1248 transaction_accounts.clone(),
1249 &[(0, false, true), (1, true, false)],
1250 Ok(()),
1251 );
1252 transaction_accounts[0].1 = accounts[0].clone();
1253 transaction_accounts[5].1 = clock(2000);
1254 assert_eq!(
1255 accounts[0].data().len(),
1256 transaction_accounts[0].1.data().len(),
1257 );
1258 assert_eq!(accounts[0].lamports(), transaction_accounts[0].1.lamports());
1259
1260 process_instruction(
1262 vec![],
1263 &bincode::serialize(&LoaderV4Instruction::Deploy).unwrap(),
1264 transaction_accounts.clone(),
1265 &[(0, false, true), (1, true, false), (2, false, false)],
1266 Err(InstructionError::InvalidArgument),
1267 );
1268
1269 process_instruction(
1271 vec![],
1272 &bincode::serialize(&LoaderV4Instruction::Deploy).unwrap(),
1273 transaction_accounts.clone(),
1274 &[(2, false, true), (1, true, false), (0, false, true)],
1275 Err(InstructionError::InvalidArgument),
1276 );
1277
1278 let accounts = process_instruction(
1280 vec![],
1281 &bincode::serialize(&LoaderV4Instruction::Retract).unwrap(),
1282 transaction_accounts.clone(),
1283 &[(0, false, true), (1, true, false)],
1284 Ok(()),
1285 );
1286 transaction_accounts[0].1 = accounts[0].clone();
1287 let accounts = process_instruction(
1288 vec![],
1289 &bincode::serialize(&LoaderV4Instruction::Deploy).unwrap(),
1290 transaction_accounts.clone(),
1291 &[(0, false, true), (1, true, false), (2, false, true)],
1292 Ok(()),
1293 );
1294 transaction_accounts[0].1 = accounts[0].clone();
1295 assert_eq!(
1296 accounts[0].data().len(),
1297 transaction_accounts[2].1.data().len(),
1298 );
1299 assert_eq!(accounts[2].data().len(), 0,);
1300 assert_eq!(
1301 accounts[2].lamports(),
1302 transaction_accounts[2].1.lamports().saturating_sub(
1303 accounts[0]
1304 .lamports()
1305 .saturating_sub(transaction_accounts[0].1.lamports())
1306 ),
1307 );
1308
1309 process_instruction(
1311 vec![],
1312 &bincode::serialize(&LoaderV4Instruction::Deploy).unwrap(),
1313 transaction_accounts.clone(),
1314 &[(0, false, true), (1, true, false)],
1315 Err(InstructionError::InvalidArgument),
1316 );
1317 transaction_accounts[5].1 = clock(3000);
1318
1319 process_instruction(
1321 vec![],
1322 &bincode::serialize(&LoaderV4Instruction::Deploy).unwrap(),
1323 transaction_accounts.clone(),
1324 &[(3, false, true), (1, true, false)],
1325 Err(InstructionError::InvalidAccountData),
1326 );
1327
1328 process_instruction(
1330 vec![],
1331 &bincode::serialize(&LoaderV4Instruction::Deploy).unwrap(),
1332 transaction_accounts.clone(),
1333 &[(4, false, true), (1, true, false)],
1334 Err(InstructionError::InvalidAccountData),
1335 );
1336
1337 process_instruction(
1339 vec![],
1340 &bincode::serialize(&LoaderV4Instruction::Deploy).unwrap(),
1341 transaction_accounts.clone(),
1342 &[(0, false, true), (1, true, false)],
1343 Err(InstructionError::InvalidArgument),
1344 );
1345
1346 test_loader_instruction_general_errors(LoaderV4Instruction::Deploy);
1347 }
1348
1349 #[test]
1350 fn test_loader_instruction_retract() {
1351 let authority_address = Pubkey::new_unique();
1352 let mut transaction_accounts = vec![
1353 (
1354 Pubkey::new_unique(),
1355 load_program_account_from_elf(
1356 authority_address,
1357 LoaderV4Status::Deployed,
1358 "rodata_section",
1359 ),
1360 ),
1361 (
1362 authority_address,
1363 AccountSharedData::new(0, 0, &Pubkey::new_unique()),
1364 ),
1365 (
1366 Pubkey::new_unique(),
1367 AccountSharedData::new(0, 0, &loader_v4::id()),
1368 ),
1369 (
1370 Pubkey::new_unique(),
1371 load_program_account_from_elf(
1372 authority_address,
1373 LoaderV4Status::Retracted,
1374 "rodata_section",
1375 ),
1376 ),
1377 (clock::id(), clock(1000)),
1378 (
1379 rent::id(),
1380 create_account_shared_data_for_test(&rent::Rent::default()),
1381 ),
1382 ];
1383
1384 let accounts = process_instruction(
1386 vec![],
1387 &bincode::serialize(&LoaderV4Instruction::Retract).unwrap(),
1388 transaction_accounts.clone(),
1389 &[(0, false, true), (1, true, false)],
1390 Ok(()),
1391 );
1392 assert_eq!(
1393 accounts[0].data().len(),
1394 transaction_accounts[0].1.data().len(),
1395 );
1396 assert_eq!(accounts[0].lamports(), transaction_accounts[0].1.lamports());
1397
1398 process_instruction(
1400 vec![],
1401 &bincode::serialize(&LoaderV4Instruction::Retract).unwrap(),
1402 transaction_accounts.clone(),
1403 &[(2, false, true), (1, true, false)],
1404 Err(InstructionError::InvalidAccountData),
1405 );
1406
1407 process_instruction(
1409 vec![],
1410 &bincode::serialize(&LoaderV4Instruction::Retract).unwrap(),
1411 transaction_accounts.clone(),
1412 &[(3, false, true), (1, true, false)],
1413 Err(InstructionError::InvalidArgument),
1414 );
1415
1416 transaction_accounts[4].1 = clock(0);
1418 process_instruction(
1419 vec![],
1420 &bincode::serialize(&LoaderV4Instruction::Retract).unwrap(),
1421 transaction_accounts.clone(),
1422 &[(0, false, true), (1, true, false)],
1423 Err(InstructionError::InvalidArgument),
1424 );
1425
1426 test_loader_instruction_general_errors(LoaderV4Instruction::Retract);
1427 }
1428
1429 #[test]
1430 fn test_loader_instruction_transfer_authority() {
1431 let authority_address = Pubkey::new_unique();
1432 let transaction_accounts = vec![
1433 (
1434 Pubkey::new_unique(),
1435 load_program_account_from_elf(
1436 authority_address,
1437 LoaderV4Status::Deployed,
1438 "rodata_section",
1439 ),
1440 ),
1441 (
1442 Pubkey::new_unique(),
1443 load_program_account_from_elf(
1444 authority_address,
1445 LoaderV4Status::Retracted,
1446 "rodata_section",
1447 ),
1448 ),
1449 (
1450 Pubkey::new_unique(),
1451 AccountSharedData::new(0, 0, &loader_v4::id()),
1452 ),
1453 (
1454 authority_address,
1455 AccountSharedData::new(0, 0, &Pubkey::new_unique()),
1456 ),
1457 (
1458 Pubkey::new_unique(),
1459 AccountSharedData::new(0, 0, &Pubkey::new_unique()),
1460 ),
1461 (
1462 clock::id(),
1463 create_account_shared_data_for_test(&clock::Clock::default()),
1464 ),
1465 (
1466 rent::id(),
1467 create_account_shared_data_for_test(&rent::Rent::default()),
1468 ),
1469 ];
1470
1471 let accounts = process_instruction(
1473 vec![],
1474 &bincode::serialize(&LoaderV4Instruction::TransferAuthority).unwrap(),
1475 transaction_accounts.clone(),
1476 &[(0, false, true), (3, true, false), (4, true, false)],
1477 Ok(()),
1478 );
1479 assert_eq!(
1480 accounts[0].data().len(),
1481 transaction_accounts[0].1.data().len(),
1482 );
1483 assert_eq!(accounts[0].lamports(), transaction_accounts[0].1.lamports());
1484
1485 let accounts = process_instruction(
1487 vec![],
1488 &bincode::serialize(&LoaderV4Instruction::TransferAuthority).unwrap(),
1489 transaction_accounts.clone(),
1490 &[(0, false, true), (3, true, false)],
1491 Ok(()),
1492 );
1493 assert_eq!(
1494 accounts[0].data().len(),
1495 transaction_accounts[0].1.data().len(),
1496 );
1497 assert_eq!(accounts[0].lamports(), transaction_accounts[0].1.lamports());
1498
1499 process_instruction(
1501 vec![],
1502 &bincode::serialize(&LoaderV4Instruction::TransferAuthority).unwrap(),
1503 transaction_accounts.clone(),
1504 &[(1, false, true), (3, true, false)],
1505 Err(InstructionError::InvalidArgument),
1506 );
1507
1508 process_instruction(
1510 vec![],
1511 &bincode::serialize(&LoaderV4Instruction::TransferAuthority).unwrap(),
1512 transaction_accounts.clone(),
1513 &[(2, false, true), (3, true, false), (4, true, false)],
1514 Err(InstructionError::InvalidAccountData),
1515 );
1516
1517 process_instruction(
1519 vec![],
1520 &bincode::serialize(&LoaderV4Instruction::TransferAuthority).unwrap(),
1521 transaction_accounts,
1522 &[(0, false, true), (3, true, false), (4, false, false)],
1523 Err(InstructionError::MissingRequiredSignature),
1524 );
1525
1526 test_loader_instruction_general_errors(LoaderV4Instruction::TransferAuthority);
1527 }
1528
1529 #[test]
1530 fn test_execute_program() {
1531 let program_address = Pubkey::new_unique();
1532 let authority_address = Pubkey::new_unique();
1533 let transaction_accounts = vec![
1534 (
1535 program_address,
1536 load_program_account_from_elf(
1537 authority_address,
1538 LoaderV4Status::Finalized,
1539 "rodata_section",
1540 ),
1541 ),
1542 (
1543 Pubkey::new_unique(),
1544 AccountSharedData::new(10000000, 32, &program_address),
1545 ),
1546 (
1547 Pubkey::new_unique(),
1548 AccountSharedData::new(0, 0, &loader_v4::id()),
1549 ),
1550 (
1551 Pubkey::new_unique(),
1552 load_program_account_from_elf(
1553 authority_address,
1554 LoaderV4Status::Retracted,
1555 "rodata_section",
1556 ),
1557 ),
1558 (
1559 Pubkey::new_unique(),
1560 load_program_account_from_elf(
1561 authority_address,
1562 LoaderV4Status::Finalized,
1563 "invalid",
1564 ),
1565 ),
1566 ];
1567
1568 process_instruction(
1570 vec![0],
1571 &[0, 1, 2, 3],
1572 transaction_accounts.clone(),
1573 &[(1, false, true)],
1574 Err(InstructionError::Custom(42)),
1575 );
1576
1577 process_instruction(
1579 vec![1],
1580 &[0, 1, 2, 3],
1581 transaction_accounts.clone(),
1582 &[(1, false, true)],
1583 Err(InstructionError::InvalidAccountOwner),
1584 );
1585
1586 process_instruction(
1588 vec![2],
1589 &[0, 1, 2, 3],
1590 transaction_accounts.clone(),
1591 &[(1, false, true)],
1592 Err(InstructionError::InvalidAccountData),
1593 );
1594
1595 process_instruction(
1597 vec![3],
1598 &[0, 1, 2, 3],
1599 transaction_accounts.clone(),
1600 &[(1, false, true)],
1601 Err(InstructionError::InvalidArgument),
1602 );
1603
1604 process_instruction(
1606 vec![4],
1607 &[0, 1, 2, 3],
1608 transaction_accounts,
1609 &[(1, false, true)],
1610 Err(InstructionError::InvalidAccountData),
1611 );
1612 }
1613}