solana_loader_v4_program/
lib.rs

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    // Slot = 0 indicates that the program hasn't been deployed yet. So no need to check for the cooldown slots.
296    // (Without this check, the program deployment is failing in freshly started test validators. That's
297    //  because at startup current_slot is 0, which is < DEPLOYMENT_COOLDOWN_IN_SLOTS).
298    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        // Error: Missing program account
670        process_instruction(
671            vec![],
672            &instruction,
673            transaction_accounts.clone(),
674            &[],
675            Err(InstructionError::NotEnoughAccountKeys),
676        );
677
678        // Error: Missing authority account
679        process_instruction(
680            vec![],
681            &instruction,
682            transaction_accounts.clone(),
683            &[(0, false, true)],
684            Err(InstructionError::NotEnoughAccountKeys),
685        );
686
687        // Error: Program not owned by loader
688        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        // Error: Program is not writeable
697        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        // Error: Authority did not sign
706        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        // Error: Program is finalized
715        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        // Error: Incorrect authority provided
724        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        // Overwrite existing data
768        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        // Empty write
781        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        // Error: Program is not retracted
794        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        // Error: Write out of bounds
807        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        // Overwrite existing data
865        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        // Empty copy
879        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        // Error: Program is not retracted
893        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        // Error: Destination and source collide
907        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        // Error: Read out of bounds
921        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        // Error: Write out of bounds
940        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        // No change
1019        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        // Initialize program account
1041        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        // Increase program account size
1065        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        // Decrease program account size, with a recipient
1089        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        // Decrease program account size, without a recipient
1119        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        // Close program account
1141        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        // Error: Program not owned by loader
1160        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        // Error: Program is not writeable
1169        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        // Error: Close program account without a recipient
1178        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        // Error: Authority did not sign
1187        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        // Error: Program is and stays uninitialized
1196        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        // Error: Program is not retracted
1205        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        // Error: Recipient is not writeable
1214        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        // Error: Insufficient funds
1223        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        // Deploy from its own data
1288        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        // Error: Source program is not writable
1304        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        // Error: Source program is not retracted
1313        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        // Redeploy: Retract, then replace data by other source
1322        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        // Error: Program was deployed recently, cooldown still in effect
1347        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        // Error: Program is uninitialized
1357        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        // Error: Program fails verification
1366        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        // Error: Program is deployed already
1375        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        // Retract program
1422        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        // Error: Program is uninitialized
1436        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        // Error: Program is not deployed
1445        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        // Error: Program was deployed recently, cooldown still in effect
1454        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        // Transfer authority
1509        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        // Error: No new authority provided
1523        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        // Error: Program is uninitialized
1532        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        // Error: New authority did not sign
1541        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        // Error: Authority did not change
1550        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        // Finalize program with a next version
1616        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        // Finalize program with itself as next version
1630        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        // Error: Program must be deployed to be finalized
1644        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        // Error: Program is uninitialized
1653        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        // Error: Next version not owned by loader
1662        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        // Error: Program is uninitialized
1671        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        // Error: Next version is finalized
1680        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        // Error: Incorrect authority of next version
1689        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        // Execute program
1740        process_instruction(
1741            vec![0],
1742            &[0, 1, 2, 3],
1743            transaction_accounts.clone(),
1744            &[(1, false, true)],
1745            Ok(()),
1746        );
1747
1748        // Error: Program not owned by loader
1749        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        // Error: Program is uninitialized
1758        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        // Error: Program is not deployed
1767        // This is only checked in integration with load_program_accounts() in the SVM
1768        process_instruction(
1769            vec![3],
1770            &[0, 1, 2, 3],
1771            transaction_accounts.clone(),
1772            &[(1, false, true)],
1773            Ok(()),
1774        );
1775
1776        // Error: Program fails verification
1777        process_instruction(
1778            vec![4],
1779            &[0, 1, 2, 3],
1780            transaction_accounts,
1781            &[(1, false, true)],
1782            Err(InstructionError::UnsupportedProgramId),
1783        );
1784    }
1785}