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