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