solana_svm/
program_loader.rs

1use {
2    solana_account::{state_traits::StateMut, AccountSharedData, ReadableAccount},
3    solana_clock::Slot,
4    solana_instruction::error::InstructionError,
5    solana_loader_v3_interface::state::UpgradeableLoaderState,
6    solana_loader_v4_interface::state::{LoaderV4State, LoaderV4Status},
7    solana_program_runtime::loaded_programs::{
8        LoadProgramMetrics, ProgramCacheEntry, ProgramCacheEntryOwner, ProgramCacheEntryType,
9        ProgramRuntimeEnvironment, ProgramRuntimeEnvironments, DELAY_VISIBILITY_SLOT_OFFSET,
10    },
11    solana_pubkey::Pubkey,
12    solana_sdk_ids::{bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, loader_v4},
13    solana_svm_callback::TransactionProcessingCallback,
14    solana_svm_timings::ExecuteTimings,
15    solana_svm_type_overrides::sync::Arc,
16    solana_transaction_error::{TransactionError, TransactionResult},
17};
18
19#[derive(Debug)]
20pub(crate) enum ProgramAccountLoadResult {
21    InvalidAccountData(ProgramCacheEntryOwner),
22    ProgramOfLoaderV1(AccountSharedData),
23    ProgramOfLoaderV2(AccountSharedData),
24    ProgramOfLoaderV3(AccountSharedData, AccountSharedData, Slot),
25    ProgramOfLoaderV4(AccountSharedData, Slot),
26}
27
28pub(crate) fn load_program_from_bytes(
29    load_program_metrics: &mut LoadProgramMetrics,
30    programdata: &[u8],
31    loader_key: &Pubkey,
32    account_size: usize,
33    deployment_slot: Slot,
34    program_runtime_environment: ProgramRuntimeEnvironment,
35    reloading: bool,
36) -> std::result::Result<ProgramCacheEntry, Box<dyn std::error::Error>> {
37    if reloading {
38        // Safety: this is safe because the program is being reloaded in the cache.
39        unsafe {
40            ProgramCacheEntry::reload(
41                loader_key,
42                program_runtime_environment.clone(),
43                deployment_slot,
44                deployment_slot.saturating_add(DELAY_VISIBILITY_SLOT_OFFSET),
45                programdata,
46                account_size,
47                load_program_metrics,
48            )
49        }
50    } else {
51        ProgramCacheEntry::new(
52            loader_key,
53            program_runtime_environment.clone(),
54            deployment_slot,
55            deployment_slot.saturating_add(DELAY_VISIBILITY_SLOT_OFFSET),
56            programdata,
57            account_size,
58            load_program_metrics,
59        )
60    }
61}
62
63pub(crate) fn load_program_accounts<CB: TransactionProcessingCallback>(
64    callbacks: &CB,
65    pubkey: &Pubkey,
66) -> Option<ProgramAccountLoadResult> {
67    let (program_account, _slot) = callbacks.get_account_shared_data(pubkey)?;
68
69    if loader_v4::check_id(program_account.owner()) {
70        return Some(
71            solana_loader_v4_program::get_state(program_account.data())
72                .ok()
73                .and_then(|state| {
74                    (!matches!(state.status, LoaderV4Status::Retracted)).then_some(state.slot)
75                })
76                .map(|slot| ProgramAccountLoadResult::ProgramOfLoaderV4(program_account, slot))
77                .unwrap_or(ProgramAccountLoadResult::InvalidAccountData(
78                    ProgramCacheEntryOwner::LoaderV4,
79                )),
80        );
81    }
82
83    if bpf_loader_upgradeable::check_id(program_account.owner()) {
84        if let Ok(UpgradeableLoaderState::Program {
85            programdata_address,
86        }) = program_account.state()
87        {
88            if let Some((programdata_account, _slot)) =
89                callbacks.get_account_shared_data(&programdata_address)
90            {
91                if let Ok(UpgradeableLoaderState::ProgramData {
92                    slot,
93                    upgrade_authority_address: _,
94                }) = programdata_account.state()
95                {
96                    return Some(ProgramAccountLoadResult::ProgramOfLoaderV3(
97                        program_account,
98                        programdata_account,
99                        slot,
100                    ));
101                }
102            }
103        }
104        return Some(ProgramAccountLoadResult::InvalidAccountData(
105            ProgramCacheEntryOwner::LoaderV3,
106        ));
107    }
108
109    if bpf_loader::check_id(program_account.owner()) {
110        return Some(ProgramAccountLoadResult::ProgramOfLoaderV2(program_account));
111    }
112
113    if bpf_loader_deprecated::check_id(program_account.owner()) {
114        return Some(ProgramAccountLoadResult::ProgramOfLoaderV1(program_account));
115    }
116
117    None
118}
119
120/// Loads the program with the given pubkey.
121///
122/// If the account doesn't exist it returns `None`. If the account does exist, it must be a program
123/// account (belong to one of the program loaders). Returns `Some(InvalidAccountData)` if the program
124/// account is `Closed`, contains invalid data or any of the programdata accounts are invalid.
125pub fn load_program_with_pubkey<CB: TransactionProcessingCallback>(
126    callbacks: &CB,
127    environments: &ProgramRuntimeEnvironments,
128    pubkey: &Pubkey,
129    slot: Slot,
130    execute_timings: &mut ExecuteTimings,
131    reload: bool,
132) -> Option<Arc<ProgramCacheEntry>> {
133    let mut load_program_metrics = LoadProgramMetrics {
134        program_id: pubkey.to_string(),
135        ..LoadProgramMetrics::default()
136    };
137
138    let loaded_program = match load_program_accounts(callbacks, pubkey)? {
139        ProgramAccountLoadResult::InvalidAccountData(owner) => Ok(
140            ProgramCacheEntry::new_tombstone(slot, owner, ProgramCacheEntryType::Closed),
141        ),
142
143        ProgramAccountLoadResult::ProgramOfLoaderV1(program_account) => load_program_from_bytes(
144            &mut load_program_metrics,
145            program_account.data(),
146            program_account.owner(),
147            program_account.data().len(),
148            0,
149            environments.program_runtime_v1.clone(),
150            reload,
151        )
152        .map_err(|_| (0, ProgramCacheEntryOwner::LoaderV1)),
153
154        ProgramAccountLoadResult::ProgramOfLoaderV2(program_account) => load_program_from_bytes(
155            &mut load_program_metrics,
156            program_account.data(),
157            program_account.owner(),
158            program_account.data().len(),
159            0,
160            environments.program_runtime_v1.clone(),
161            reload,
162        )
163        .map_err(|_| (0, ProgramCacheEntryOwner::LoaderV2)),
164
165        ProgramAccountLoadResult::ProgramOfLoaderV3(program_account, programdata_account, slot) => {
166            programdata_account
167                .data()
168                .get(UpgradeableLoaderState::size_of_programdata_metadata()..)
169                .ok_or(Box::new(InstructionError::InvalidAccountData).into())
170                .and_then(|programdata| {
171                    load_program_from_bytes(
172                        &mut load_program_metrics,
173                        programdata,
174                        program_account.owner(),
175                        program_account
176                            .data()
177                            .len()
178                            .saturating_add(programdata_account.data().len()),
179                        slot,
180                        environments.program_runtime_v1.clone(),
181                        reload,
182                    )
183                })
184                .map_err(|_| (slot, ProgramCacheEntryOwner::LoaderV3))
185        }
186
187        ProgramAccountLoadResult::ProgramOfLoaderV4(program_account, slot) => program_account
188            .data()
189            .get(LoaderV4State::program_data_offset()..)
190            .ok_or(Box::new(InstructionError::InvalidAccountData).into())
191            .and_then(|elf_bytes| {
192                load_program_from_bytes(
193                    &mut load_program_metrics,
194                    elf_bytes,
195                    &loader_v4::id(),
196                    program_account.data().len(),
197                    slot,
198                    environments.program_runtime_v1.clone(),
199                    reload,
200                )
201            })
202            .map_err(|_| (slot, ProgramCacheEntryOwner::LoaderV4)),
203    }
204    .unwrap_or_else(|(slot, owner)| {
205        let env = environments.program_runtime_v1.clone();
206        ProgramCacheEntry::new_tombstone(
207            slot,
208            owner,
209            ProgramCacheEntryType::FailedVerification(env),
210        )
211    });
212
213    load_program_metrics.submit_datapoint(&mut execute_timings.details);
214    loaded_program.update_access_slot(slot);
215    Some(Arc::new(loaded_program))
216}
217
218/// Find the slot in which the program was most recently modified.
219/// Returns slot 0 for programs deployed with v1/v2 loaders, since programs deployed
220/// with those loaders do not retain deployment slot information.
221/// Returns an error if the program's account state can not be found or parsed.
222pub(crate) fn get_program_modification_slot<CB: TransactionProcessingCallback>(
223    callbacks: &CB,
224    pubkey: &Pubkey,
225) -> TransactionResult<Slot> {
226    let (program, _slot) = callbacks
227        .get_account_shared_data(pubkey)
228        .ok_or(TransactionError::ProgramAccountNotFound)?;
229    if bpf_loader_upgradeable::check_id(program.owner()) {
230        if let Ok(UpgradeableLoaderState::Program {
231            programdata_address,
232        }) = program.state()
233        {
234            let (programdata, _slot) = callbacks
235                .get_account_shared_data(&programdata_address)
236                .ok_or(TransactionError::ProgramAccountNotFound)?;
237            if let Ok(UpgradeableLoaderState::ProgramData {
238                slot,
239                upgrade_authority_address: _,
240            }) = programdata.state()
241            {
242                return Ok(slot);
243            }
244        }
245        Err(TransactionError::ProgramAccountNotFound)
246    } else if loader_v4::check_id(program.owner()) {
247        let state = solana_loader_v4_program::get_state(program.data())
248            .map_err(|_| TransactionError::ProgramAccountNotFound)?;
249        Ok(state.slot)
250    } else {
251        Ok(0)
252    }
253}
254
255#[cfg(test)]
256mod tests {
257    use {
258        super::*,
259        crate::transaction_processor::TransactionBatchProcessor,
260        solana_account::WritableAccount,
261        solana_program_runtime::{
262            loaded_programs::{BlockRelation, ForkGraph, ProgramRuntimeEnvironments},
263            solana_sbpf::program::BuiltinProgram,
264        },
265        solana_sdk_ids::{bpf_loader, bpf_loader_upgradeable},
266        solana_svm_callback::InvokeContextCallback,
267        std::{
268            cell::RefCell,
269            collections::HashMap,
270            env,
271            fs::{self, File},
272            io::Read,
273        },
274    };
275
276    struct TestForkGraph {}
277
278    impl ForkGraph for TestForkGraph {
279        fn relationship(&self, _a: Slot, _b: Slot) -> BlockRelation {
280            BlockRelation::Unknown
281        }
282    }
283
284    #[derive(Default, Clone)]
285    pub(crate) struct MockBankCallback {
286        pub(crate) account_shared_data: RefCell<HashMap<Pubkey, AccountSharedData>>,
287    }
288
289    impl InvokeContextCallback for MockBankCallback {}
290
291    impl TransactionProcessingCallback for MockBankCallback {
292        fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option<(AccountSharedData, Slot)> {
293            self.account_shared_data
294                .borrow()
295                .get(pubkey)
296                .map(|account| (account.clone(), 0))
297        }
298    }
299
300    #[test]
301    fn test_load_program_accounts_account_not_found() {
302        let mock_bank = MockBankCallback::default();
303        let key = Pubkey::new_unique();
304
305        let result = load_program_accounts(&mock_bank, &key);
306        assert!(result.is_none());
307
308        let mut account_data = AccountSharedData::default();
309        account_data.set_owner(bpf_loader_upgradeable::id());
310        let state = UpgradeableLoaderState::Program {
311            programdata_address: Pubkey::new_unique(),
312        };
313        account_data.set_data(bincode::serialize(&state).unwrap());
314        mock_bank
315            .account_shared_data
316            .borrow_mut()
317            .insert(key, account_data.clone());
318
319        let result = load_program_accounts(&mock_bank, &key);
320        assert!(matches!(
321            result,
322            Some(ProgramAccountLoadResult::InvalidAccountData(_))
323        ));
324
325        account_data.set_data(Vec::new());
326        mock_bank
327            .account_shared_data
328            .borrow_mut()
329            .insert(key, account_data);
330
331        let result = load_program_accounts(&mock_bank, &key);
332
333        assert!(matches!(
334            result,
335            Some(ProgramAccountLoadResult::InvalidAccountData(_))
336        ));
337    }
338
339    #[test]
340    fn test_load_program_accounts_loader_v4() {
341        let key = Pubkey::new_unique();
342        let mock_bank = MockBankCallback::default();
343        let mut account_data = AccountSharedData::default();
344        account_data.set_owner(loader_v4::id());
345        mock_bank
346            .account_shared_data
347            .borrow_mut()
348            .insert(key, account_data.clone());
349
350        let result = load_program_accounts(&mock_bank, &key);
351        assert!(matches!(
352            result,
353            Some(ProgramAccountLoadResult::InvalidAccountData(_))
354        ));
355
356        account_data.set_data(vec![0; 64]);
357        mock_bank
358            .account_shared_data
359            .borrow_mut()
360            .insert(key, account_data.clone());
361        let result = load_program_accounts(&mock_bank, &key);
362        assert!(matches!(
363            result,
364            Some(ProgramAccountLoadResult::InvalidAccountData(_))
365        ));
366
367        let loader_data = LoaderV4State {
368            slot: 25,
369            authority_address_or_next_version: Pubkey::new_unique(),
370            status: LoaderV4Status::Deployed,
371        };
372        let encoded = unsafe {
373            std::mem::transmute::<&LoaderV4State, &[u8; LoaderV4State::program_data_offset()]>(
374                &loader_data,
375            )
376        };
377        account_data.set_data(encoded.to_vec());
378        mock_bank
379            .account_shared_data
380            .borrow_mut()
381            .insert(key, account_data.clone());
382
383        let result = load_program_accounts(&mock_bank, &key);
384
385        match result {
386            Some(ProgramAccountLoadResult::ProgramOfLoaderV4(data, slot)) => {
387                assert_eq!(data, account_data);
388                assert_eq!(slot, 25);
389            }
390
391            _ => panic!("Invalid result"),
392        }
393    }
394
395    #[test]
396    fn test_load_program_accounts_loader_v1_or_v2() {
397        let key = Pubkey::new_unique();
398        let mock_bank = MockBankCallback::default();
399        let mut account_data = AccountSharedData::default();
400        account_data.set_owner(bpf_loader::id());
401        mock_bank
402            .account_shared_data
403            .borrow_mut()
404            .insert(key, account_data.clone());
405
406        let result = load_program_accounts(&mock_bank, &key);
407        match result {
408            Some(ProgramAccountLoadResult::ProgramOfLoaderV1(data))
409            | Some(ProgramAccountLoadResult::ProgramOfLoaderV2(data)) => {
410                assert_eq!(data, account_data);
411            }
412            _ => panic!("Invalid result"),
413        }
414    }
415
416    #[test]
417    fn test_load_program_accounts_success() {
418        let key1 = Pubkey::new_unique();
419        let key2 = Pubkey::new_unique();
420        let mock_bank = MockBankCallback::default();
421
422        let mut account_data = AccountSharedData::default();
423        account_data.set_owner(bpf_loader_upgradeable::id());
424
425        let state = UpgradeableLoaderState::Program {
426            programdata_address: key2,
427        };
428        account_data.set_data(bincode::serialize(&state).unwrap());
429        mock_bank
430            .account_shared_data
431            .borrow_mut()
432            .insert(key1, account_data.clone());
433
434        let state = UpgradeableLoaderState::ProgramData {
435            slot: 25,
436            upgrade_authority_address: None,
437        };
438        let mut account_data2 = AccountSharedData::default();
439        account_data2.set_data(bincode::serialize(&state).unwrap());
440        mock_bank
441            .account_shared_data
442            .borrow_mut()
443            .insert(key2, account_data2.clone());
444
445        let result = load_program_accounts(&mock_bank, &key1);
446
447        match result {
448            Some(ProgramAccountLoadResult::ProgramOfLoaderV3(data1, data2, slot)) => {
449                assert_eq!(data1, account_data);
450                assert_eq!(data2, account_data2);
451                assert_eq!(slot, 25);
452            }
453
454            _ => panic!("Invalid result"),
455        }
456    }
457
458    fn load_test_program() -> Vec<u8> {
459        let mut dir = env::current_dir().unwrap();
460        dir.push("tests");
461        dir.push("example-programs");
462        dir.push("hello-solana");
463        dir.push("hello_solana_program.so");
464        let mut file = File::open(dir.clone()).expect("file not found");
465        let metadata = fs::metadata(dir).expect("Unable to read metadata");
466        let mut buffer = vec![0; metadata.len() as usize];
467        file.read_exact(&mut buffer).expect("Buffer overflow");
468        buffer
469    }
470
471    #[test]
472    fn test_load_program_from_bytes() {
473        let buffer = load_test_program();
474
475        let mut metrics = LoadProgramMetrics::default();
476        let loader = bpf_loader_upgradeable::id();
477        let size = buffer.len();
478        let slot = 2;
479        let environment = ProgramRuntimeEnvironment::new(BuiltinProgram::new_mock());
480
481        let result = load_program_from_bytes(
482            &mut metrics,
483            &buffer,
484            &loader,
485            size,
486            slot,
487            environment.clone(),
488            false,
489        );
490
491        assert!(result.is_ok());
492
493        let result = load_program_from_bytes(
494            &mut metrics,
495            &buffer,
496            &loader,
497            size,
498            slot,
499            environment,
500            true,
501        );
502
503        assert!(result.is_ok());
504    }
505
506    #[test]
507    fn test_load_program_not_found() {
508        let mock_bank = MockBankCallback::default();
509        let key = Pubkey::new_unique();
510        let batch_processor = TransactionBatchProcessor::<TestForkGraph>::default();
511
512        let result = load_program_with_pubkey(
513            &mock_bank,
514            &batch_processor.get_environments_for_epoch(50),
515            &key,
516            500,
517            &mut ExecuteTimings::default(),
518            false,
519        );
520        assert!(result.is_none());
521    }
522
523    #[test]
524    fn test_load_program_invalid_account_data() {
525        let key = Pubkey::new_unique();
526        let mock_bank = MockBankCallback::default();
527        let mut account_data = AccountSharedData::default();
528        account_data.set_owner(loader_v4::id());
529        let batch_processor = TransactionBatchProcessor::<TestForkGraph>::default();
530        mock_bank
531            .account_shared_data
532            .borrow_mut()
533            .insert(key, account_data.clone());
534
535        let result = load_program_with_pubkey(
536            &mock_bank,
537            &batch_processor.get_environments_for_epoch(20),
538            &key,
539            0, // Slot 0
540            &mut ExecuteTimings::default(),
541            false,
542        );
543
544        let loaded_program = ProgramCacheEntry::new_tombstone(
545            0, // Slot 0
546            ProgramCacheEntryOwner::LoaderV4,
547            ProgramCacheEntryType::FailedVerification(
548                batch_processor
549                    .get_environments_for_epoch(20)
550                    .program_runtime_v1,
551            ),
552        );
553        assert_eq!(result.unwrap(), Arc::new(loaded_program));
554    }
555
556    #[test]
557    fn test_load_program_program_loader_v1_or_v2() {
558        let key = Pubkey::new_unique();
559        let mock_bank = MockBankCallback::default();
560        let mut account_data = AccountSharedData::default();
561        account_data.set_owner(bpf_loader::id());
562        let batch_processor = TransactionBatchProcessor::<TestForkGraph>::default();
563        mock_bank
564            .account_shared_data
565            .borrow_mut()
566            .insert(key, account_data.clone());
567
568        // This should return an error
569        let result = load_program_with_pubkey(
570            &mock_bank,
571            &batch_processor.get_environments_for_epoch(20),
572            &key,
573            200,
574            &mut ExecuteTimings::default(),
575            false,
576        );
577        let loaded_program = ProgramCacheEntry::new_tombstone(
578            0,
579            ProgramCacheEntryOwner::LoaderV2,
580            ProgramCacheEntryType::FailedVerification(
581                batch_processor
582                    .get_environments_for_epoch(20)
583                    .program_runtime_v1,
584            ),
585        );
586        assert_eq!(result.unwrap(), Arc::new(loaded_program));
587
588        let buffer = load_test_program();
589        account_data.set_data(buffer);
590
591        mock_bank
592            .account_shared_data
593            .borrow_mut()
594            .insert(key, account_data.clone());
595
596        let result = load_program_with_pubkey(
597            &mock_bank,
598            &batch_processor.get_environments_for_epoch(20),
599            &key,
600            200,
601            &mut ExecuteTimings::default(),
602            false,
603        );
604
605        let environments = ProgramRuntimeEnvironments::default();
606        let expected = load_program_from_bytes(
607            &mut LoadProgramMetrics::default(),
608            account_data.data(),
609            account_data.owner(),
610            account_data.data().len(),
611            0,
612            environments.program_runtime_v1.clone(),
613            false,
614        );
615
616        assert_eq!(result.unwrap(), Arc::new(expected.unwrap()));
617    }
618
619    #[test]
620    fn test_load_program_program_loader_v3() {
621        let key1 = Pubkey::new_unique();
622        let key2 = Pubkey::new_unique();
623        let mock_bank = MockBankCallback::default();
624        let batch_processor = TransactionBatchProcessor::<TestForkGraph>::default();
625
626        let mut account_data = AccountSharedData::default();
627        account_data.set_owner(bpf_loader_upgradeable::id());
628
629        let state = UpgradeableLoaderState::Program {
630            programdata_address: key2,
631        };
632        account_data.set_data(bincode::serialize(&state).unwrap());
633        mock_bank
634            .account_shared_data
635            .borrow_mut()
636            .insert(key1, account_data.clone());
637
638        let state = UpgradeableLoaderState::ProgramData {
639            slot: 0,
640            upgrade_authority_address: None,
641        };
642        let mut account_data2 = AccountSharedData::default();
643        account_data2.set_data(bincode::serialize(&state).unwrap());
644        mock_bank
645            .account_shared_data
646            .borrow_mut()
647            .insert(key2, account_data2.clone());
648
649        // This should return an error
650        let result = load_program_with_pubkey(
651            &mock_bank,
652            &batch_processor.get_environments_for_epoch(0),
653            &key1,
654            0,
655            &mut ExecuteTimings::default(),
656            false,
657        );
658        let loaded_program = ProgramCacheEntry::new_tombstone(
659            0,
660            ProgramCacheEntryOwner::LoaderV3,
661            ProgramCacheEntryType::FailedVerification(
662                batch_processor
663                    .get_environments_for_epoch(0)
664                    .program_runtime_v1,
665            ),
666        );
667        assert_eq!(result.unwrap(), Arc::new(loaded_program));
668
669        let mut buffer = load_test_program();
670        let mut header = bincode::serialize(&state).unwrap();
671        let mut complement = vec![
672            0;
673            std::cmp::max(
674                0,
675                UpgradeableLoaderState::size_of_programdata_metadata() - header.len()
676            )
677        ];
678        header.append(&mut complement);
679        header.append(&mut buffer);
680        account_data.set_data(header);
681
682        mock_bank
683            .account_shared_data
684            .borrow_mut()
685            .insert(key2, account_data.clone());
686
687        let result = load_program_with_pubkey(
688            &mock_bank,
689            &batch_processor.get_environments_for_epoch(20),
690            &key1,
691            200,
692            &mut ExecuteTimings::default(),
693            false,
694        );
695
696        let data = account_data.data();
697        account_data
698            .set_data(data[UpgradeableLoaderState::size_of_programdata_metadata()..].to_vec());
699
700        let environments = ProgramRuntimeEnvironments::default();
701        let expected = load_program_from_bytes(
702            &mut LoadProgramMetrics::default(),
703            account_data.data(),
704            account_data.owner(),
705            account_data.data().len(),
706            0,
707            environments.program_runtime_v1.clone(),
708            false,
709        );
710        assert_eq!(result.unwrap(), Arc::new(expected.unwrap()));
711    }
712
713    #[test]
714    fn test_load_program_of_loader_v4() {
715        let key = Pubkey::new_unique();
716        let mock_bank = MockBankCallback::default();
717        let mut account_data = AccountSharedData::default();
718        account_data.set_owner(loader_v4::id());
719        let batch_processor = TransactionBatchProcessor::<TestForkGraph>::default();
720
721        let loader_data = LoaderV4State {
722            slot: 0,
723            authority_address_or_next_version: Pubkey::new_unique(),
724            status: LoaderV4Status::Deployed,
725        };
726        let encoded = unsafe {
727            std::mem::transmute::<&LoaderV4State, &[u8; LoaderV4State::program_data_offset()]>(
728                &loader_data,
729            )
730        };
731        account_data.set_data(encoded.to_vec());
732        mock_bank
733            .account_shared_data
734            .borrow_mut()
735            .insert(key, account_data.clone());
736
737        let result = load_program_with_pubkey(
738            &mock_bank,
739            &batch_processor.get_environments_for_epoch(0),
740            &key,
741            0,
742            &mut ExecuteTimings::default(),
743            false,
744        );
745        let loaded_program = ProgramCacheEntry::new_tombstone(
746            0,
747            ProgramCacheEntryOwner::LoaderV4,
748            ProgramCacheEntryType::FailedVerification(
749                batch_processor
750                    .get_environments_for_epoch(0)
751                    .program_runtime_v1,
752            ),
753        );
754        assert_eq!(result.unwrap(), Arc::new(loaded_program));
755
756        let mut header = account_data.data().to_vec();
757        let mut complement =
758            vec![0; std::cmp::max(0, LoaderV4State::program_data_offset() - header.len())];
759        header.append(&mut complement);
760
761        let mut buffer = load_test_program();
762        header.append(&mut buffer);
763
764        account_data.set_data(header);
765        mock_bank
766            .account_shared_data
767            .borrow_mut()
768            .insert(key, account_data.clone());
769
770        let result = load_program_with_pubkey(
771            &mock_bank,
772            &batch_processor.get_environments_for_epoch(20),
773            &key,
774            200,
775            &mut ExecuteTimings::default(),
776            false,
777        );
778
779        let data = account_data.data()[LoaderV4State::program_data_offset()..].to_vec();
780        account_data.set_data(data);
781        mock_bank
782            .account_shared_data
783            .borrow_mut()
784            .insert(key, account_data.clone());
785
786        let environments = ProgramRuntimeEnvironments::default();
787        let expected = load_program_from_bytes(
788            &mut LoadProgramMetrics::default(),
789            account_data.data(),
790            account_data.owner(),
791            account_data.data().len(),
792            0,
793            environments.program_runtime_v1.clone(),
794            false,
795        );
796        assert_eq!(result.unwrap(), Arc::new(expected.unwrap()));
797    }
798
799    #[test]
800    fn test_load_program_environment() {
801        let key = Pubkey::new_unique();
802        let mock_bank = MockBankCallback::default();
803        let mut account_data = AccountSharedData::default();
804        account_data.set_owner(bpf_loader::id());
805        let batch_processor = TransactionBatchProcessor::<TestForkGraph>::default();
806        let upcoming_environments = ProgramRuntimeEnvironments::default();
807        let current_environments = batch_processor.environments.clone();
808        {
809            let mut epoch_boundary_preparation =
810                batch_processor.epoch_boundary_preparation.write().unwrap();
811            epoch_boundary_preparation.upcoming_epoch = 1;
812            epoch_boundary_preparation.upcoming_environments = Some(upcoming_environments.clone());
813        }
814        mock_bank
815            .account_shared_data
816            .borrow_mut()
817            .insert(key, account_data.clone());
818
819        for is_upcoming_env in [false, true] {
820            let result = load_program_with_pubkey(
821                &mock_bank,
822                &batch_processor.get_environments_for_epoch(is_upcoming_env as u64),
823                &key,
824                200,
825                &mut ExecuteTimings::default(),
826                false,
827            )
828            .unwrap();
829            assert_ne!(
830                is_upcoming_env,
831                Arc::ptr_eq(
832                    result.program.get_environment().unwrap(),
833                    &current_environments.program_runtime_v1,
834                )
835            );
836            assert_eq!(
837                is_upcoming_env,
838                Arc::ptr_eq(
839                    result.program.get_environment().unwrap(),
840                    &upcoming_environments.program_runtime_v1,
841                )
842            );
843        }
844    }
845
846    #[test]
847    fn test_program_modification_slot_account_not_found() {
848        let mock_bank = MockBankCallback::default();
849
850        let key = Pubkey::new_unique();
851
852        let result = get_program_modification_slot(&mock_bank, &key);
853        assert_eq!(result.err(), Some(TransactionError::ProgramAccountNotFound));
854
855        let mut account_data = AccountSharedData::new(100, 100, &bpf_loader_upgradeable::id());
856        mock_bank
857            .account_shared_data
858            .borrow_mut()
859            .insert(key, account_data.clone());
860
861        let result = get_program_modification_slot(&mock_bank, &key);
862        assert_eq!(result.err(), Some(TransactionError::ProgramAccountNotFound));
863
864        let state = UpgradeableLoaderState::Program {
865            programdata_address: Pubkey::new_unique(),
866        };
867        account_data.set_data(bincode::serialize(&state).unwrap());
868        mock_bank
869            .account_shared_data
870            .borrow_mut()
871            .insert(key, account_data.clone());
872
873        let result = get_program_modification_slot(&mock_bank, &key);
874        assert_eq!(result.err(), Some(TransactionError::ProgramAccountNotFound));
875
876        account_data.set_owner(loader_v4::id());
877        mock_bank
878            .account_shared_data
879            .borrow_mut()
880            .insert(key, account_data);
881
882        let result = get_program_modification_slot(&mock_bank, &key);
883        assert_eq!(result.err(), Some(TransactionError::ProgramAccountNotFound));
884    }
885
886    #[test]
887    fn test_program_modification_slot_success() {
888        let mock_bank = MockBankCallback::default();
889
890        let key1 = Pubkey::new_unique();
891        let key2 = Pubkey::new_unique();
892
893        let account_data = AccountSharedData::new_data(
894            100,
895            &UpgradeableLoaderState::Program {
896                programdata_address: key2,
897            },
898            &bpf_loader_upgradeable::id(),
899        )
900        .unwrap();
901        mock_bank
902            .account_shared_data
903            .borrow_mut()
904            .insert(key1, account_data);
905
906        let account_data = AccountSharedData::new_data(
907            100,
908            &UpgradeableLoaderState::ProgramData {
909                slot: 77,
910                upgrade_authority_address: None,
911            },
912            &bpf_loader_upgradeable::id(),
913        )
914        .unwrap();
915        mock_bank
916            .account_shared_data
917            .borrow_mut()
918            .insert(key2, account_data);
919
920        let result = get_program_modification_slot(&mock_bank, &key1);
921        assert_eq!(result.unwrap(), 77);
922
923        let state = LoaderV4State {
924            slot: 58,
925            authority_address_or_next_version: Pubkey::new_unique(),
926            status: LoaderV4Status::Deployed,
927        };
928        let encoded = unsafe {
929            std::mem::transmute::<&LoaderV4State, &[u8; LoaderV4State::program_data_offset()]>(
930                &state,
931            )
932        };
933        let mut account_data = AccountSharedData::new(100, encoded.len(), &loader_v4::id());
934        account_data.set_data(encoded.to_vec());
935        mock_bank
936            .account_shared_data
937            .borrow_mut()
938            .insert(key1, account_data.clone());
939
940        let result = get_program_modification_slot(&mock_bank, &key1);
941        assert_eq!(result.unwrap(), 58);
942
943        account_data.set_owner(Pubkey::new_unique());
944        mock_bank
945            .account_shared_data
946            .borrow_mut()
947            .insert(key2, account_data);
948
949        let result = get_program_modification_slot(&mock_bank, &key2);
950        assert_eq!(result.unwrap(), 0);
951    }
952}