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_timings::ExecuteTimings,
15 solana_transaction_error::{TransactionError, TransactionResult},
16 solana_type_overrides::sync::Arc,
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 = 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) = callbacks.get_account_shared_data(&programdata_address) {
96 if let Ok(UpgradeableLoaderState::ProgramData {
97 slot,
98 upgrade_authority_address: _,
99 }) = programdata_account.state()
100 {
101 return Some(ProgramAccountLoadResult::ProgramOfLoaderV3(
102 program_account,
103 programdata_account,
104 slot,
105 ));
106 }
107 }
108 }
109 Some(ProgramAccountLoadResult::InvalidAccountData(
110 ProgramCacheEntryOwner::LoaderV3,
111 ))
112}
113
114pub fn load_program_with_pubkey<CB: TransactionProcessingCallback>(
120 callbacks: &CB,
121 environments: &ProgramRuntimeEnvironments,
122 pubkey: &Pubkey,
123 slot: Slot,
124 execute_timings: &mut ExecuteTimings,
125 reload: bool,
126) -> Option<Arc<ProgramCacheEntry>> {
127 let mut load_program_metrics = LoadProgramMetrics {
128 program_id: pubkey.to_string(),
129 ..LoadProgramMetrics::default()
130 };
131
132 let loaded_program = match load_program_accounts(callbacks, pubkey)? {
133 ProgramAccountLoadResult::InvalidAccountData(owner) => Ok(
134 ProgramCacheEntry::new_tombstone(slot, owner, ProgramCacheEntryType::Closed),
135 ),
136
137 ProgramAccountLoadResult::ProgramOfLoaderV1(program_account) => load_program_from_bytes(
138 &mut load_program_metrics,
139 program_account.data(),
140 program_account.owner(),
141 program_account.data().len(),
142 0,
143 environments.program_runtime_v1.clone(),
144 reload,
145 )
146 .map_err(|_| (0, ProgramCacheEntryOwner::LoaderV1)),
147
148 ProgramAccountLoadResult::ProgramOfLoaderV2(program_account) => load_program_from_bytes(
149 &mut load_program_metrics,
150 program_account.data(),
151 program_account.owner(),
152 program_account.data().len(),
153 0,
154 environments.program_runtime_v1.clone(),
155 reload,
156 )
157 .map_err(|_| (0, ProgramCacheEntryOwner::LoaderV2)),
158
159 ProgramAccountLoadResult::ProgramOfLoaderV3(program_account, programdata_account, slot) => {
160 programdata_account
161 .data()
162 .get(UpgradeableLoaderState::size_of_programdata_metadata()..)
163 .ok_or(Box::new(InstructionError::InvalidAccountData).into())
164 .and_then(|programdata| {
165 load_program_from_bytes(
166 &mut load_program_metrics,
167 programdata,
168 program_account.owner(),
169 program_account
170 .data()
171 .len()
172 .saturating_add(programdata_account.data().len()),
173 slot,
174 environments.program_runtime_v1.clone(),
175 reload,
176 )
177 })
178 .map_err(|_| (slot, ProgramCacheEntryOwner::LoaderV3))
179 }
180
181 ProgramAccountLoadResult::ProgramOfLoaderV4(program_account, slot) => program_account
182 .data()
183 .get(LoaderV4State::program_data_offset()..)
184 .ok_or(Box::new(InstructionError::InvalidAccountData).into())
185 .and_then(|elf_bytes| {
186 load_program_from_bytes(
187 &mut load_program_metrics,
188 elf_bytes,
189 &loader_v4::id(),
190 program_account.data().len(),
191 slot,
192 environments.program_runtime_v1.clone(),
193 reload,
194 )
195 })
196 .map_err(|_| (slot, ProgramCacheEntryOwner::LoaderV4)),
197 }
198 .unwrap_or_else(|(slot, owner)| {
199 let env = environments.program_runtime_v1.clone();
200 ProgramCacheEntry::new_tombstone(
201 slot,
202 owner,
203 ProgramCacheEntryType::FailedVerification(env),
204 )
205 });
206
207 load_program_metrics.submit_datapoint(&mut execute_timings.details);
208 loaded_program.update_access_slot(slot);
209 Some(Arc::new(loaded_program))
210}
211
212pub(crate) fn get_program_modification_slot<CB: TransactionProcessingCallback>(
217 callbacks: &CB,
218 pubkey: &Pubkey,
219) -> TransactionResult<Slot> {
220 let program = callbacks
221 .get_account_shared_data(pubkey)
222 .ok_or(TransactionError::ProgramAccountNotFound)?;
223 if bpf_loader_upgradeable::check_id(program.owner()) {
224 if let Ok(UpgradeableLoaderState::Program {
225 programdata_address,
226 }) = program.state()
227 {
228 let programdata = callbacks
229 .get_account_shared_data(&programdata_address)
230 .ok_or(TransactionError::ProgramAccountNotFound)?;
231 if let Ok(UpgradeableLoaderState::ProgramData {
232 slot,
233 upgrade_authority_address: _,
234 }) = programdata.state()
235 {
236 return Ok(slot);
237 }
238 }
239 Err(TransactionError::ProgramAccountNotFound)
240 } else if loader_v4::check_id(program.owner()) {
241 let state = solana_loader_v4_program::get_state(program.data())
242 .map_err(|_| TransactionError::ProgramAccountNotFound)?;
243 Ok(state.slot)
244 } else {
245 Ok(0)
246 }
247}
248
249#[cfg(test)]
250mod tests {
251 use {
252 super::*,
253 crate::transaction_processor::TransactionBatchProcessor,
254 solana_account::WritableAccount,
255 solana_program_runtime::{
256 loaded_programs::{BlockRelation, ForkGraph, ProgramRuntimeEnvironments},
257 solana_sbpf::program::BuiltinProgram,
258 },
259 solana_sdk_ids::{bpf_loader, bpf_loader_upgradeable},
260 solana_svm_callback::InvokeContextCallback,
261 std::{
262 cell::RefCell,
263 collections::HashMap,
264 env,
265 fs::{self, File},
266 io::Read,
267 },
268 };
269
270 struct TestForkGraph {}
271
272 impl ForkGraph for TestForkGraph {
273 fn relationship(&self, _a: Slot, _b: Slot) -> BlockRelation {
274 BlockRelation::Unknown
275 }
276 }
277
278 #[derive(Default, Clone)]
279 pub(crate) struct MockBankCallback {
280 pub(crate) account_shared_data: RefCell<HashMap<Pubkey, AccountSharedData>>,
281 }
282
283 impl InvokeContextCallback for MockBankCallback {}
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}