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