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