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 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#[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
215pub(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, &mut ExecuteTimings::default(),
552 false,
553 );
554
555 let loaded_program = ProgramCacheEntry::new_tombstone(
556 0, 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 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 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 ¤t_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}