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 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
116pub 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
214pub(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, &mut ExecuteTimings::default(),
545 false,
546 );
547
548 let loaded_program = ProgramCacheEntry::new_tombstone(
549 0, 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 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 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 ¤t_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}