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