solana_svm/
program_loader.rs

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