1#[cfg(feature = "svm-internal")]
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::{
22 bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, loader_v4, system_program,
23 },
24 solana_transaction_context::{BorrowedAccount, InstructionContext},
25 solana_type_overrides::sync::{atomic::Ordering, Arc},
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(instruction_data, solana_packet::PACKET_DATA_SIZE as u64)? {
498 LoaderV4Instruction::Write { offset, bytes } => {
499 process_instruction_write(invoke_context, offset, bytes)
500 }
501 LoaderV4Instruction::Copy {
502 destination_offset,
503 source_offset,
504 length,
505 } => {
506 process_instruction_copy(invoke_context, destination_offset, source_offset, length)
507 }
508 LoaderV4Instruction::SetProgramLength { new_size } => {
509 process_instruction_set_program_length(invoke_context, new_size)
510 }
511 LoaderV4Instruction::Deploy => process_instruction_deploy(invoke_context),
512 LoaderV4Instruction::Retract => process_instruction_retract(invoke_context),
513 LoaderV4Instruction::TransferAuthority => {
514 process_instruction_transfer_authority(invoke_context)
515 }
516 LoaderV4Instruction::Finalize => process_instruction_finalize(invoke_context),
517 }
518 .map_err(|err| Box::new(err) as Box<dyn std::error::Error>)
519 } else {
520 let program = instruction_context.try_borrow_last_program_account(transaction_context)?;
521 let mut get_or_create_executor_time = Measure::start("get_or_create_executor_time");
522 let loaded_program = invoke_context
523 .program_cache_for_tx_batch
524 .find(program.get_key())
525 .ok_or_else(|| {
526 ic_logger_msg!(log_collector, "Program is not cached");
527 InstructionError::UnsupportedProgramId
528 })?;
529 get_or_create_executor_time.stop();
530 invoke_context.timings.get_or_create_executor_us += get_or_create_executor_time.as_us();
531 drop(program);
532 loaded_program
533 .ix_usage_counter
534 .fetch_add(1, Ordering::Relaxed);
535 match &loaded_program.program {
536 ProgramCacheEntryType::FailedVerification(_)
537 | ProgramCacheEntryType::Closed
538 | ProgramCacheEntryType::DelayVisibility => {
539 ic_logger_msg!(log_collector, "Program is not deployed");
540 Err(Box::new(InstructionError::UnsupportedProgramId) as Box<dyn std::error::Error>)
541 }
542 ProgramCacheEntryType::Loaded(executable) => execute(executable, invoke_context),
543 _ => {
544 Err(Box::new(InstructionError::UnsupportedProgramId) as Box<dyn std::error::Error>)
545 }
546 }
547 }
548 .map(|_| 0)
549}
550
551#[cfg(test)]
552mod tests {
553 use {
554 super::*,
555 solana_account::{
556 create_account_shared_data_for_test, AccountSharedData, ReadableAccount,
557 WritableAccount,
558 },
559 solana_bpf_loader_program::test_utils,
560 solana_clock::Slot,
561 solana_instruction::AccountMeta,
562 solana_program_runtime::invoke_context::mock_process_instruction,
563 solana_sysvar::{clock, rent},
564 solana_transaction_context::IndexOfAccount,
565 std::{fs::File, io::Read, path::Path},
566 };
567
568 fn process_instruction(
569 program_indices: Vec<IndexOfAccount>,
570 instruction_data: &[u8],
571 transaction_accounts: Vec<(Pubkey, AccountSharedData)>,
572 instruction_accounts: &[(IndexOfAccount, bool, bool)],
573 expected_result: Result<(), InstructionError>,
574 ) -> Vec<AccountSharedData> {
575 let instruction_accounts = instruction_accounts
576 .iter()
577 .map(
578 |(index_in_transaction, is_signer, is_writable)| AccountMeta {
579 pubkey: transaction_accounts[*index_in_transaction as usize].0,
580 is_signer: *is_signer,
581 is_writable: *is_writable,
582 },
583 )
584 .collect::<Vec<_>>();
585 mock_process_instruction(
586 &loader_v4::id(),
587 program_indices,
588 instruction_data,
589 transaction_accounts,
590 instruction_accounts,
591 expected_result,
592 Entrypoint::vm,
593 |invoke_context| {
594 test_utils::load_all_invoked_programs(invoke_context);
595 },
596 |_invoke_context| {},
597 )
598 }
599
600 fn load_program_account_from_elf(
601 authority_address: Pubkey,
602 status: LoaderV4Status,
603 path: &str,
604 ) -> AccountSharedData {
605 let path = Path::new("../bpf_loader/test_elfs/out/")
606 .join(path)
607 .with_extension("so");
608 let mut file = File::open(path).expect("file open failed");
609 let mut elf_bytes = Vec::new();
610 file.read_to_end(&mut elf_bytes).unwrap();
611 let rent = rent::Rent::default();
612 let account_size = LoaderV4State::program_data_offset().saturating_add(elf_bytes.len());
613 let mut program_account = AccountSharedData::new(
614 rent.minimum_balance(account_size),
615 account_size,
616 &loader_v4::id(),
617 );
618 let state = get_state_mut(program_account.data_as_mut_slice()).unwrap();
619 state.slot = 0;
620 state.authority_address_or_next_version = authority_address;
621 state.status = status;
622 program_account.data_as_mut_slice()[LoaderV4State::program_data_offset()..]
623 .copy_from_slice(&elf_bytes);
624 program_account
625 }
626
627 fn clock(slot: Slot) -> AccountSharedData {
628 let clock = clock::Clock {
629 slot,
630 ..clock::Clock::default()
631 };
632 create_account_shared_data_for_test(&clock)
633 }
634
635 fn test_loader_instruction_general_errors(instruction: LoaderV4Instruction) {
636 let instruction = bincode::serialize(&instruction).unwrap();
637 let authority_address = Pubkey::new_unique();
638 let transaction_accounts = vec![
639 (
640 Pubkey::new_unique(),
641 load_program_account_from_elf(
642 authority_address,
643 LoaderV4Status::Deployed,
644 "sbpfv3_return_err",
645 ),
646 ),
647 (
648 authority_address,
649 AccountSharedData::new(0, 0, &Pubkey::new_unique()),
650 ),
651 (
652 Pubkey::new_unique(),
653 load_program_account_from_elf(
654 authority_address,
655 LoaderV4Status::Finalized,
656 "sbpfv3_return_err",
657 ),
658 ),
659 (
660 clock::id(),
661 create_account_shared_data_for_test(&clock::Clock::default()),
662 ),
663 (
664 rent::id(),
665 create_account_shared_data_for_test(&rent::Rent::default()),
666 ),
667 ];
668
669 process_instruction(
671 vec![],
672 &instruction,
673 transaction_accounts.clone(),
674 &[],
675 Err(InstructionError::NotEnoughAccountKeys),
676 );
677
678 process_instruction(
680 vec![],
681 &instruction,
682 transaction_accounts.clone(),
683 &[(0, false, true)],
684 Err(InstructionError::NotEnoughAccountKeys),
685 );
686
687 process_instruction(
689 vec![],
690 &instruction,
691 transaction_accounts.clone(),
692 &[(1, false, true), (1, true, false), (2, true, true)],
693 Err(InstructionError::InvalidAccountOwner),
694 );
695
696 process_instruction(
698 vec![],
699 &instruction,
700 transaction_accounts.clone(),
701 &[(0, false, false), (1, true, false), (2, true, true)],
702 Err(InstructionError::InvalidArgument),
703 );
704
705 process_instruction(
707 vec![],
708 &instruction,
709 transaction_accounts.clone(),
710 &[(0, false, true), (1, false, false), (2, true, true)],
711 Err(InstructionError::MissingRequiredSignature),
712 );
713
714 process_instruction(
716 vec![],
717 &instruction,
718 transaction_accounts.clone(),
719 &[(2, false, true), (1, true, false), (0, true, true)],
720 Err(InstructionError::Immutable),
721 );
722
723 process_instruction(
725 vec![],
726 &instruction,
727 transaction_accounts,
728 &[(0, false, true), (2, true, false), (2, true, true)],
729 Err(InstructionError::IncorrectAuthority),
730 );
731 }
732
733 #[test]
734 fn test_loader_instruction_write() {
735 let authority_address = Pubkey::new_unique();
736 let transaction_accounts = vec![
737 (
738 Pubkey::new_unique(),
739 load_program_account_from_elf(
740 authority_address,
741 LoaderV4Status::Retracted,
742 "sbpfv3_return_err",
743 ),
744 ),
745 (
746 authority_address,
747 AccountSharedData::new(0, 0, &Pubkey::new_unique()),
748 ),
749 (
750 Pubkey::new_unique(),
751 load_program_account_from_elf(
752 authority_address,
753 LoaderV4Status::Deployed,
754 "sbpfv3_return_err",
755 ),
756 ),
757 (
758 clock::id(),
759 create_account_shared_data_for_test(&clock::Clock::default()),
760 ),
761 (
762 rent::id(),
763 create_account_shared_data_for_test(&rent::Rent::default()),
764 ),
765 ];
766
767 process_instruction(
769 vec![],
770 &bincode::serialize(&LoaderV4Instruction::Write {
771 offset: 2,
772 bytes: vec![8, 8, 8, 8],
773 })
774 .unwrap(),
775 transaction_accounts.clone(),
776 &[(0, false, true), (1, true, false)],
777 Ok(()),
778 );
779
780 process_instruction(
782 vec![],
783 &bincode::serialize(&LoaderV4Instruction::Write {
784 offset: 2,
785 bytes: Vec::new(),
786 })
787 .unwrap(),
788 transaction_accounts.clone(),
789 &[(0, false, true), (1, true, false)],
790 Ok(()),
791 );
792
793 process_instruction(
795 vec![],
796 &bincode::serialize(&LoaderV4Instruction::Write {
797 offset: 8,
798 bytes: vec![8, 8, 8, 8],
799 })
800 .unwrap(),
801 transaction_accounts.clone(),
802 &[(2, false, true), (1, true, false)],
803 Err(InstructionError::InvalidArgument),
804 );
805
806 process_instruction(
808 vec![],
809 &bincode::serialize(&LoaderV4Instruction::Write {
810 offset: transaction_accounts[0]
811 .1
812 .data()
813 .len()
814 .saturating_sub(LoaderV4State::program_data_offset())
815 .saturating_sub(3) as u32,
816 bytes: vec![8, 8, 8, 8],
817 })
818 .unwrap(),
819 transaction_accounts.clone(),
820 &[(0, false, true), (1, true, false)],
821 Err(InstructionError::AccountDataTooSmall),
822 );
823
824 test_loader_instruction_general_errors(LoaderV4Instruction::Write {
825 offset: 0,
826 bytes: Vec::new(),
827 });
828 }
829
830 #[test]
831 fn test_loader_instruction_copy() {
832 let authority_address = Pubkey::new_unique();
833 let transaction_accounts = vec![
834 (
835 Pubkey::new_unique(),
836 load_program_account_from_elf(
837 authority_address,
838 LoaderV4Status::Retracted,
839 "sbpfv3_return_err",
840 ),
841 ),
842 (
843 authority_address,
844 AccountSharedData::new(0, 0, &Pubkey::new_unique()),
845 ),
846 (
847 Pubkey::new_unique(),
848 load_program_account_from_elf(
849 authority_address,
850 LoaderV4Status::Deployed,
851 "sbpfv3_return_err",
852 ),
853 ),
854 (
855 clock::id(),
856 create_account_shared_data_for_test(&clock::Clock::default()),
857 ),
858 (
859 rent::id(),
860 create_account_shared_data_for_test(&rent::Rent::default()),
861 ),
862 ];
863
864 process_instruction(
866 vec![],
867 &bincode::serialize(&LoaderV4Instruction::Copy {
868 destination_offset: 1,
869 source_offset: 2,
870 length: 3,
871 })
872 .unwrap(),
873 transaction_accounts.clone(),
874 &[(0, false, true), (1, true, false), (2, false, false)],
875 Ok(()),
876 );
877
878 process_instruction(
880 vec![],
881 &bincode::serialize(&LoaderV4Instruction::Copy {
882 destination_offset: 1,
883 source_offset: 2,
884 length: 0,
885 })
886 .unwrap(),
887 transaction_accounts.clone(),
888 &[(0, false, true), (1, true, false), (2, false, false)],
889 Ok(()),
890 );
891
892 process_instruction(
894 vec![],
895 &bincode::serialize(&LoaderV4Instruction::Copy {
896 destination_offset: 1,
897 source_offset: 2,
898 length: 3,
899 })
900 .unwrap(),
901 transaction_accounts.clone(),
902 &[(2, false, true), (1, true, false), (0, false, false)],
903 Err(InstructionError::InvalidArgument),
904 );
905
906 process_instruction(
908 vec![],
909 &bincode::serialize(&LoaderV4Instruction::Copy {
910 destination_offset: 1,
911 source_offset: 2,
912 length: 3,
913 })
914 .unwrap(),
915 transaction_accounts.clone(),
916 &[(2, false, true), (1, true, false), (2, false, false)],
917 Err(InstructionError::AccountBorrowFailed),
918 );
919
920 process_instruction(
922 vec![],
923 &bincode::serialize(&LoaderV4Instruction::Copy {
924 destination_offset: 1,
925 source_offset: transaction_accounts[2]
926 .1
927 .data()
928 .len()
929 .saturating_sub(LoaderV4State::program_data_offset())
930 .saturating_sub(3) as u32,
931 length: 4,
932 })
933 .unwrap(),
934 transaction_accounts.clone(),
935 &[(0, false, true), (1, true, false), (2, false, false)],
936 Err(InstructionError::AccountDataTooSmall),
937 );
938
939 process_instruction(
941 vec![],
942 &bincode::serialize(&LoaderV4Instruction::Copy {
943 destination_offset: transaction_accounts[0]
944 .1
945 .data()
946 .len()
947 .saturating_sub(LoaderV4State::program_data_offset())
948 .saturating_sub(3) as u32,
949 source_offset: 2,
950 length: 4,
951 })
952 .unwrap(),
953 transaction_accounts.clone(),
954 &[(0, false, true), (1, true, false), (2, false, false)],
955 Err(InstructionError::AccountDataTooSmall),
956 );
957
958 test_loader_instruction_general_errors(LoaderV4Instruction::Copy {
959 destination_offset: 1,
960 source_offset: 2,
961 length: 3,
962 });
963 }
964
965 #[test]
966 fn test_loader_instruction_truncate() {
967 let authority_address = Pubkey::new_unique();
968 let mut transaction_accounts = vec![
969 (
970 Pubkey::new_unique(),
971 load_program_account_from_elf(
972 authority_address,
973 LoaderV4Status::Retracted,
974 "sbpfv3_return_err",
975 ),
976 ),
977 (
978 authority_address,
979 AccountSharedData::new(0, 0, &Pubkey::new_unique()),
980 ),
981 (
982 Pubkey::new_unique(),
983 AccountSharedData::new(0, 0, &loader_v4::id()),
984 ),
985 (
986 Pubkey::new_unique(),
987 AccountSharedData::new(0, 0, &loader_v4::id()),
988 ),
989 (
990 Pubkey::new_unique(),
991 load_program_account_from_elf(
992 authority_address,
993 LoaderV4Status::Retracted,
994 "sbpfv3_return_ok",
995 ),
996 ),
997 (
998 Pubkey::new_unique(),
999 load_program_account_from_elf(
1000 authority_address,
1001 LoaderV4Status::Deployed,
1002 "sbpfv3_return_err",
1003 ),
1004 ),
1005 (
1006 clock::id(),
1007 create_account_shared_data_for_test(&clock::Clock::default()),
1008 ),
1009 (
1010 rent::id(),
1011 create_account_shared_data_for_test(&rent::Rent::default()),
1012 ),
1013 ];
1014 let smaller_program_lamports = transaction_accounts[0].1.lamports();
1015 let larger_program_lamports = transaction_accounts[4].1.lamports();
1016 assert_ne!(smaller_program_lamports, larger_program_lamports);
1017
1018 let accounts = process_instruction(
1020 vec![],
1021 &bincode::serialize(&LoaderV4Instruction::SetProgramLength {
1022 new_size: transaction_accounts[0]
1023 .1
1024 .data()
1025 .len()
1026 .saturating_sub(LoaderV4State::program_data_offset())
1027 as u32,
1028 })
1029 .unwrap(),
1030 transaction_accounts.clone(),
1031 &[(0, false, true), (1, true, false)],
1032 Ok(()),
1033 );
1034 assert_eq!(
1035 accounts[0].data().len(),
1036 transaction_accounts[0].1.data().len(),
1037 );
1038 assert_eq!(accounts[2].lamports(), transaction_accounts[2].1.lamports());
1039
1040 transaction_accounts[3]
1042 .1
1043 .set_lamports(smaller_program_lamports);
1044 let accounts = process_instruction(
1045 vec![],
1046 &bincode::serialize(&LoaderV4Instruction::SetProgramLength {
1047 new_size: transaction_accounts[0]
1048 .1
1049 .data()
1050 .len()
1051 .saturating_sub(LoaderV4State::program_data_offset())
1052 as u32,
1053 })
1054 .unwrap(),
1055 transaction_accounts.clone(),
1056 &[(3, true, true), (1, true, false), (2, false, true)],
1057 Ok(()),
1058 );
1059 assert_eq!(
1060 accounts[3].data().len(),
1061 transaction_accounts[0].1.data().len(),
1062 );
1063
1064 transaction_accounts[0]
1066 .1
1067 .set_lamports(larger_program_lamports);
1068 let accounts = process_instruction(
1069 vec![],
1070 &bincode::serialize(&LoaderV4Instruction::SetProgramLength {
1071 new_size: transaction_accounts[4]
1072 .1
1073 .data()
1074 .len()
1075 .saturating_sub(LoaderV4State::program_data_offset())
1076 as u32,
1077 })
1078 .unwrap(),
1079 transaction_accounts.clone(),
1080 &[(0, false, true), (1, true, false)],
1081 Ok(()),
1082 );
1083 assert_eq!(
1084 accounts[0].data().len(),
1085 transaction_accounts[4].1.data().len(),
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), (2, false, true)],
1102 Ok(()),
1103 );
1104 assert_eq!(
1105 accounts[4].data().len(),
1106 transaction_accounts[0].1.data().len(),
1107 );
1108 assert_eq!(
1109 accounts[2].lamports(),
1110 transaction_accounts[2].1.lamports().saturating_add(
1111 transaction_accounts[4]
1112 .1
1113 .lamports()
1114 .saturating_sub(accounts[4].lamports())
1115 ),
1116 );
1117
1118 let accounts = process_instruction(
1120 vec![],
1121 &bincode::serialize(&LoaderV4Instruction::SetProgramLength {
1122 new_size: transaction_accounts[0]
1123 .1
1124 .data()
1125 .len()
1126 .saturating_sub(LoaderV4State::program_data_offset())
1127 as u32,
1128 })
1129 .unwrap(),
1130 transaction_accounts.clone(),
1131 &[(4, false, true), (1, true, false)],
1132 Ok(()),
1133 );
1134 assert_eq!(
1135 accounts[4].data().len(),
1136 transaction_accounts[0].1.data().len(),
1137 );
1138 assert_eq!(accounts[2].lamports(), transaction_accounts[2].1.lamports(),);
1139
1140 let accounts = process_instruction(
1142 vec![],
1143 &bincode::serialize(&LoaderV4Instruction::SetProgramLength { new_size: 0 }).unwrap(),
1144 transaction_accounts.clone(),
1145 &[(0, false, true), (1, true, false), (2, false, true)],
1146 Ok(()),
1147 );
1148 assert_eq!(accounts[0].data().len(), 0);
1149 assert_eq!(
1150 accounts[2].lamports(),
1151 transaction_accounts[2].1.lamports().saturating_add(
1152 transaction_accounts[0]
1153 .1
1154 .lamports()
1155 .saturating_sub(accounts[0].lamports())
1156 ),
1157 );
1158
1159 process_instruction(
1161 vec![],
1162 &bincode::serialize(&LoaderV4Instruction::SetProgramLength { new_size: 8 }).unwrap(),
1163 transaction_accounts.clone(),
1164 &[(1, false, true), (1, true, false), (2, true, true)],
1165 Err(InstructionError::InvalidAccountOwner),
1166 );
1167
1168 process_instruction(
1170 vec![],
1171 &bincode::serialize(&LoaderV4Instruction::SetProgramLength { new_size: 8 }).unwrap(),
1172 transaction_accounts.clone(),
1173 &[(3, false, false), (1, true, false), (2, true, true)],
1174 Err(InstructionError::InvalidArgument),
1175 );
1176
1177 process_instruction(
1179 vec![],
1180 &bincode::serialize(&LoaderV4Instruction::SetProgramLength { new_size: 0 }).unwrap(),
1181 transaction_accounts.clone(),
1182 &[(0, false, true), (1, true, false)],
1183 Err(InstructionError::InvalidArgument),
1184 );
1185
1186 process_instruction(
1188 vec![],
1189 &bincode::serialize(&LoaderV4Instruction::SetProgramLength { new_size: 8 }).unwrap(),
1190 transaction_accounts.clone(),
1191 &[(3, true, true), (1, false, false), (2, true, true)],
1192 Err(InstructionError::MissingRequiredSignature),
1193 );
1194
1195 process_instruction(
1197 vec![],
1198 &bincode::serialize(&LoaderV4Instruction::SetProgramLength { new_size: 0 }).unwrap(),
1199 transaction_accounts.clone(),
1200 &[(3, false, true), (1, true, false), (2, true, true)],
1201 Err(InstructionError::AccountDataTooSmall),
1202 );
1203
1204 process_instruction(
1206 vec![],
1207 &bincode::serialize(&LoaderV4Instruction::SetProgramLength { new_size: 8 }).unwrap(),
1208 transaction_accounts.clone(),
1209 &[(5, false, true), (1, true, false), (2, false, true)],
1210 Err(InstructionError::InvalidArgument),
1211 );
1212
1213 process_instruction(
1215 vec![],
1216 &bincode::serialize(&LoaderV4Instruction::SetProgramLength { new_size: 0 }).unwrap(),
1217 transaction_accounts.clone(),
1218 &[(0, false, true), (1, true, false), (2, false, false)],
1219 Err(InstructionError::InvalidArgument),
1220 );
1221
1222 process_instruction(
1224 vec![],
1225 &bincode::serialize(&LoaderV4Instruction::SetProgramLength {
1226 new_size: transaction_accounts[4]
1227 .1
1228 .data()
1229 .len()
1230 .saturating_sub(LoaderV4State::program_data_offset())
1231 .saturating_add(1) as u32,
1232 })
1233 .unwrap(),
1234 transaction_accounts.clone(),
1235 &[(0, false, true), (1, true, false)],
1236 Err(InstructionError::InsufficientFunds),
1237 );
1238
1239 test_loader_instruction_general_errors(LoaderV4Instruction::SetProgramLength {
1240 new_size: 0,
1241 });
1242 }
1243
1244 #[test]
1245 fn test_loader_instruction_deploy() {
1246 let authority_address = Pubkey::new_unique();
1247 let mut transaction_accounts = vec![
1248 (
1249 Pubkey::new_unique(),
1250 load_program_account_from_elf(
1251 authority_address,
1252 LoaderV4Status::Retracted,
1253 "sbpfv3_return_ok",
1254 ),
1255 ),
1256 (
1257 authority_address,
1258 AccountSharedData::new(0, 0, &Pubkey::new_unique()),
1259 ),
1260 (
1261 Pubkey::new_unique(),
1262 load_program_account_from_elf(
1263 authority_address,
1264 LoaderV4Status::Retracted,
1265 "sbpfv3_return_err",
1266 ),
1267 ),
1268 (
1269 Pubkey::new_unique(),
1270 AccountSharedData::new(0, 0, &loader_v4::id()),
1271 ),
1272 (
1273 Pubkey::new_unique(),
1274 load_program_account_from_elf(
1275 authority_address,
1276 LoaderV4Status::Retracted,
1277 "sbpfv0_verifier_err",
1278 ),
1279 ),
1280 (clock::id(), clock(1000)),
1281 (
1282 rent::id(),
1283 create_account_shared_data_for_test(&rent::Rent::default()),
1284 ),
1285 ];
1286
1287 let accounts = process_instruction(
1289 vec![],
1290 &bincode::serialize(&LoaderV4Instruction::Deploy).unwrap(),
1291 transaction_accounts.clone(),
1292 &[(0, false, true), (1, true, false)],
1293 Ok(()),
1294 );
1295 transaction_accounts[0].1 = accounts[0].clone();
1296 transaction_accounts[5].1 = clock(2000);
1297 assert_eq!(
1298 accounts[0].data().len(),
1299 transaction_accounts[0].1.data().len(),
1300 );
1301 assert_eq!(accounts[0].lamports(), transaction_accounts[0].1.lamports());
1302
1303 process_instruction(
1305 vec![],
1306 &bincode::serialize(&LoaderV4Instruction::Deploy).unwrap(),
1307 transaction_accounts.clone(),
1308 &[(0, false, true), (1, true, false), (2, false, false)],
1309 Err(InstructionError::InvalidArgument),
1310 );
1311
1312 process_instruction(
1314 vec![],
1315 &bincode::serialize(&LoaderV4Instruction::Deploy).unwrap(),
1316 transaction_accounts.clone(),
1317 &[(2, false, true), (1, true, false), (0, false, true)],
1318 Err(InstructionError::InvalidArgument),
1319 );
1320
1321 let accounts = process_instruction(
1323 vec![],
1324 &bincode::serialize(&LoaderV4Instruction::Retract).unwrap(),
1325 transaction_accounts.clone(),
1326 &[(0, false, true), (1, true, false)],
1327 Ok(()),
1328 );
1329 transaction_accounts[0].1 = accounts[0].clone();
1330 let accounts = process_instruction(
1331 vec![],
1332 &bincode::serialize(&LoaderV4Instruction::Deploy).unwrap(),
1333 transaction_accounts.clone(),
1334 &[(0, false, true), (1, true, false), (2, false, true)],
1335 Ok(()),
1336 );
1337 assert_eq!(
1338 accounts[0].data().len(),
1339 transaction_accounts[2].1.data().len(),
1340 );
1341 assert_eq!(accounts[2].data().len(), 0,);
1342 assert_eq!(accounts[0].lamports(), transaction_accounts[2].1.lamports());
1343 assert_eq!(accounts[2].lamports(), transaction_accounts[0].1.lamports());
1344 transaction_accounts[0].1 = accounts[0].clone();
1345
1346 process_instruction(
1348 vec![],
1349 &bincode::serialize(&LoaderV4Instruction::Deploy).unwrap(),
1350 transaction_accounts.clone(),
1351 &[(0, false, true), (1, true, false)],
1352 Err(InstructionError::InvalidArgument),
1353 );
1354 transaction_accounts[5].1 = clock(3000);
1355
1356 process_instruction(
1358 vec![],
1359 &bincode::serialize(&LoaderV4Instruction::Deploy).unwrap(),
1360 transaction_accounts.clone(),
1361 &[(3, false, true), (1, true, false)],
1362 Err(InstructionError::AccountDataTooSmall),
1363 );
1364
1365 process_instruction(
1367 vec![],
1368 &bincode::serialize(&LoaderV4Instruction::Deploy).unwrap(),
1369 transaction_accounts.clone(),
1370 &[(4, false, true), (1, true, false)],
1371 Err(InstructionError::InvalidAccountData),
1372 );
1373
1374 process_instruction(
1376 vec![],
1377 &bincode::serialize(&LoaderV4Instruction::Deploy).unwrap(),
1378 transaction_accounts.clone(),
1379 &[(0, false, true), (1, true, false)],
1380 Err(InstructionError::InvalidArgument),
1381 );
1382
1383 test_loader_instruction_general_errors(LoaderV4Instruction::Deploy);
1384 }
1385
1386 #[test]
1387 fn test_loader_instruction_retract() {
1388 let authority_address = Pubkey::new_unique();
1389 let mut transaction_accounts = vec![
1390 (
1391 Pubkey::new_unique(),
1392 load_program_account_from_elf(
1393 authority_address,
1394 LoaderV4Status::Deployed,
1395 "sbpfv3_return_err",
1396 ),
1397 ),
1398 (
1399 authority_address,
1400 AccountSharedData::new(0, 0, &Pubkey::new_unique()),
1401 ),
1402 (
1403 Pubkey::new_unique(),
1404 AccountSharedData::new(0, 0, &loader_v4::id()),
1405 ),
1406 (
1407 Pubkey::new_unique(),
1408 load_program_account_from_elf(
1409 authority_address,
1410 LoaderV4Status::Retracted,
1411 "sbpfv3_return_err",
1412 ),
1413 ),
1414 (clock::id(), clock(1000)),
1415 (
1416 rent::id(),
1417 create_account_shared_data_for_test(&rent::Rent::default()),
1418 ),
1419 ];
1420
1421 let accounts = process_instruction(
1423 vec![],
1424 &bincode::serialize(&LoaderV4Instruction::Retract).unwrap(),
1425 transaction_accounts.clone(),
1426 &[(0, false, true), (1, true, false)],
1427 Ok(()),
1428 );
1429 assert_eq!(
1430 accounts[0].data().len(),
1431 transaction_accounts[0].1.data().len(),
1432 );
1433 assert_eq!(accounts[0].lamports(), transaction_accounts[0].1.lamports());
1434
1435 process_instruction(
1437 vec![],
1438 &bincode::serialize(&LoaderV4Instruction::Retract).unwrap(),
1439 transaction_accounts.clone(),
1440 &[(2, false, true), (1, true, false)],
1441 Err(InstructionError::AccountDataTooSmall),
1442 );
1443
1444 process_instruction(
1446 vec![],
1447 &bincode::serialize(&LoaderV4Instruction::Retract).unwrap(),
1448 transaction_accounts.clone(),
1449 &[(3, false, true), (1, true, false)],
1450 Err(InstructionError::InvalidArgument),
1451 );
1452
1453 transaction_accounts[4].1 = clock(0);
1455 process_instruction(
1456 vec![],
1457 &bincode::serialize(&LoaderV4Instruction::Retract).unwrap(),
1458 transaction_accounts.clone(),
1459 &[(0, false, true), (1, true, false)],
1460 Err(InstructionError::InvalidArgument),
1461 );
1462
1463 test_loader_instruction_general_errors(LoaderV4Instruction::Retract);
1464 }
1465
1466 #[test]
1467 fn test_loader_instruction_transfer_authority() {
1468 let authority_address = Pubkey::new_unique();
1469 let transaction_accounts = vec![
1470 (
1471 Pubkey::new_unique(),
1472 load_program_account_from_elf(
1473 authority_address,
1474 LoaderV4Status::Deployed,
1475 "sbpfv3_return_err",
1476 ),
1477 ),
1478 (
1479 Pubkey::new_unique(),
1480 load_program_account_from_elf(
1481 authority_address,
1482 LoaderV4Status::Retracted,
1483 "sbpfv3_return_err",
1484 ),
1485 ),
1486 (
1487 Pubkey::new_unique(),
1488 AccountSharedData::new(0, 0, &loader_v4::id()),
1489 ),
1490 (
1491 authority_address,
1492 AccountSharedData::new(0, 0, &Pubkey::new_unique()),
1493 ),
1494 (
1495 Pubkey::new_unique(),
1496 AccountSharedData::new(0, 0, &Pubkey::new_unique()),
1497 ),
1498 (
1499 clock::id(),
1500 create_account_shared_data_for_test(&clock::Clock::default()),
1501 ),
1502 (
1503 rent::id(),
1504 create_account_shared_data_for_test(&rent::Rent::default()),
1505 ),
1506 ];
1507
1508 let accounts = process_instruction(
1510 vec![],
1511 &bincode::serialize(&LoaderV4Instruction::TransferAuthority).unwrap(),
1512 transaction_accounts.clone(),
1513 &[(0, false, true), (3, true, false), (4, true, false)],
1514 Ok(()),
1515 );
1516 assert_eq!(
1517 accounts[0].data().len(),
1518 transaction_accounts[0].1.data().len(),
1519 );
1520 assert_eq!(accounts[0].lamports(), transaction_accounts[0].1.lamports());
1521
1522 process_instruction(
1524 vec![],
1525 &bincode::serialize(&LoaderV4Instruction::TransferAuthority).unwrap(),
1526 transaction_accounts.clone(),
1527 &[(0, false, true), (3, true, false)],
1528 Err(InstructionError::NotEnoughAccountKeys),
1529 );
1530
1531 process_instruction(
1533 vec![],
1534 &bincode::serialize(&LoaderV4Instruction::TransferAuthority).unwrap(),
1535 transaction_accounts.clone(),
1536 &[(2, false, true), (3, true, false), (4, true, false)],
1537 Err(InstructionError::AccountDataTooSmall),
1538 );
1539
1540 process_instruction(
1542 vec![],
1543 &bincode::serialize(&LoaderV4Instruction::TransferAuthority).unwrap(),
1544 transaction_accounts.clone(),
1545 &[(0, false, true), (3, true, false), (4, false, false)],
1546 Err(InstructionError::MissingRequiredSignature),
1547 );
1548
1549 process_instruction(
1551 vec![],
1552 &bincode::serialize(&LoaderV4Instruction::TransferAuthority).unwrap(),
1553 transaction_accounts,
1554 &[(0, false, true), (3, true, false), (3, true, false)],
1555 Err(InstructionError::InvalidArgument),
1556 );
1557
1558 test_loader_instruction_general_errors(LoaderV4Instruction::TransferAuthority);
1559 }
1560
1561 #[test]
1562 fn test_loader_instruction_finalize() {
1563 let authority_address = Pubkey::new_unique();
1564 let transaction_accounts = vec![
1565 (
1566 Pubkey::new_unique(),
1567 load_program_account_from_elf(
1568 authority_address,
1569 LoaderV4Status::Deployed,
1570 "sbpfv3_return_err",
1571 ),
1572 ),
1573 (
1574 Pubkey::new_unique(),
1575 load_program_account_from_elf(
1576 authority_address,
1577 LoaderV4Status::Retracted,
1578 "sbpfv3_return_err",
1579 ),
1580 ),
1581 (
1582 Pubkey::new_unique(),
1583 load_program_account_from_elf(
1584 authority_address,
1585 LoaderV4Status::Finalized,
1586 "sbpfv3_return_err",
1587 ),
1588 ),
1589 (
1590 Pubkey::new_unique(),
1591 load_program_account_from_elf(
1592 Pubkey::new_unique(),
1593 LoaderV4Status::Retracted,
1594 "sbpfv3_return_err",
1595 ),
1596 ),
1597 (
1598 Pubkey::new_unique(),
1599 AccountSharedData::new(0, 0, &loader_v4::id()),
1600 ),
1601 (
1602 authority_address,
1603 AccountSharedData::new(0, 0, &Pubkey::new_unique()),
1604 ),
1605 (
1606 clock::id(),
1607 create_account_shared_data_for_test(&clock::Clock::default()),
1608 ),
1609 (
1610 rent::id(),
1611 create_account_shared_data_for_test(&rent::Rent::default()),
1612 ),
1613 ];
1614
1615 let accounts = process_instruction(
1617 vec![],
1618 &bincode::serialize(&LoaderV4Instruction::Finalize).unwrap(),
1619 transaction_accounts.clone(),
1620 &[(0, false, true), (5, true, false), (1, false, false)],
1621 Ok(()),
1622 );
1623 assert_eq!(
1624 accounts[0].data().len(),
1625 transaction_accounts[0].1.data().len(),
1626 );
1627 assert_eq!(accounts[0].lamports(), transaction_accounts[0].1.lamports());
1628
1629 let accounts = process_instruction(
1631 vec![],
1632 &bincode::serialize(&LoaderV4Instruction::Finalize).unwrap(),
1633 transaction_accounts.clone(),
1634 &[(0, false, true), (5, true, false), (0, false, false)],
1635 Ok(()),
1636 );
1637 assert_eq!(
1638 accounts[0].data().len(),
1639 transaction_accounts[0].1.data().len(),
1640 );
1641 assert_eq!(accounts[0].lamports(), transaction_accounts[0].1.lamports());
1642
1643 process_instruction(
1645 vec![],
1646 &bincode::serialize(&LoaderV4Instruction::Finalize).unwrap(),
1647 transaction_accounts.clone(),
1648 &[(1, false, true), (5, true, false)],
1649 Err(InstructionError::InvalidArgument),
1650 );
1651
1652 process_instruction(
1654 vec![],
1655 &bincode::serialize(&LoaderV4Instruction::Finalize).unwrap(),
1656 transaction_accounts.clone(),
1657 &[(4, false, true), (5, true, false)],
1658 Err(InstructionError::AccountDataTooSmall),
1659 );
1660
1661 process_instruction(
1663 vec![],
1664 &bincode::serialize(&LoaderV4Instruction::Finalize).unwrap(),
1665 transaction_accounts.clone(),
1666 &[(0, false, true), (5, true, false), (5, false, false)],
1667 Err(InstructionError::InvalidAccountOwner),
1668 );
1669
1670 process_instruction(
1672 vec![],
1673 &bincode::serialize(&LoaderV4Instruction::Finalize).unwrap(),
1674 transaction_accounts.clone(),
1675 &[(0, false, true), (5, true, false), (4, false, false)],
1676 Err(InstructionError::AccountDataTooSmall),
1677 );
1678
1679 process_instruction(
1681 vec![],
1682 &bincode::serialize(&LoaderV4Instruction::Finalize).unwrap(),
1683 transaction_accounts.clone(),
1684 &[(0, false, true), (5, true, false), (2, false, false)],
1685 Err(InstructionError::Immutable),
1686 );
1687
1688 process_instruction(
1690 vec![],
1691 &bincode::serialize(&LoaderV4Instruction::Finalize).unwrap(),
1692 transaction_accounts.clone(),
1693 &[(0, false, true), (5, true, false), (3, false, false)],
1694 Err(InstructionError::IncorrectAuthority),
1695 );
1696
1697 test_loader_instruction_general_errors(LoaderV4Instruction::TransferAuthority);
1698 }
1699
1700 #[test]
1701 fn test_execute_program() {
1702 let program_address = Pubkey::new_unique();
1703 let authority_address = Pubkey::new_unique();
1704 let transaction_accounts = vec![
1705 (
1706 program_address,
1707 load_program_account_from_elf(
1708 authority_address,
1709 LoaderV4Status::Finalized,
1710 "sbpfv3_return_ok",
1711 ),
1712 ),
1713 (
1714 Pubkey::new_unique(),
1715 AccountSharedData::new(10000000, 32, &program_address),
1716 ),
1717 (
1718 Pubkey::new_unique(),
1719 AccountSharedData::new(0, 0, &loader_v4::id()),
1720 ),
1721 (
1722 Pubkey::new_unique(),
1723 load_program_account_from_elf(
1724 authority_address,
1725 LoaderV4Status::Retracted,
1726 "sbpfv3_return_ok",
1727 ),
1728 ),
1729 (
1730 Pubkey::new_unique(),
1731 load_program_account_from_elf(
1732 authority_address,
1733 LoaderV4Status::Finalized,
1734 "sbpfv0_verifier_err",
1735 ),
1736 ),
1737 ];
1738
1739 process_instruction(
1741 vec![0],
1742 &[0, 1, 2, 3],
1743 transaction_accounts.clone(),
1744 &[(1, false, true)],
1745 Ok(()),
1746 );
1747
1748 process_instruction(
1750 vec![1],
1751 &[0, 1, 2, 3],
1752 transaction_accounts.clone(),
1753 &[(1, false, true)],
1754 Err(InstructionError::UnsupportedProgramId),
1755 );
1756
1757 process_instruction(
1759 vec![2],
1760 &[0, 1, 2, 3],
1761 transaction_accounts.clone(),
1762 &[(1, false, true)],
1763 Err(InstructionError::UnsupportedProgramId),
1764 );
1765
1766 process_instruction(
1769 vec![3],
1770 &[0, 1, 2, 3],
1771 transaction_accounts.clone(),
1772 &[(1, false, true)],
1773 Ok(()),
1774 );
1775
1776 process_instruction(
1778 vec![4],
1779 &[0, 1, 2, 3],
1780 transaction_accounts,
1781 &[(1, false, true)],
1782 Err(InstructionError::UnsupportedProgramId),
1783 );
1784 }
1785}