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