1#[cfg(feature = "metrics")]
2use solana_program_runtime::program_metrics::LoadProgramMetrics;
3use {
4 solana_account::{AccountSharedData, ReadableAccount, state_traits::StateMut},
5 solana_clock::Slot,
6 solana_instruction::error::InstructionError,
7 solana_loader_v3_interface::state::UpgradeableLoaderState,
8 solana_loader_v4_interface::state::{LoaderV4State, LoaderV4Status},
9 solana_program_runtime::{
10 loaded_programs::{
11 ProgramCacheForTxBatch, ProgramCacheMatchCriteria, ProgramRuntimeEnvironment,
12 ProgramToLoad,
13 },
14 program_cache_entry::{
15 DELAY_VISIBILITY_SLOT_OFFSET, ProgramCacheEntry, ProgramCacheEntryOwner,
16 ProgramCacheEntryType,
17 },
18 },
19 solana_pubkey::Pubkey,
20 solana_sdk_ids::{bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, loader_v4},
21 solana_svm_callback::TransactionProcessingCallback,
22 solana_svm_timings::ExecuteTimings,
23 solana_svm_type_overrides::sync::Arc,
24 solana_transaction_error::{TransactionError, TransactionResult},
25 std::sync::atomic::Ordering,
26};
27
28#[derive(Debug)]
29pub(crate) enum ProgramAccountLoadResult {
30 InvalidAccountData(ProgramCacheEntryOwner),
31 ProgramOfLoaderV1(AccountSharedData),
32 ProgramOfLoaderV2(AccountSharedData),
33 ProgramOfLoaderV3(AccountSharedData, AccountSharedData, Slot),
34 ProgramOfLoaderV4(AccountSharedData, Slot),
35}
36
37pub(crate) fn load_program_accounts<CB: TransactionProcessingCallback>(
38 callbacks: &CB,
39 pubkey: &Pubkey,
40) -> Option<(ProgramAccountLoadResult, Slot)> {
41 let (program_account, last_modification_slot) = callbacks.get_account_shared_data(pubkey)?;
42
43 let load_result = if loader_v4::check_id(program_account.owner()) {
44 loader_v4_get_state(program_account.data())
45 .ok()
46 .and_then(|state| {
47 (!matches!(state.status, LoaderV4Status::Retracted)).then_some(state.slot)
48 })
49 .map(|slot| ProgramAccountLoadResult::ProgramOfLoaderV4(program_account, slot))
50 .unwrap_or(ProgramAccountLoadResult::InvalidAccountData(
51 ProgramCacheEntryOwner::LoaderV4,
52 ))
53 } else if bpf_loader_upgradeable::check_id(program_account.owner()) {
54 if let Ok(UpgradeableLoaderState::Program {
55 programdata_address,
56 }) = program_account.state()
57 {
58 if let Some((programdata_account, _slot)) =
59 callbacks.get_account_shared_data(&programdata_address)
60 {
61 if bpf_loader_upgradeable::check_id(programdata_account.owner()) {
62 if let Ok(UpgradeableLoaderState::ProgramData {
63 slot,
64 upgrade_authority_address: _,
65 }) = programdata_account.state()
66 {
67 ProgramAccountLoadResult::ProgramOfLoaderV3(
68 program_account,
69 programdata_account,
70 slot,
71 )
72 } else {
73 ProgramAccountLoadResult::InvalidAccountData(
74 ProgramCacheEntryOwner::LoaderV3,
75 )
76 }
77 } else {
78 ProgramAccountLoadResult::InvalidAccountData(ProgramCacheEntryOwner::LoaderV3)
79 }
80 } else {
81 ProgramAccountLoadResult::InvalidAccountData(ProgramCacheEntryOwner::LoaderV3)
82 }
83 } else {
84 ProgramAccountLoadResult::InvalidAccountData(ProgramCacheEntryOwner::LoaderV3)
85 }
86 } else if bpf_loader::check_id(program_account.owner()) {
87 ProgramAccountLoadResult::ProgramOfLoaderV2(program_account)
88 } else if bpf_loader_deprecated::check_id(program_account.owner()) {
89 ProgramAccountLoadResult::ProgramOfLoaderV1(program_account)
90 } else {
91 return None;
92 };
93
94 Some((load_result, last_modification_slot))
95}
96
97pub fn load_program_with_pubkey<CB: TransactionProcessingCallback>(
103 callbacks: &CB,
104 program_runtime_environment: &ProgramRuntimeEnvironment,
105 pubkey: &Pubkey,
106 current_slot: Slot,
107 execute_timings: &mut ExecuteTimings,
108) -> Option<(Arc<ProgramCacheEntry>, Slot)> {
109 #[cfg(feature = "metrics")]
110 let mut load_program_metrics = LoadProgramMetrics {
111 program_id: pubkey.to_string(),
112 ..LoadProgramMetrics::default()
113 };
114 #[cfg(not(feature = "metrics"))]
115 let _ = execute_timings;
116
117 let (load_result, last_modification_slot) = load_program_accounts(callbacks, pubkey)?;
118 let loaded_program = match load_result {
119 ProgramAccountLoadResult::InvalidAccountData(owner) => Ok(
120 ProgramCacheEntry::new_tombstone(current_slot, owner, ProgramCacheEntryType::Closed),
121 ),
122
123 ProgramAccountLoadResult::ProgramOfLoaderV1(program_account) => ProgramCacheEntry::new(
124 program_account.owner(),
125 ProgramRuntimeEnvironment::clone(program_runtime_environment),
126 0,
127 DELAY_VISIBILITY_SLOT_OFFSET,
128 program_account.data(),
129 program_account.data().len(),
130 #[cfg(feature = "metrics")]
131 &mut load_program_metrics,
132 )
133 .map_err(|_| (0, ProgramCacheEntryOwner::LoaderV1)),
134
135 ProgramAccountLoadResult::ProgramOfLoaderV2(program_account) => ProgramCacheEntry::new(
136 program_account.owner(),
137 ProgramRuntimeEnvironment::clone(program_runtime_environment),
138 0,
139 DELAY_VISIBILITY_SLOT_OFFSET,
140 program_account.data(),
141 program_account.data().len(),
142 #[cfg(feature = "metrics")]
143 &mut load_program_metrics,
144 )
145 .map_err(|_| (0, ProgramCacheEntryOwner::LoaderV2)),
146
147 ProgramAccountLoadResult::ProgramOfLoaderV3(
148 program_account,
149 programdata_account,
150 deployment_slot,
151 ) => programdata_account
152 .data()
153 .get(UpgradeableLoaderState::size_of_programdata_metadata()..)
154 .ok_or(())
155 .and_then(|programdata| {
156 ProgramCacheEntry::new(
157 program_account.owner(),
158 ProgramRuntimeEnvironment::clone(program_runtime_environment),
159 deployment_slot,
160 deployment_slot.saturating_add(DELAY_VISIBILITY_SLOT_OFFSET),
161 programdata,
162 program_account
163 .data()
164 .len()
165 .saturating_add(programdata_account.data().len()),
166 #[cfg(feature = "metrics")]
167 &mut load_program_metrics,
168 )
169 .map_err(|_| ())
170 })
171 .map_err(|_| (deployment_slot, ProgramCacheEntryOwner::LoaderV3)),
172
173 ProgramAccountLoadResult::ProgramOfLoaderV4(program_account, deployment_slot) => {
174 program_account
175 .data()
176 .get(LoaderV4State::program_data_offset()..)
177 .ok_or(())
178 .and_then(|elf_bytes| {
179 ProgramCacheEntry::new(
180 &loader_v4::id(),
181 ProgramRuntimeEnvironment::clone(program_runtime_environment),
182 deployment_slot,
183 deployment_slot.saturating_add(DELAY_VISIBILITY_SLOT_OFFSET),
184 elf_bytes,
185 program_account.data().len(),
186 #[cfg(feature = "metrics")]
187 &mut load_program_metrics,
188 )
189 .map_err(|_| ())
190 })
191 .map_err(|_| (deployment_slot, ProgramCacheEntryOwner::LoaderV4))
192 }
193 }
194 .unwrap_or_else(|(deployment_slot, owner)| {
195 let env = ProgramRuntimeEnvironment::clone(program_runtime_environment);
196 ProgramCacheEntry::new_tombstone(
197 deployment_slot,
198 owner,
199 ProgramCacheEntryType::FailedVerification(env),
200 )
201 });
202
203 #[cfg(feature = "metrics")]
204 load_program_metrics.submit_datapoint(&mut execute_timings.details);
205 loaded_program.update_access_slot(current_slot);
206 Some((Arc::new(loaded_program), last_modification_slot))
207}
208
209pub(crate) fn get_program_deployment_slot<CB: TransactionProcessingCallback>(
214 callbacks: &CB,
215 program: &AccountSharedData,
216 loader: ProgramCacheEntryOwner,
217) -> TransactionResult<Slot> {
218 match loader {
219 ProgramCacheEntryOwner::LoaderV1 | ProgramCacheEntryOwner::LoaderV2 => Ok(0),
220 ProgramCacheEntryOwner::LoaderV3 => {
221 if let Ok(UpgradeableLoaderState::Program {
222 programdata_address,
223 }) = program.state()
224 {
225 let (programdata, _slot) = callbacks
226 .get_account_shared_data(&programdata_address)
227 .ok_or(TransactionError::ProgramAccountNotFound)?;
228 if let Ok(UpgradeableLoaderState::ProgramData {
229 slot,
230 upgrade_authority_address: _,
231 }) = programdata.state()
232 {
233 return Ok(slot);
234 }
235 }
236 Err(TransactionError::ProgramAccountNotFound)
237 }
238 ProgramCacheEntryOwner::LoaderV4 => {
239 let state = loader_v4_get_state(program.data())
240 .map_err(|_| TransactionError::ProgramAccountNotFound)?;
241 Ok(state.slot)
242 }
243 ProgramCacheEntryOwner::NativeLoader => unreachable!(),
244 }
245}
246
247pub fn filter_executable_program_accounts<'a, CB: TransactionProcessingCallback>(
250 callbacks: &CB,
251 program_cache_for_tx_batch: &ProgramCacheForTxBatch,
252 keys: impl Iterator<Item = &'a Pubkey>,
253 check_program_deployment_slot: bool,
254) -> Vec<ProgramToLoad<'a>> {
255 let mut result = Vec::new();
256 for account_key in keys {
257 if let Some(cache_entry) = program_cache_for_tx_batch.find(account_key) {
258 cache_entry.stats.uses.fetch_add(1, Ordering::Relaxed);
259 } else if let Some((account, last_modification_slot)) =
260 callbacks.get_account_shared_data(account_key)
261 {
262 let loader = if loader_v4::check_id(account.owner()) {
263 ProgramCacheEntryOwner::LoaderV4
264 } else if bpf_loader_upgradeable::check_id(account.owner()) {
265 ProgramCacheEntryOwner::LoaderV3
266 } else if bpf_loader::check_id(account.owner()) {
267 ProgramCacheEntryOwner::LoaderV2
268 } else if bpf_loader_deprecated::check_id(account.owner()) {
269 ProgramCacheEntryOwner::LoaderV1
270 } else {
271 continue;
272 };
273 let match_criteria = if check_program_deployment_slot {
274 get_program_deployment_slot(callbacks, &account, loader)
275 .map_or(ProgramCacheMatchCriteria::Tombstone, |slot| {
276 ProgramCacheMatchCriteria::DeployedOnOrAfterSlot(slot)
277 })
278 } else {
279 ProgramCacheMatchCriteria::NoCriteria
280 };
281 result.push(ProgramToLoad {
282 program_id: account_key,
283 loader,
284 match_criteria,
285 last_modification_slot,
286 });
287 }
288 }
289 result
290}
291
292fn loader_v4_get_state(data: &[u8]) -> Result<&LoaderV4State, InstructionError> {
294 unsafe {
295 let data = data
296 .get(0..LoaderV4State::program_data_offset())
297 .ok_or(InstructionError::AccountDataTooSmall)?
298 .try_into()
299 .unwrap();
300 Ok(std::mem::transmute::<
301 &[u8; LoaderV4State::program_data_offset()],
302 &LoaderV4State,
303 >(data))
304 }
305}
306
307#[cfg(test)]
308mod tests {
309 use {
310 super::*,
311 crate::transaction_processor::TransactionBatchProcessor,
312 solana_account::WritableAccount,
313 solana_hash::Hash,
314 solana_keypair::Keypair,
315 solana_message::compiled_instruction::CompiledInstruction,
316 solana_program_runtime::{
317 loaded_programs::{
318 BlockRelation, ForkGraph, ProgramRuntimeEnvironment,
319 get_mock_program_runtime_environment,
320 },
321 solana_sbpf::program::BuiltinProgram,
322 },
323 solana_sdk_ids::{bpf_loader, bpf_loader_upgradeable, native_loader},
324 solana_svm_transaction::svm_message::SVMMessage,
325 solana_svm_type_overrides::sync::atomic::AtomicU64,
326 solana_transaction::{Transaction, sanitized::SanitizedTransaction},
327 std::{
328 cell::RefCell,
329 collections::HashMap,
330 env,
331 fs::{self, File},
332 io::Read,
333 },
334 };
335
336 struct TestForkGraph {}
337
338 impl ForkGraph for TestForkGraph {
339 fn relationship(&self, _a: Slot, _b: Slot) -> BlockRelation {
340 BlockRelation::Unknown
341 }
342 }
343
344 #[derive(Default, Clone)]
345 pub(crate) struct MockBankCallback {
346 pub(crate) account_shared_data: RefCell<HashMap<Pubkey, (AccountSharedData, Slot)>>,
347 }
348
349 impl TransactionProcessingCallback for MockBankCallback {
350 fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option<(AccountSharedData, Slot)> {
351 self.account_shared_data.borrow().get(pubkey).cloned()
352 }
353 }
354
355 #[test]
356 fn test_load_program_accounts_account_not_found() {
357 let mock_bank = MockBankCallback::default();
358 let key = Pubkey::new_unique();
359
360 let result = load_program_accounts(&mock_bank, &key);
361 assert!(result.is_none());
362
363 let mut account_data = AccountSharedData::default();
364 account_data.set_owner(bpf_loader_upgradeable::id());
365 let state = UpgradeableLoaderState::Program {
366 programdata_address: Pubkey::new_unique(),
367 };
368 account_data.set_data(bincode::serialize(&state).unwrap());
369 mock_bank
370 .account_shared_data
371 .borrow_mut()
372 .insert(key, (account_data.clone(), 0));
373
374 let result = load_program_accounts(&mock_bank, &key);
375 assert!(matches!(
376 result,
377 Some((ProgramAccountLoadResult::InvalidAccountData(_), _))
378 ));
379
380 account_data.set_data(Vec::new());
381 mock_bank
382 .account_shared_data
383 .borrow_mut()
384 .insert(key, (account_data, 0));
385
386 let result = load_program_accounts(&mock_bank, &key);
387
388 assert!(matches!(
389 result,
390 Some((ProgramAccountLoadResult::InvalidAccountData(_), _))
391 ));
392 }
393
394 #[test]
395 fn test_load_program_accounts_loader_v1_or_v2() {
396 let key = Pubkey::new_unique();
397 let mock_bank = MockBankCallback::default();
398 let mut account_data = AccountSharedData::default();
399 account_data.set_owner(bpf_loader::id());
400 mock_bank
401 .account_shared_data
402 .borrow_mut()
403 .insert(key, (account_data.clone(), 0));
404
405 let result = load_program_accounts(&mock_bank, &key);
406 match result {
407 Some((ProgramAccountLoadResult::ProgramOfLoaderV1(data), last_modification_slot))
408 | Some((ProgramAccountLoadResult::ProgramOfLoaderV2(data), last_modification_slot)) => {
409 assert_eq!(data, account_data);
410 assert_eq!(last_modification_slot, 0);
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(), 25));
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_owner(bpf_loader_upgradeable::id());
440 account_data2.set_data(bincode::serialize(&state).unwrap());
441 mock_bank
442 .account_shared_data
443 .borrow_mut()
444 .insert(key2, (account_data2.clone(), 25));
445
446 let result = load_program_accounts(&mock_bank, &key1);
447
448 match result {
449 Some((
450 ProgramAccountLoadResult::ProgramOfLoaderV3(data1, data2, deployment_slot),
451 last_modification_slot,
452 )) => {
453 assert_eq!(data1, account_data);
454 assert_eq!(data2, account_data2);
455 assert_eq!(deployment_slot, 25);
456 assert_eq!(last_modification_slot, 25);
457 }
458
459 _ => panic!("Invalid result"),
460 }
461 }
462
463 fn load_test_program() -> Vec<u8> {
464 let mut dir = env::current_dir().unwrap();
465 dir.push("tests");
466 dir.push("example-programs");
467 dir.push("hello-solana");
468 dir.push("hello_solana_program.so");
469 let mut file = File::open(dir.clone()).expect("file not found");
470 let metadata = fs::metadata(dir).expect("Unable to read metadata");
471 let mut buffer = vec![0; metadata.len() as usize];
472 file.read_exact(&mut buffer).expect("Buffer overflow");
473 buffer
474 }
475
476 #[test]
477 fn test_load_program_from_bytes() {
478 let buffer = load_test_program();
479
480 #[cfg(feature = "metrics")]
481 let mut metrics = LoadProgramMetrics::default();
482 let loader = bpf_loader_upgradeable::id();
483 let size = buffer.len();
484 let slot: Slot = 2;
485 let environment = ProgramRuntimeEnvironment::from(BuiltinProgram::new_mock());
486
487 let result = ProgramCacheEntry::new(
488 &loader,
489 ProgramRuntimeEnvironment::clone(&environment),
490 slot,
491 slot.saturating_add(DELAY_VISIBILITY_SLOT_OFFSET),
492 &buffer,
493 size,
494 #[cfg(feature = "metrics")]
495 &mut metrics,
496 );
497
498 assert!(result.is_ok());
499 }
500
501 #[test]
502 fn test_load_program_not_found() {
503 let mock_bank = MockBankCallback::default();
504 let key = Pubkey::new_unique();
505 let batch_processor = TransactionBatchProcessor::<TestForkGraph>::default();
506
507 let result = load_program_with_pubkey(
508 &mock_bank,
509 &batch_processor.program_runtime_environment_for_epoch(50),
510 &key,
511 500,
512 &mut ExecuteTimings::default(),
513 );
514 assert!(result.is_none());
515 }
516
517 #[test]
518 fn test_load_program_invalid_account_data() {
519 let key = Pubkey::new_unique();
520 let mock_bank = MockBankCallback::default();
521 let mut account_data = AccountSharedData::default();
522 account_data.set_owner(bpf_loader_upgradeable::id());
523 let batch_processor = TransactionBatchProcessor::<TestForkGraph>::default();
524 mock_bank
525 .account_shared_data
526 .borrow_mut()
527 .insert(key, (account_data.clone(), 0));
528
529 let result = load_program_with_pubkey(
530 &mock_bank,
531 &batch_processor.program_runtime_environment_for_epoch(20),
532 &key,
533 0, &mut ExecuteTimings::default(),
535 );
536
537 let loaded_program = ProgramCacheEntry::new_tombstone(
538 0, ProgramCacheEntryOwner::LoaderV3,
540 ProgramCacheEntryType::FailedVerification(
541 batch_processor.program_runtime_environment_for_epoch(20),
542 ),
543 );
544 assert_eq!(result.unwrap(), (Arc::new(loaded_program), 0));
545 }
546
547 #[test]
548 fn test_load_program_program_loader_v1_or_v2() {
549 let key = Pubkey::new_unique();
550 let mock_bank = MockBankCallback::default();
551 let mut account_data = AccountSharedData::default();
552 account_data.set_owner(bpf_loader::id());
553 let batch_processor = TransactionBatchProcessor::<TestForkGraph>::default();
554 mock_bank
555 .account_shared_data
556 .borrow_mut()
557 .insert(key, (account_data.clone(), 0));
558
559 let result = load_program_with_pubkey(
561 &mock_bank,
562 &batch_processor.program_runtime_environment_for_epoch(20),
563 &key,
564 200,
565 &mut ExecuteTimings::default(),
566 );
567 let loaded_program = ProgramCacheEntry::new_tombstone(
568 0,
569 ProgramCacheEntryOwner::LoaderV2,
570 ProgramCacheEntryType::FailedVerification(
571 batch_processor.program_runtime_environment_for_epoch(20),
572 ),
573 );
574 assert_eq!(result.unwrap(), (Arc::new(loaded_program), 0));
575
576 let buffer = load_test_program();
577 account_data.set_data(buffer);
578
579 mock_bank
580 .account_shared_data
581 .borrow_mut()
582 .insert(key, (account_data.clone(), 0));
583
584 let result = load_program_with_pubkey(
585 &mock_bank,
586 &batch_processor.program_runtime_environment_for_epoch(20),
587 &key,
588 200,
589 &mut ExecuteTimings::default(),
590 );
591
592 let program_runtime_environment = get_mock_program_runtime_environment();
593 let expected = ProgramCacheEntry::new(
594 account_data.owner(),
595 ProgramRuntimeEnvironment::clone(&program_runtime_environment),
596 0,
597 DELAY_VISIBILITY_SLOT_OFFSET,
598 account_data.data(),
599 account_data.data().len(),
600 #[cfg(feature = "metrics")]
601 &mut LoadProgramMetrics::default(),
602 );
603
604 assert_eq!(result.unwrap(), (Arc::new(expected.unwrap()), 0));
605 }
606
607 #[test]
608 fn test_load_program_program_loader_v3() {
609 let key1 = Pubkey::new_unique();
610 let key2 = Pubkey::new_unique();
611 let mock_bank = MockBankCallback::default();
612 let batch_processor = TransactionBatchProcessor::<TestForkGraph>::default();
613
614 let mut account_data = AccountSharedData::default();
615 account_data.set_owner(bpf_loader_upgradeable::id());
616
617 let state = UpgradeableLoaderState::Program {
618 programdata_address: key2,
619 };
620 account_data.set_data(bincode::serialize(&state).unwrap());
621 mock_bank
622 .account_shared_data
623 .borrow_mut()
624 .insert(key1, (account_data.clone(), 0));
625
626 let state = UpgradeableLoaderState::ProgramData {
627 slot: 0,
628 upgrade_authority_address: None,
629 };
630 let mut account_data2 = AccountSharedData::default();
631 account_data2.set_data(bincode::serialize(&state).unwrap());
632 mock_bank
633 .account_shared_data
634 .borrow_mut()
635 .insert(key2, (account_data2.clone(), 0));
636
637 let result = load_program_with_pubkey(
639 &mock_bank,
640 &batch_processor.program_runtime_environment_for_epoch(0),
641 &key1,
642 0,
643 &mut ExecuteTimings::default(),
644 );
645 let loaded_program = ProgramCacheEntry::new_tombstone(
646 0,
647 ProgramCacheEntryOwner::LoaderV3,
648 ProgramCacheEntryType::FailedVerification(
649 batch_processor.program_runtime_environment_for_epoch(0),
650 ),
651 );
652 assert_eq!(result.unwrap(), (Arc::new(loaded_program), 0));
653
654 let mut buffer = load_test_program();
655 let mut header = bincode::serialize(&state).unwrap();
656 let mut complement = vec![
657 0;
658 std::cmp::max(
659 0,
660 UpgradeableLoaderState::size_of_programdata_metadata() - header.len()
661 )
662 ];
663 header.append(&mut complement);
664 header.append(&mut buffer);
665 account_data.set_data(header);
666
667 mock_bank
668 .account_shared_data
669 .borrow_mut()
670 .insert(key2, (account_data.clone(), 0));
671
672 let result = load_program_with_pubkey(
673 &mock_bank,
674 &batch_processor.program_runtime_environment_for_epoch(20),
675 &key1,
676 200,
677 &mut ExecuteTimings::default(),
678 );
679
680 let data = account_data.data();
681 account_data
682 .set_data(data[UpgradeableLoaderState::size_of_programdata_metadata()..].to_vec());
683
684 let program_runtime_environment = get_mock_program_runtime_environment();
685 let expected = ProgramCacheEntry::new(
686 account_data.owner(),
687 ProgramRuntimeEnvironment::clone(&program_runtime_environment),
688 0,
689 DELAY_VISIBILITY_SLOT_OFFSET,
690 account_data.data(),
691 account_data.data().len(),
692 #[cfg(feature = "metrics")]
693 &mut LoadProgramMetrics::default(),
694 );
695 assert_eq!(result.unwrap(), (Arc::new(expected.unwrap()), 0));
696 }
697
698 #[test]
699 fn test_load_program_environment() {
700 let key = Pubkey::new_unique();
701 let mock_bank = MockBankCallback::default();
702 let mut account_data = AccountSharedData::default();
703 account_data.set_owner(bpf_loader::id());
704 let batch_processor = TransactionBatchProcessor::<TestForkGraph>::default();
705 let upcoming_environment = get_mock_program_runtime_environment();
706 let current_environment =
707 ProgramRuntimeEnvironment::clone(&batch_processor.program_runtime_environment);
708 {
709 let mut epoch_boundary_preparation =
710 batch_processor.epoch_boundary_preparation.write().unwrap();
711 epoch_boundary_preparation.upcoming_epoch = 1;
712 epoch_boundary_preparation.upcoming_environment = Some(upcoming_environment.clone());
713 }
714 mock_bank
715 .account_shared_data
716 .borrow_mut()
717 .insert(key, (account_data.clone(), 0));
718
719 for is_upcoming_env in [false, true] {
720 let (result, _last_modification_slot) = load_program_with_pubkey(
721 &mock_bank,
722 &batch_processor.program_runtime_environment_for_epoch(is_upcoming_env as u64),
723 &key,
724 200,
725 &mut ExecuteTimings::default(),
726 )
727 .unwrap();
728 assert_ne!(
729 is_upcoming_env,
730 result.program.get_environment().unwrap() == ¤t_environment,
731 );
732 assert_eq!(
733 is_upcoming_env,
734 result.program.get_environment().unwrap() == &upcoming_environment,
735 );
736 }
737 }
738
739 #[test]
740 fn test_program_modification_slot_account_not_found() {
741 let mock_bank = MockBankCallback::default();
742 let key = Pubkey::new_unique();
743
744 let mut account_data = AccountSharedData::new(100, 100, &bpf_loader_upgradeable::id());
745 mock_bank
746 .account_shared_data
747 .borrow_mut()
748 .insert(key, (account_data.clone(), 0));
749
750 let result = get_program_deployment_slot(
751 &mock_bank,
752 &mock_bank.get_account_shared_data(&key).unwrap().0,
753 ProgramCacheEntryOwner::LoaderV3,
754 );
755 assert_eq!(result.err(), Some(TransactionError::ProgramAccountNotFound));
756
757 let state = UpgradeableLoaderState::Program {
758 programdata_address: Pubkey::new_unique(),
759 };
760 account_data.set_data(bincode::serialize(&state).unwrap());
761 mock_bank
762 .account_shared_data
763 .borrow_mut()
764 .insert(key, (account_data.clone(), 0));
765
766 let result = get_program_deployment_slot(
767 &mock_bank,
768 &mock_bank.get_account_shared_data(&key).unwrap().0,
769 ProgramCacheEntryOwner::LoaderV3,
770 );
771 assert_eq!(result.err(), Some(TransactionError::ProgramAccountNotFound));
772 }
773
774 #[test]
775 fn test_program_deployment_slot_success() {
776 let mock_bank = MockBankCallback::default();
777
778 let key1 = Pubkey::new_unique();
779 let key2 = Pubkey::new_unique();
780
781 let account_data = AccountSharedData::new_data(
782 100,
783 &UpgradeableLoaderState::Program {
784 programdata_address: key2,
785 },
786 &bpf_loader_upgradeable::id(),
787 )
788 .unwrap();
789 mock_bank
790 .account_shared_data
791 .borrow_mut()
792 .insert(key1, (account_data, 0));
793
794 let account_data = AccountSharedData::new_data(
795 100,
796 &UpgradeableLoaderState::ProgramData {
797 slot: 77,
798 upgrade_authority_address: None,
799 },
800 &bpf_loader_upgradeable::id(),
801 )
802 .unwrap();
803 mock_bank
804 .account_shared_data
805 .borrow_mut()
806 .insert(key2, (account_data.clone(), 0));
807
808 let result = get_program_deployment_slot(
809 &mock_bank,
810 &mock_bank.get_account_shared_data(&key1).unwrap().0,
811 ProgramCacheEntryOwner::LoaderV3,
812 );
813 assert_eq!(result.unwrap(), 77);
814 }
815
816 #[test]
817 fn test_filter_executable_program_accounts() {
818 let feepayer = Keypair::new();
819 let loader_ids = [
820 bpf_loader_deprecated::id(),
821 bpf_loader::id(),
822 bpf_loader_upgradeable::id(),
823 native_loader::id(),
824 ];
825 let program_ids = [
826 Pubkey::new_unique(),
827 Pubkey::new_unique(),
828 Pubkey::new_unique(),
829 Pubkey::new_unique(),
830 ];
831 let account_ids = [
832 Pubkey::new_unique(),
833 Pubkey::new_unique(),
834 Pubkey::new_unique(),
835 Pubkey::new_unique(),
836 ];
837
838 let mut loaded_programs_for_tx_batch = ProgramCacheForTxBatch::default();
839 let mock_bank = MockBankCallback::default();
840 for i in 0..3 {
841 loaded_programs_for_tx_batch.replenish(
842 loader_ids[i],
843 Arc::new(ProgramCacheEntry {
844 program: ProgramCacheEntryType::Builtin(BuiltinProgram::new_mock()),
845 account_owner: ProgramCacheEntryOwner::NativeLoader,
846 account_size: 0,
847 deployment_slot: 0,
848 effective_slot: 0,
849 stats: Arc::default(),
850 latest_access_slot: AtomicU64::default(),
851 }),
852 );
853 mock_bank.account_shared_data.borrow_mut().insert(
854 loader_ids[i],
855 (AccountSharedData::new(1, 1, &program_ids[3]), 0),
856 );
857 mock_bank.account_shared_data.borrow_mut().insert(
858 program_ids[i],
859 (AccountSharedData::new(1, 1, &loader_ids[i]), 0),
860 );
861 mock_bank.account_shared_data.borrow_mut().insert(
862 account_ids[i],
863 (AccountSharedData::new(1, 1, &program_ids[i]), 0),
864 );
865 }
866
867 let tx = Transaction::new_with_compiled_instructions(
868 &[&feepayer],
869 &[program_ids[1], program_ids[2], loader_ids[2]],
870 Hash::new_unique(),
871 vec![
872 account_ids[0],
873 account_ids[1],
874 account_ids[2],
875 account_ids[3],
876 ],
877 vec![
878 CompiledInstruction::new(1, &(), vec![0, 1, 2, 3]),
879 CompiledInstruction::new(2, &(), vec![0, 1, 2, 3]),
880 CompiledInstruction::new(3, &(), vec![0, 1, 2, 3]),
881 ],
882 );
883 let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(tx);
884
885 let missing_programs = filter_executable_program_accounts(
886 &mock_bank,
887 &loaded_programs_for_tx_batch,
888 sanitized_tx.account_keys().iter(),
889 false,
890 );
891 assert_eq!(
892 missing_programs,
893 &[
894 ProgramToLoad {
895 program_id: &program_ids[1],
896 loader: ProgramCacheEntryOwner::LoaderV2,
897 match_criteria: ProgramCacheMatchCriteria::NoCriteria,
898 last_modification_slot: 0,
899 },
900 ProgramToLoad {
901 program_id: &program_ids[2],
902 loader: ProgramCacheEntryOwner::LoaderV3,
903 match_criteria: ProgramCacheMatchCriteria::NoCriteria,
904 last_modification_slot: 0,
905 },
906 ]
907 );
908
909 let missing_programs = filter_executable_program_accounts(
910 &mock_bank,
911 &loaded_programs_for_tx_batch,
912 sanitized_tx.account_keys().iter(),
913 true,
914 );
915 assert_eq!(
916 missing_programs,
917 &[
918 ProgramToLoad {
919 program_id: &program_ids[1],
920 loader: ProgramCacheEntryOwner::LoaderV2,
921 match_criteria: ProgramCacheMatchCriteria::DeployedOnOrAfterSlot(0),
922 last_modification_slot: 0,
923 },
924 ProgramToLoad {
925 program_id: &program_ids[2],
926 loader: ProgramCacheEntryOwner::LoaderV3,
927 match_criteria: ProgramCacheMatchCriteria::Tombstone,
928 last_modification_slot: 0,
929 },
930 ]
931 );
932 }
933}