clone_solana_loader_v4_program/
lib.rs

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