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