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