solana_loader_v4_program/
lib.rs

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