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