solana_svm/
program_loader.rs

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