1#[cfg(feature = "dev-context-only-utils")]
2use qualifier_attr::{field_qualifiers, qualifiers};
3use {
4 crate::{
5 account_overrides::AccountOverrides,
6 nonce_info::NonceInfo,
7 rent_calculator::{
8 check_rent_state_with_account, get_account_rent_state, RENT_EXEMPT_RENT_EPOCH,
9 },
10 rollback_accounts::RollbackAccounts,
11 transaction_error_metrics::TransactionErrorMetrics,
12 transaction_execution_result::ExecutedTransaction,
13 },
14 ahash::{AHashMap, AHashSet},
15 solana_account::{
16 state_traits::StateMut, Account, AccountSharedData, ReadableAccount, WritableAccount,
17 PROGRAM_OWNERS,
18 },
19 solana_clock::Slot,
20 solana_fee_structure::FeeDetails,
21 solana_instruction::{BorrowedAccountMeta, BorrowedInstruction},
22 solana_instructions_sysvar::construct_instructions_data,
23 solana_loader_v3_interface::state::UpgradeableLoaderState,
24 solana_nonce::state::State as NonceState,
25 solana_nonce_account::{get_system_account_kind, SystemAccountKind},
26 solana_program_runtime::execution_budget::{
27 SVMTransactionExecutionAndFeeBudgetLimits, SVMTransactionExecutionBudget,
28 },
29 solana_pubkey::Pubkey,
30 solana_rent::Rent,
31 solana_sdk_ids::{
32 bpf_loader_upgradeable, native_loader,
33 sysvar::{self, slot_history},
34 },
35 solana_svm_callback::{AccountState, TransactionProcessingCallback},
36 solana_svm_feature_set::SVMFeatureSet,
37 solana_svm_transaction::svm_message::SVMMessage,
38 solana_transaction_context::{transaction_accounts::KeyedAccountSharedData, IndexOfAccount},
39 solana_transaction_error::{TransactionError, TransactionResult as Result},
40 std::num::{NonZeroU32, Saturating},
41};
42
43#[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
46pub(crate) const TRANSACTION_ACCOUNT_BASE_SIZE: usize = 64;
47
48const ADDRESS_LOOKUP_TABLE_BASE_SIZE: usize = 8248;
51
52pub type TransactionCheckResult = Result<CheckedTransactionDetails>;
54type TransactionValidationResult = Result<ValidatedTransactionDetails>;
55
56#[derive(PartialEq, Eq, Debug)]
57pub(crate) enum TransactionLoadResult {
58 Loaded(LoadedTransaction),
60 FeesOnly(FeesOnlyTransaction),
64 NotLoaded(TransactionError),
67}
68
69#[derive(PartialEq, Eq, Debug, Clone)]
70#[cfg_attr(feature = "svm-internal", field_qualifiers(nonce(pub)))]
71pub struct CheckedTransactionDetails {
72 pub(crate) nonce: Option<NonceInfo>,
73 pub(crate) compute_budget_and_limits: Result<SVMTransactionExecutionAndFeeBudgetLimits>,
74}
75
76#[cfg(feature = "dev-context-only-utils")]
77impl Default for CheckedTransactionDetails {
78 fn default() -> Self {
79 Self {
80 nonce: None,
81 compute_budget_and_limits: Ok(SVMTransactionExecutionAndFeeBudgetLimits {
82 budget: SVMTransactionExecutionBudget::default(),
83 loaded_accounts_data_size_limit: NonZeroU32::new(32)
84 .expect("Failed to set loaded_accounts_bytes"),
85 fee_details: FeeDetails::default(),
86 }),
87 }
88 }
89}
90
91impl CheckedTransactionDetails {
92 pub fn new(
93 nonce: Option<NonceInfo>,
94 compute_budget_and_limits: Result<SVMTransactionExecutionAndFeeBudgetLimits>,
95 ) -> Self {
96 Self {
97 nonce,
98 compute_budget_and_limits,
99 }
100 }
101}
102
103#[derive(PartialEq, Eq, Debug, Clone)]
104pub(crate) struct ValidatedTransactionDetails {
105 pub(crate) rollback_accounts: RollbackAccounts,
106 pub(crate) compute_budget: SVMTransactionExecutionBudget,
107 pub(crate) loaded_accounts_bytes_limit: NonZeroU32,
108 pub(crate) fee_details: FeeDetails,
109 pub(crate) loaded_fee_payer_account: LoadedTransactionAccount,
110}
111
112#[cfg(feature = "dev-context-only-utils")]
113impl Default for ValidatedTransactionDetails {
114 fn default() -> Self {
115 Self {
116 rollback_accounts: RollbackAccounts::default(),
117 compute_budget: SVMTransactionExecutionBudget::default(),
118 loaded_accounts_bytes_limit:
119 solana_program_runtime::execution_budget::MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES,
120 fee_details: FeeDetails::default(),
121 loaded_fee_payer_account: LoadedTransactionAccount::default(),
122 }
123 }
124}
125
126#[derive(PartialEq, Eq, Debug, Clone)]
127#[cfg_attr(feature = "dev-context-only-utils", derive(Default))]
128pub(crate) struct LoadedTransactionAccount {
129 pub(crate) account: AccountSharedData,
130 pub(crate) loaded_size: usize,
131}
132
133#[derive(PartialEq, Eq, Debug, Clone)]
134#[cfg_attr(feature = "dev-context-only-utils", derive(Default))]
135#[cfg_attr(
136 feature = "dev-context-only-utils",
137 field_qualifiers(program_indices(pub), compute_budget(pub))
138)]
139pub struct LoadedTransaction {
140 pub accounts: Vec<KeyedAccountSharedData>,
141 pub(crate) program_indices: Vec<IndexOfAccount>,
142 pub fee_details: FeeDetails,
143 pub rollback_accounts: RollbackAccounts,
144 pub(crate) compute_budget: SVMTransactionExecutionBudget,
145 pub loaded_accounts_data_size: u32,
146}
147
148#[derive(PartialEq, Eq, Debug, Clone)]
149pub struct FeesOnlyTransaction {
150 pub load_error: TransactionError,
151 pub rollback_accounts: RollbackAccounts,
152 pub fee_details: FeeDetails,
153}
154
155#[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
162pub(crate) struct AccountLoader<'a, CB: TransactionProcessingCallback> {
163 loaded_accounts: AHashMap<Pubkey, AccountSharedData>,
164 callbacks: &'a CB,
165 pub(crate) feature_set: &'a SVMFeatureSet,
166}
167
168impl<'a, CB: TransactionProcessingCallback> AccountLoader<'a, CB> {
169 #[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
171 pub(crate) fn new_with_loaded_accounts_capacity(
172 account_overrides: Option<&'a AccountOverrides>,
173 callbacks: &'a CB,
174 feature_set: &'a SVMFeatureSet,
175 capacity: usize,
176 ) -> AccountLoader<'a, CB> {
177 let mut loaded_accounts = AHashMap::with_capacity(capacity);
178
179 if let Some(slot_history) =
182 account_overrides.and_then(|overrides| overrides.get(&slot_history::id()))
183 {
184 loaded_accounts.insert(slot_history::id(), slot_history.clone());
185 }
186
187 Self {
188 loaded_accounts,
189 callbacks,
190 feature_set,
191 }
192 }
193
194 pub(crate) fn load_transaction_account(
200 &mut self,
201 account_key: &Pubkey,
202 is_writable: bool,
203 ) -> Option<LoadedTransactionAccount> {
204 let base_account_size = if self.feature_set.formalize_loaded_transaction_data_size {
205 TRANSACTION_ACCOUNT_BASE_SIZE
206 } else {
207 0
208 };
209
210 let account = self.load_account(account_key);
211
212 self.callbacks.inspect_account(
220 account_key,
221 if let Some(ref account) = account {
222 AccountState::Alive(account)
223 } else {
224 AccountState::Dead
225 },
226 is_writable,
227 );
228
229 account.map(|account| LoadedTransactionAccount {
230 loaded_size: base_account_size.saturating_add(account.data().len()),
231 account,
232 })
233 }
234
235 pub(crate) fn load_account(&mut self, account_key: &Pubkey) -> Option<AccountSharedData> {
238 match self.do_load(account_key) {
239 (Some(account), false) => Some(account),
241 (None, false) => None,
243 (Some(account), true) => {
245 self.loaded_accounts.insert(*account_key, account.clone());
246 Some(account)
247 }
248 (None, true) => {
250 self.loaded_accounts
251 .insert(*account_key, AccountSharedData::default());
252 None
253 }
254 }
255 }
256
257 fn do_load(&self, account_key: &Pubkey) -> (Option<AccountSharedData>, bool) {
261 if let Some(account) = self.loaded_accounts.get(account_key) {
262 let option_account = if account.lamports() == 0 {
266 None
267 } else {
268 Some(account.clone())
269 };
270
271 (option_account, false)
272 } else if let Some((account, _slot)) = self.callbacks.get_account_shared_data(account_key) {
273 (Some(account), true)
274 } else {
275 (None, true)
276 }
277 }
278
279 pub(crate) fn update_accounts_for_executed_tx(
280 &mut self,
281 message: &impl SVMMessage,
282 executed_transaction: &ExecutedTransaction,
283 ) {
284 if executed_transaction.was_successful() {
285 self.update_accounts_for_successful_tx(
286 message,
287 &executed_transaction.loaded_transaction.accounts,
288 );
289 } else {
290 self.update_accounts_for_failed_tx(
291 &executed_transaction.loaded_transaction.rollback_accounts,
292 );
293 }
294 }
295
296 pub(crate) fn update_accounts_for_failed_tx(&mut self, rollback_accounts: &RollbackAccounts) {
297 for (account_address, account) in rollback_accounts {
298 self.loaded_accounts
299 .insert(*account_address, account.clone());
300 }
301 }
302
303 fn update_accounts_for_successful_tx(
304 &mut self,
305 message: &impl SVMMessage,
306 transaction_accounts: &[KeyedAccountSharedData],
307 ) {
308 for (i, (address, account)) in (0..message.account_keys().len()).zip(transaction_accounts) {
309 if !message.is_writable(i) {
310 continue;
311 }
312
313 if message.is_invoked(i) && !message.is_instruction_account(i) {
318 continue;
319 }
320
321 self.loaded_accounts.insert(*address, account.clone());
322 }
323 }
324}
325
326impl<CB: TransactionProcessingCallback> TransactionProcessingCallback for AccountLoader<'_, CB> {
332 fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option<(AccountSharedData, Slot)> {
333 self.do_load(pubkey).0.map(|account| (account, 0))
336 }
337}
338
339impl<CB: TransactionProcessingCallback> solana_svm_callback::InvokeContextCallback
343 for AccountLoader<'_, CB>
344{
345}
346
347pub fn update_rent_exempt_status_for_account(rent: &Rent, account: &mut AccountSharedData) {
353 if account.rent_epoch() != RENT_EXEMPT_RENT_EPOCH
359 && rent.is_exempt(account.lamports(), account.data().len())
360 {
361 account.set_rent_epoch(RENT_EXEMPT_RENT_EPOCH);
362 }
363}
364
365pub fn validate_fee_payer(
371 payer_address: &Pubkey,
372 payer_account: &mut AccountSharedData,
373 payer_index: IndexOfAccount,
374 error_metrics: &mut TransactionErrorMetrics,
375 rent: &Rent,
376 fee: u64,
377) -> Result<()> {
378 if payer_account.lamports() == 0 {
379 error_metrics.account_not_found += 1;
380 return Err(TransactionError::AccountNotFound);
381 }
382 let system_account_kind = get_system_account_kind(payer_account).ok_or_else(|| {
383 error_metrics.invalid_account_for_fee += 1;
384 TransactionError::InvalidAccountForFee
385 })?;
386 let min_balance = match system_account_kind {
387 SystemAccountKind::System => 0,
388 SystemAccountKind::Nonce => {
389 rent.minimum_balance(NonceState::size())
392 }
393 };
394
395 payer_account
396 .lamports()
397 .checked_sub(min_balance)
398 .and_then(|v| v.checked_sub(fee))
399 .ok_or_else(|| {
400 error_metrics.insufficient_funds += 1;
401 TransactionError::InsufficientFundsForFee
402 })?;
403
404 let payer_pre_rent_state =
405 get_account_rent_state(rent, payer_account.lamports(), payer_account.data().len());
406 payer_account
407 .checked_sub_lamports(fee)
408 .map_err(|_| TransactionError::InsufficientFundsForFee)?;
409
410 let payer_post_rent_state =
411 get_account_rent_state(rent, payer_account.lamports(), payer_account.data().len());
412 check_rent_state_with_account(
413 &payer_pre_rent_state,
414 &payer_post_rent_state,
415 payer_address,
416 payer_index,
417 )
418}
419
420pub(crate) fn load_transaction<CB: TransactionProcessingCallback>(
421 account_loader: &mut AccountLoader<CB>,
422 message: &impl SVMMessage,
423 validation_result: TransactionValidationResult,
424 error_metrics: &mut TransactionErrorMetrics,
425 rent: &Rent,
426) -> TransactionLoadResult {
427 match validation_result {
428 Err(e) => TransactionLoadResult::NotLoaded(e),
429 Ok(tx_details) => {
430 let load_result = load_transaction_accounts(
431 account_loader,
432 message,
433 tx_details.loaded_fee_payer_account,
434 tx_details.loaded_accounts_bytes_limit,
435 error_metrics,
436 rent,
437 );
438
439 match load_result {
440 Ok(loaded_tx_accounts) => TransactionLoadResult::Loaded(LoadedTransaction {
441 accounts: loaded_tx_accounts.accounts,
442 program_indices: loaded_tx_accounts.program_indices,
443 fee_details: tx_details.fee_details,
444 rollback_accounts: tx_details.rollback_accounts,
445 compute_budget: tx_details.compute_budget,
446 loaded_accounts_data_size: loaded_tx_accounts.loaded_accounts_data_size,
447 }),
448 Err(err) => TransactionLoadResult::FeesOnly(FeesOnlyTransaction {
449 load_error: err,
450 fee_details: tx_details.fee_details,
451 rollback_accounts: tx_details.rollback_accounts,
452 }),
453 }
454 }
455 }
456}
457
458#[derive(PartialEq, Eq, Debug, Clone)]
459struct LoadedTransactionAccounts {
460 pub(crate) accounts: Vec<KeyedAccountSharedData>,
461 pub(crate) program_indices: Vec<IndexOfAccount>,
462 pub(crate) loaded_accounts_data_size: u32,
463}
464
465impl LoadedTransactionAccounts {
466 fn increase_calculated_data_size(
467 &mut self,
468 data_size_delta: usize,
469 requested_loaded_accounts_data_size_limit: NonZeroU32,
470 error_metrics: &mut TransactionErrorMetrics,
471 ) -> Result<()> {
472 let Ok(data_size_delta) = u32::try_from(data_size_delta) else {
473 error_metrics.max_loaded_accounts_data_size_exceeded += 1;
474 return Err(TransactionError::MaxLoadedAccountsDataSizeExceeded);
475 };
476
477 self.loaded_accounts_data_size = self
478 .loaded_accounts_data_size
479 .saturating_add(data_size_delta);
480
481 if self.loaded_accounts_data_size > requested_loaded_accounts_data_size_limit.get() {
482 error_metrics.max_loaded_accounts_data_size_exceeded += 1;
483 Err(TransactionError::MaxLoadedAccountsDataSizeExceeded)
484 } else {
485 Ok(())
486 }
487 }
488}
489
490fn load_transaction_accounts<CB: TransactionProcessingCallback>(
491 account_loader: &mut AccountLoader<CB>,
492 message: &impl SVMMessage,
493 loaded_fee_payer_account: LoadedTransactionAccount,
494 loaded_accounts_bytes_limit: NonZeroU32,
495 error_metrics: &mut TransactionErrorMetrics,
496 rent: &Rent,
497) -> Result<LoadedTransactionAccounts> {
498 if account_loader
499 .feature_set
500 .formalize_loaded_transaction_data_size
501 {
502 load_transaction_accounts_simd186(
503 account_loader,
504 message,
505 loaded_fee_payer_account,
506 loaded_accounts_bytes_limit,
507 error_metrics,
508 rent,
509 )
510 } else {
511 load_transaction_accounts_old(
512 account_loader,
513 message,
514 loaded_fee_payer_account,
515 loaded_accounts_bytes_limit,
516 error_metrics,
517 rent,
518 )
519 }
520}
521
522fn load_transaction_accounts_simd186<CB: TransactionProcessingCallback>(
523 account_loader: &mut AccountLoader<CB>,
524 message: &impl SVMMessage,
525 loaded_fee_payer_account: LoadedTransactionAccount,
526 loaded_accounts_bytes_limit: NonZeroU32,
527 error_metrics: &mut TransactionErrorMetrics,
528 rent: &Rent,
529) -> Result<LoadedTransactionAccounts> {
530 let account_keys = message.account_keys();
531 let mut additional_loaded_accounts: AHashSet<Pubkey> = AHashSet::new();
532
533 let mut loaded_transaction_accounts = LoadedTransactionAccounts {
534 accounts: Vec::with_capacity(account_keys.len()),
535 program_indices: Vec::with_capacity(message.num_instructions()),
536 loaded_accounts_data_size: 0,
537 };
538
539 loaded_transaction_accounts.increase_calculated_data_size(
541 message
542 .num_lookup_tables()
543 .saturating_mul(ADDRESS_LOOKUP_TABLE_BASE_SIZE),
544 loaded_accounts_bytes_limit,
545 error_metrics,
546 )?;
547
548 let mut collect_loaded_account =
549 |account_loader: &mut AccountLoader<CB>, key: &Pubkey, loaded_account| -> Result<()> {
550 let LoadedTransactionAccount {
551 account,
552 loaded_size,
553 } = loaded_account;
554
555 loaded_transaction_accounts.increase_calculated_data_size(
556 loaded_size,
557 loaded_accounts_bytes_limit,
558 error_metrics,
559 )?;
560
561 if bpf_loader_upgradeable::check_id(account.owner()) {
573 if let Ok(UpgradeableLoaderState::Program {
574 programdata_address,
575 }) = account.state()
576 {
577 if !account_keys.iter().any(|key| programdata_address == *key)
579 && !additional_loaded_accounts.contains(&programdata_address)
580 {
581 if let Some(programdata_account) =
583 account_loader.load_account(&programdata_address)
584 {
585 loaded_transaction_accounts.increase_calculated_data_size(
587 TRANSACTION_ACCOUNT_BASE_SIZE
588 .saturating_add(programdata_account.data().len()),
589 loaded_accounts_bytes_limit,
590 error_metrics,
591 )?;
592 additional_loaded_accounts.insert(programdata_address);
593 }
594 }
595 }
596 }
597
598 loaded_transaction_accounts.accounts.push((*key, account));
599
600 Ok(())
601 };
602
603 collect_loaded_account(
606 account_loader,
607 message.fee_payer(),
608 loaded_fee_payer_account,
609 )?;
610
611 for (account_index, account_key) in account_keys.iter().enumerate().skip(1) {
613 let loaded_account =
614 load_transaction_account(account_loader, message, account_key, account_index, rent);
615 collect_loaded_account(account_loader, account_key, loaded_account)?;
616 }
617
618 for (program_id, instruction) in message.program_instructions_iter() {
619 let Some(program_account) = account_loader.load_account(program_id) else {
620 error_metrics.account_not_found += 1;
621 return Err(TransactionError::ProgramAccountNotFound);
622 };
623
624 let owner_id = program_account.owner();
625 if !native_loader::check_id(owner_id) && !PROGRAM_OWNERS.contains(owner_id) {
626 error_metrics.invalid_program_for_execution += 1;
627 return Err(TransactionError::InvalidProgramForExecution);
628 }
629
630 loaded_transaction_accounts
631 .program_indices
632 .push(instruction.program_id_index as IndexOfAccount);
633 }
634
635 Ok(loaded_transaction_accounts)
636}
637
638fn load_transaction_accounts_old<CB: TransactionProcessingCallback>(
639 account_loader: &mut AccountLoader<CB>,
640 message: &impl SVMMessage,
641 loaded_fee_payer_account: LoadedTransactionAccount,
642 loaded_accounts_bytes_limit: NonZeroU32,
643 error_metrics: &mut TransactionErrorMetrics,
644 rent: &Rent,
645) -> Result<LoadedTransactionAccounts> {
646 let account_keys = message.account_keys();
647 let mut accounts = Vec::with_capacity(account_keys.len());
648 let mut validated_loaders = AHashSet::with_capacity(PROGRAM_OWNERS.len());
649 let mut accumulated_accounts_data_size: Saturating<u32> = Saturating(0);
650
651 let mut collect_loaded_account = |key: &Pubkey, loaded_account| -> Result<()> {
652 let LoadedTransactionAccount {
653 account,
654 loaded_size,
655 } = loaded_account;
656
657 accumulate_and_check_loaded_account_data_size(
658 &mut accumulated_accounts_data_size,
659 loaded_size,
660 loaded_accounts_bytes_limit,
661 error_metrics,
662 )?;
663
664 accounts.push((*key, account));
665 Ok(())
666 };
667
668 collect_loaded_account(message.fee_payer(), loaded_fee_payer_account)?;
671
672 for (account_index, account_key) in account_keys.iter().enumerate().skip(1) {
674 let loaded_account =
675 load_transaction_account(account_loader, message, account_key, account_index, rent);
676 collect_loaded_account(account_key, loaded_account)?;
677 }
678
679 let program_indices = message
680 .program_instructions_iter()
681 .map(|(program_id, instruction)| {
682 if native_loader::check_id(program_id) {
683 return Ok(u16::MAX as IndexOfAccount);
686 }
687
688 let program_index = instruction.program_id_index as usize;
689
690 let Some(program_account) = account_loader.load_account(program_id) else {
691 error_metrics.account_not_found += 1;
692 return Err(TransactionError::ProgramAccountNotFound);
693 };
694
695 let owner_id = program_account.owner();
696 if native_loader::check_id(owner_id) {
697 return Ok(program_index as IndexOfAccount);
698 }
699
700 if !validated_loaders.contains(owner_id) {
701 if let Some(owner_account) = account_loader.load_account(owner_id) {
702 if !native_loader::check_id(owner_account.owner()) {
703 error_metrics.invalid_program_for_execution += 1;
704 return Err(TransactionError::InvalidProgramForExecution);
705 }
706 accumulate_and_check_loaded_account_data_size(
707 &mut accumulated_accounts_data_size,
708 owner_account.data().len(),
709 loaded_accounts_bytes_limit,
710 error_metrics,
711 )?;
712 validated_loaders.insert(*owner_id);
713 } else {
714 error_metrics.account_not_found += 1;
715 return Err(TransactionError::ProgramAccountNotFound);
716 }
717 }
718 Ok(program_index as IndexOfAccount)
719 })
720 .collect::<Result<Vec<IndexOfAccount>>>()?;
721
722 Ok(LoadedTransactionAccounts {
723 accounts,
724 program_indices,
725 loaded_accounts_data_size: accumulated_accounts_data_size.0,
726 })
727}
728
729fn load_transaction_account<CB: TransactionProcessingCallback>(
730 account_loader: &mut AccountLoader<CB>,
731 message: &impl SVMMessage,
732 account_key: &Pubkey,
733 account_index: usize,
734 rent: &Rent,
735) -> LoadedTransactionAccount {
736 let is_writable = message.is_writable(account_index);
737 let loaded_account = if solana_sdk_ids::sysvar::instructions::check_id(account_key) {
738 LoadedTransactionAccount {
741 loaded_size: 0,
742 account: construct_instructions_account(message),
743 }
744 } else if let Some(mut loaded_account) =
745 account_loader.load_transaction_account(account_key, is_writable)
746 {
747 if is_writable {
748 update_rent_exempt_status_for_account(rent, &mut loaded_account.account);
749 }
750 loaded_account
751 } else {
752 let mut default_account = AccountSharedData::default();
753 default_account.set_rent_epoch(RENT_EXEMPT_RENT_EPOCH);
754 LoadedTransactionAccount {
755 loaded_size: default_account.data().len(),
756 account: default_account,
757 }
758 };
759
760 loaded_account
761}
762
763fn accumulate_and_check_loaded_account_data_size(
768 accumulated_loaded_accounts_data_size: &mut Saturating<u32>,
769 account_data_size: usize,
770 requested_loaded_accounts_data_size_limit: NonZeroU32,
771 error_metrics: &mut TransactionErrorMetrics,
772) -> Result<()> {
773 let Ok(account_data_size) = u32::try_from(account_data_size) else {
774 error_metrics.max_loaded_accounts_data_size_exceeded += 1;
775 return Err(TransactionError::MaxLoadedAccountsDataSizeExceeded);
776 };
777 *accumulated_loaded_accounts_data_size += account_data_size;
778 if accumulated_loaded_accounts_data_size.0 > requested_loaded_accounts_data_size_limit.get() {
779 error_metrics.max_loaded_accounts_data_size_exceeded += 1;
780 Err(TransactionError::MaxLoadedAccountsDataSizeExceeded)
781 } else {
782 Ok(())
783 }
784}
785
786fn construct_instructions_account(message: &impl SVMMessage) -> AccountSharedData {
787 let account_keys = message.account_keys();
788 let mut decompiled_instructions = Vec::with_capacity(message.num_instructions());
789 for (program_id, instruction) in message.program_instructions_iter() {
790 let accounts = instruction
791 .accounts
792 .iter()
793 .map(|account_index| {
794 let account_index = usize::from(*account_index);
795 BorrowedAccountMeta {
796 is_signer: message.is_signer(account_index),
797 is_writable: message.is_writable(account_index),
798 pubkey: account_keys.get(account_index).unwrap(),
799 }
800 })
801 .collect();
802
803 decompiled_instructions.push(BorrowedInstruction {
804 accounts,
805 data: instruction.data,
806 program_id,
807 });
808 }
809
810 AccountSharedData::from(Account {
811 data: construct_instructions_data(&decompiled_instructions),
812 owner: sysvar::id(),
813 ..Account::default()
814 })
815}
816
817#[cfg(test)]
818mod tests {
819 use {
820 super::*,
821 crate::transaction_account_state_info::TransactionAccountStateInfo,
822 rand0_7::prelude::*,
823 solana_account::{Account, AccountSharedData, ReadableAccount, WritableAccount},
824 solana_hash::Hash,
825 solana_instruction::{AccountMeta, Instruction},
826 solana_keypair::Keypair,
827 solana_loader_v3_interface::state::UpgradeableLoaderState,
828 solana_message::{
829 compiled_instruction::CompiledInstruction,
830 v0::{LoadedAddresses, LoadedMessage},
831 LegacyMessage, Message, MessageHeader, SanitizedMessage,
832 },
833 solana_native_token::LAMPORTS_PER_SOL,
834 solana_nonce::{self as nonce, versions::Versions as NonceVersions},
835 solana_program_runtime::execution_budget::{
836 DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT, MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES,
837 },
838 solana_pubkey::Pubkey,
839 solana_rent::Rent,
840 solana_sdk_ids::{
841 bpf_loader, bpf_loader_upgradeable, native_loader, system_program, sysvar,
842 },
843 solana_signature::Signature,
844 solana_signer::Signer,
845 solana_svm_callback::{InvokeContextCallback, TransactionProcessingCallback},
846 solana_system_transaction::transfer,
847 solana_transaction::{sanitized::SanitizedTransaction, Transaction},
848 solana_transaction_context::{
849 transaction_accounts::KeyedAccountSharedData, TransactionContext,
850 },
851 solana_transaction_error::{TransactionError, TransactionResult as Result},
852 std::{
853 borrow::Cow,
854 cell::RefCell,
855 collections::{HashMap, HashSet},
856 fs::File,
857 io::Read,
858 sync::Arc,
859 },
860 test_case::test_case,
861 };
862
863 #[derive(Clone)]
864 struct TestCallbacks {
865 accounts_map: HashMap<Pubkey, AccountSharedData>,
866 #[allow(clippy::type_complexity)]
867 inspected_accounts:
868 RefCell<HashMap<Pubkey, Vec<(Option<AccountSharedData>, bool)>>>,
869 feature_set: SVMFeatureSet,
870 }
871
872 impl Default for TestCallbacks {
873 fn default() -> Self {
874 Self {
875 accounts_map: HashMap::default(),
876 inspected_accounts: RefCell::default(),
877 feature_set: SVMFeatureSet::all_enabled(),
878 }
879 }
880 }
881
882 impl InvokeContextCallback for TestCallbacks {}
883
884 impl TransactionProcessingCallback for TestCallbacks {
885 fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option<(AccountSharedData, Slot)> {
886 self.accounts_map
887 .get(pubkey)
888 .map(|account| (account.clone(), 0))
889 }
890
891 fn inspect_account(
892 &self,
893 address: &Pubkey,
894 account_state: AccountState,
895 is_writable: bool,
896 ) {
897 let account = match account_state {
898 AccountState::Dead => None,
899 AccountState::Alive(account) => Some(account.clone()),
900 };
901 self.inspected_accounts
902 .borrow_mut()
903 .entry(*address)
904 .or_default()
905 .push((account, is_writable));
906 }
907 }
908
909 impl<'a> From<&'a TestCallbacks> for AccountLoader<'a, TestCallbacks> {
910 fn from(callbacks: &'a TestCallbacks) -> AccountLoader<'a, TestCallbacks> {
911 AccountLoader::new_with_loaded_accounts_capacity(
912 None,
913 callbacks,
914 &callbacks.feature_set,
915 0,
916 )
917 }
918 }
919
920 fn load_accounts_with_features_and_rent(
921 tx: Transaction,
922 accounts: &[KeyedAccountSharedData],
923 rent: &Rent,
924 error_metrics: &mut TransactionErrorMetrics,
925 feature_set: SVMFeatureSet,
926 ) -> TransactionLoadResult {
927 let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(tx);
928 let fee_payer_account = accounts[0].1.clone();
929 let mut accounts_map = HashMap::new();
930 for (pubkey, account) in accounts {
931 accounts_map.insert(*pubkey, account.clone());
932 }
933 let callbacks = TestCallbacks {
934 accounts_map,
935 ..Default::default()
936 };
937 let mut account_loader: AccountLoader<TestCallbacks> = (&callbacks).into();
938 account_loader.feature_set = &feature_set;
939 load_transaction(
940 &mut account_loader,
941 &sanitized_tx,
942 Ok(ValidatedTransactionDetails {
943 loaded_fee_payer_account: LoadedTransactionAccount {
944 account: fee_payer_account,
945 ..LoadedTransactionAccount::default()
946 },
947 ..ValidatedTransactionDetails::default()
948 }),
949 error_metrics,
950 rent,
951 )
952 }
953
954 fn new_unchecked_sanitized_message(message: Message) -> SanitizedMessage {
955 SanitizedMessage::Legacy(LegacyMessage::new(message, &HashSet::new()))
956 }
957
958 #[test_case(false; "informal_loaded_size")]
959 #[test_case(true; "simd186_loaded_size")]
960 fn test_load_accounts_unknown_program_id(formalize_loaded_transaction_data_size: bool) {
961 let mut accounts: Vec<KeyedAccountSharedData> = Vec::new();
962 let mut error_metrics = TransactionErrorMetrics::default();
963
964 let keypair = Keypair::new();
965 let key0 = keypair.pubkey();
966 let key1 = Pubkey::from([5u8; 32]);
967
968 let account = AccountSharedData::new(1, 0, &Pubkey::default());
969 accounts.push((key0, account));
970
971 let account = AccountSharedData::new(2, 1, &Pubkey::default());
972 accounts.push((key1, account));
973
974 let instructions = vec![CompiledInstruction::new(1, &(), vec![0])];
975 let tx = Transaction::new_with_compiled_instructions(
976 &[&keypair],
977 &[],
978 Hash::default(),
979 vec![Pubkey::default()],
980 instructions,
981 );
982
983 let mut feature_set = SVMFeatureSet::all_enabled();
984 feature_set.formalize_loaded_transaction_data_size = formalize_loaded_transaction_data_size;
985
986 let load_results = load_accounts_with_features_and_rent(
987 tx,
988 &accounts,
989 &Rent::default(),
990 &mut error_metrics,
991 feature_set,
992 );
993
994 assert_eq!(error_metrics.account_not_found.0, 1);
995 assert!(matches!(
996 load_results,
997 TransactionLoadResult::FeesOnly(FeesOnlyTransaction {
998 load_error: TransactionError::ProgramAccountNotFound,
999 ..
1000 }),
1001 ));
1002 }
1003
1004 #[test_case(false; "informal_loaded_size")]
1005 #[test_case(true; "simd186_loaded_size")]
1006 fn test_load_accounts_no_loaders(formalize_loaded_transaction_data_size: bool) {
1007 let mut accounts: Vec<KeyedAccountSharedData> = Vec::new();
1008 let mut error_metrics = TransactionErrorMetrics::default();
1009
1010 let keypair = Keypair::new();
1011 let key0 = keypair.pubkey();
1012 let key1 = Pubkey::from([5u8; 32]);
1013
1014 let mut account = AccountSharedData::new(1, 0, &Pubkey::default());
1015 account.set_rent_epoch(1);
1016 accounts.push((key0, account));
1017
1018 let mut account = AccountSharedData::new(2, 1, &Pubkey::default());
1019 account.set_rent_epoch(1);
1020 accounts.push((key1, account));
1021
1022 let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
1023 let tx = Transaction::new_with_compiled_instructions(
1024 &[&keypair],
1025 &[key1],
1026 Hash::default(),
1027 vec![native_loader::id()],
1028 instructions,
1029 );
1030
1031 let mut feature_set = SVMFeatureSet::all_enabled();
1032 feature_set.formalize_loaded_transaction_data_size = formalize_loaded_transaction_data_size;
1033
1034 let loaded_accounts = load_accounts_with_features_and_rent(
1035 tx,
1036 &accounts,
1037 &Rent::default(),
1038 &mut error_metrics,
1039 feature_set,
1040 );
1041
1042 match &loaded_accounts {
1043 TransactionLoadResult::Loaded(loaded_transaction)
1044 if !formalize_loaded_transaction_data_size =>
1045 {
1046 assert_eq!(error_metrics.account_not_found.0, 0);
1047 assert_eq!(loaded_transaction.accounts.len(), 3);
1048 assert_eq!(loaded_transaction.accounts[0].1, accounts[0].1);
1049 assert_eq!(loaded_transaction.program_indices.len(), 1);
1050 assert_eq!(loaded_transaction.program_indices[0], u16::MAX);
1051 }
1052 TransactionLoadResult::FeesOnly(fees_only_tx)
1053 if formalize_loaded_transaction_data_size =>
1054 {
1055 assert_eq!(error_metrics.account_not_found.0, 1);
1056 assert_eq!(
1057 fees_only_tx.load_error,
1058 TransactionError::ProgramAccountNotFound,
1059 );
1060 }
1061 result => panic!("unexpected result: {result:?}"),
1062 }
1063 }
1064
1065 #[test_case(false; "informal_loaded_size")]
1066 #[test_case(true; "simd186_loaded_size")]
1067 fn test_load_accounts_bad_owner(formalize_loaded_transaction_data_size: bool) {
1068 let mut accounts: Vec<KeyedAccountSharedData> = Vec::new();
1069 let mut error_metrics = TransactionErrorMetrics::default();
1070
1071 let keypair = Keypair::new();
1072 let key0 = keypair.pubkey();
1073 let key1 = Pubkey::from([5u8; 32]);
1074
1075 let account = AccountSharedData::new(1, 0, &Pubkey::default());
1076 accounts.push((key0, account));
1077
1078 let mut account = AccountSharedData::new(40, 1, &Pubkey::default());
1079 account.set_executable(true);
1080 accounts.push((key1, account));
1081
1082 let instructions = vec![CompiledInstruction::new(1, &(), vec![0])];
1083 let tx = Transaction::new_with_compiled_instructions(
1084 &[&keypair],
1085 &[],
1086 Hash::default(),
1087 vec![key1],
1088 instructions,
1089 );
1090
1091 let mut feature_set = SVMFeatureSet::all_enabled();
1092 feature_set.formalize_loaded_transaction_data_size = formalize_loaded_transaction_data_size;
1093
1094 let load_results = load_accounts_with_features_and_rent(
1095 tx,
1096 &accounts,
1097 &Rent::default(),
1098 &mut error_metrics,
1099 feature_set,
1100 );
1101
1102 if formalize_loaded_transaction_data_size {
1103 assert_eq!(error_metrics.invalid_program_for_execution.0, 1);
1104 assert!(matches!(
1105 load_results,
1106 TransactionLoadResult::FeesOnly(FeesOnlyTransaction {
1107 load_error: TransactionError::InvalidProgramForExecution,
1108 ..
1109 }),
1110 ));
1111 } else {
1112 assert_eq!(error_metrics.account_not_found.0, 1);
1113 assert!(matches!(
1114 load_results,
1115 TransactionLoadResult::FeesOnly(FeesOnlyTransaction {
1116 load_error: TransactionError::ProgramAccountNotFound,
1117 ..
1118 }),
1119 ));
1120 }
1121 }
1122
1123 #[test_case(false; "informal_loaded_size")]
1124 #[test_case(true; "simd186_loaded_size")]
1125 fn test_load_accounts_not_executable(formalize_loaded_transaction_data_size: bool) {
1126 let mut accounts: Vec<KeyedAccountSharedData> = Vec::new();
1127 let mut error_metrics = TransactionErrorMetrics::default();
1128
1129 let keypair = Keypair::new();
1130 let key0 = keypair.pubkey();
1131 let key1 = Pubkey::from([5u8; 32]);
1132
1133 let account = AccountSharedData::new(1, 0, &Pubkey::default());
1134 accounts.push((key0, account));
1135
1136 let account = AccountSharedData::new(40, 0, &native_loader::id());
1137 accounts.push((key1, account));
1138
1139 let instructions = vec![CompiledInstruction::new(1, &(), vec![0])];
1140 let tx = Transaction::new_with_compiled_instructions(
1141 &[&keypair],
1142 &[],
1143 Hash::default(),
1144 vec![key1],
1145 instructions,
1146 );
1147
1148 let mut feature_set = SVMFeatureSet::all_enabled();
1149 feature_set.formalize_loaded_transaction_data_size = formalize_loaded_transaction_data_size;
1150
1151 let load_results = load_accounts_with_features_and_rent(
1152 tx,
1153 &accounts,
1154 &Rent::default(),
1155 &mut error_metrics,
1156 feature_set,
1157 );
1158
1159 assert_eq!(error_metrics.invalid_program_for_execution.0, 0);
1160 match &load_results {
1161 TransactionLoadResult::Loaded(loaded_transaction) => {
1162 assert_eq!(loaded_transaction.accounts.len(), 2);
1163 assert_eq!(loaded_transaction.accounts[0].1, accounts[0].1);
1164 assert_eq!(loaded_transaction.accounts[1].1, accounts[1].1);
1165 assert_eq!(loaded_transaction.program_indices.len(), 1);
1166 assert_eq!(loaded_transaction.program_indices[0], 1);
1167 }
1168 TransactionLoadResult::FeesOnly(fees_only_tx) => panic!("{}", fees_only_tx.load_error),
1169 TransactionLoadResult::NotLoaded(e) => panic!("{e}"),
1170 }
1171 }
1172
1173 #[test_case(false; "informal_loaded_size")]
1174 #[test_case(true; "simd186_loaded_size")]
1175 fn test_load_accounts_multiple_loaders(formalize_loaded_transaction_data_size: bool) {
1176 let mut accounts: Vec<KeyedAccountSharedData> = Vec::new();
1177 let mut error_metrics = TransactionErrorMetrics::default();
1178
1179 let keypair = Keypair::new();
1180 let key0 = keypair.pubkey();
1181 let key1 = bpf_loader_upgradeable::id();
1182 let key2 = Pubkey::from([6u8; 32]);
1183
1184 let mut account = AccountSharedData::new(1, 0, &Pubkey::default());
1185 account.set_rent_epoch(1);
1186 accounts.push((key0, account));
1187
1188 let mut account = AccountSharedData::new(40, 1, &Pubkey::default());
1189 account.set_executable(true);
1190 account.set_rent_epoch(1);
1191 account.set_owner(native_loader::id());
1192 accounts.push((key1, account));
1193
1194 let mut account = AccountSharedData::new(41, 1, &Pubkey::default());
1195 account.set_executable(true);
1196 account.set_rent_epoch(1);
1197 account.set_owner(key1);
1198 accounts.push((key2, account));
1199
1200 let instructions = vec![
1201 CompiledInstruction::new(1, &(), vec![0]),
1202 CompiledInstruction::new(2, &(), vec![0]),
1203 ];
1204 let tx = Transaction::new_with_compiled_instructions(
1205 &[&keypair],
1206 &[],
1207 Hash::default(),
1208 vec![key1, key2],
1209 instructions,
1210 );
1211
1212 let mut feature_set = SVMFeatureSet::all_enabled();
1213 feature_set.formalize_loaded_transaction_data_size = formalize_loaded_transaction_data_size;
1214
1215 let loaded_accounts = load_accounts_with_features_and_rent(
1216 tx,
1217 &accounts,
1218 &Rent::default(),
1219 &mut error_metrics,
1220 feature_set,
1221 );
1222
1223 assert_eq!(error_metrics.account_not_found.0, 0);
1224 match &loaded_accounts {
1225 TransactionLoadResult::Loaded(loaded_transaction) => {
1226 assert_eq!(loaded_transaction.accounts.len(), 3);
1227 assert_eq!(loaded_transaction.accounts[0].1, accounts[0].1);
1228 assert_eq!(loaded_transaction.program_indices.len(), 2);
1229 assert_eq!(loaded_transaction.program_indices[0], 1);
1230 assert_eq!(loaded_transaction.program_indices[1], 2);
1231 }
1232 TransactionLoadResult::FeesOnly(fees_only_tx) => panic!("{}", fees_only_tx.load_error),
1233 TransactionLoadResult::NotLoaded(e) => panic!("{e}"),
1234 }
1235 }
1236
1237 fn load_accounts_no_store(
1238 accounts: &[KeyedAccountSharedData],
1239 tx: Transaction,
1240 account_overrides: Option<&AccountOverrides>,
1241 ) -> TransactionLoadResult {
1242 let tx = SanitizedTransaction::from_transaction_for_tests(tx);
1243
1244 let mut error_metrics = TransactionErrorMetrics::default();
1245 let mut accounts_map = HashMap::new();
1246 for (pubkey, account) in accounts {
1247 accounts_map.insert(*pubkey, account.clone());
1248 }
1249 let callbacks = TestCallbacks {
1250 accounts_map,
1251 ..Default::default()
1252 };
1253 let feature_set = SVMFeatureSet::all_enabled();
1254 let mut account_loader = AccountLoader::new_with_loaded_accounts_capacity(
1255 account_overrides,
1256 &callbacks,
1257 &feature_set,
1258 0,
1259 );
1260 load_transaction(
1261 &mut account_loader,
1262 &tx,
1263 Ok(ValidatedTransactionDetails::default()),
1264 &mut error_metrics,
1265 &Rent::default(),
1266 )
1267 }
1268
1269 #[test]
1270 fn test_instructions() {
1271 agave_logger::setup();
1272 let instructions_key = solana_sdk_ids::sysvar::instructions::id();
1273 let keypair = Keypair::new();
1274 let instructions = vec![CompiledInstruction::new(1, &(), vec![0, 1])];
1275 let tx = Transaction::new_with_compiled_instructions(
1276 &[&keypair],
1277 &[solana_pubkey::new_rand(), instructions_key],
1278 Hash::default(),
1279 vec![native_loader::id()],
1280 instructions,
1281 );
1282
1283 let load_results = load_accounts_no_store(&[], tx, None);
1284 assert!(matches!(
1285 load_results,
1286 TransactionLoadResult::FeesOnly(FeesOnlyTransaction {
1287 load_error: TransactionError::ProgramAccountNotFound,
1288 ..
1289 }),
1290 ));
1291 }
1292
1293 #[test]
1294 fn test_overrides() {
1295 agave_logger::setup();
1296 let mut account_overrides = AccountOverrides::default();
1297 let slot_history_id = sysvar::slot_history::id();
1298 let account = AccountSharedData::new(42, 0, &Pubkey::default());
1299 account_overrides.set_slot_history(Some(account));
1300
1301 let keypair = Keypair::new();
1302 let account = AccountSharedData::new(1_000_000, 0, &Pubkey::default());
1303
1304 let mut program_account = AccountSharedData::default();
1305 program_account.set_lamports(1);
1306 program_account.set_executable(true);
1307 program_account.set_owner(native_loader::id());
1308
1309 let instructions = vec![CompiledInstruction::new(2, &(), vec![0])];
1310 let tx = Transaction::new_with_compiled_instructions(
1311 &[&keypair],
1312 &[slot_history_id],
1313 Hash::default(),
1314 vec![bpf_loader::id()],
1315 instructions,
1316 );
1317
1318 let loaded_accounts = load_accounts_no_store(
1319 &[
1320 (keypair.pubkey(), account),
1321 (bpf_loader::id(), program_account),
1322 ],
1323 tx,
1324 Some(&account_overrides),
1325 );
1326 match &loaded_accounts {
1327 TransactionLoadResult::Loaded(loaded_transaction) => {
1328 assert_eq!(loaded_transaction.accounts[0].0, keypair.pubkey());
1329 assert_eq!(loaded_transaction.accounts[1].0, slot_history_id);
1330 assert_eq!(loaded_transaction.accounts[1].1.lamports(), 42);
1331 }
1332 TransactionLoadResult::FeesOnly(fees_only_tx) => panic!("{}", fees_only_tx.load_error),
1333 TransactionLoadResult::NotLoaded(e) => panic!("{e}"),
1334 }
1335 }
1336
1337 #[test]
1338 fn test_accumulate_and_check_loaded_account_data_size() {
1339 let mut error_metrics = TransactionErrorMetrics::default();
1340 let mut accumulated_data_size: Saturating<u32> = Saturating(0);
1341 let data_size: usize = 123;
1342 let requested_data_size_limit = NonZeroU32::new(data_size as u32).unwrap();
1343
1344 assert!(accumulate_and_check_loaded_account_data_size(
1346 &mut accumulated_data_size,
1347 data_size,
1348 requested_data_size_limit,
1349 &mut error_metrics
1350 )
1351 .is_ok());
1352 assert_eq!(data_size as u32, accumulated_data_size.0);
1353
1354 let another_byte: usize = 1;
1356 assert_eq!(
1357 accumulate_and_check_loaded_account_data_size(
1358 &mut accumulated_data_size,
1359 another_byte,
1360 requested_data_size_limit,
1361 &mut error_metrics
1362 ),
1363 Err(TransactionError::MaxLoadedAccountsDataSizeExceeded)
1364 );
1365 }
1366
1367 struct ValidateFeePayerTestParameter {
1368 is_nonce: bool,
1369 payer_init_balance: u64,
1370 fee: u64,
1371 expected_result: Result<()>,
1372 payer_post_balance: u64,
1373 }
1374 fn validate_fee_payer_account(test_parameter: ValidateFeePayerTestParameter, rent: &Rent) {
1375 let payer_account_keys = Keypair::new();
1376 let mut account = if test_parameter.is_nonce {
1377 AccountSharedData::new_data(
1378 test_parameter.payer_init_balance,
1379 &NonceVersions::new(NonceState::Initialized(nonce::state::Data::default())),
1380 &system_program::id(),
1381 )
1382 .unwrap()
1383 } else {
1384 AccountSharedData::new(test_parameter.payer_init_balance, 0, &system_program::id())
1385 };
1386 let result = validate_fee_payer(
1387 &payer_account_keys.pubkey(),
1388 &mut account,
1389 0,
1390 &mut TransactionErrorMetrics::default(),
1391 rent,
1392 test_parameter.fee,
1393 );
1394
1395 assert_eq!(result, test_parameter.expected_result);
1396 assert_eq!(account.lamports(), test_parameter.payer_post_balance);
1397 }
1398
1399 #[test]
1400 fn test_validate_fee_payer() {
1401 let rent = Rent {
1402 lamports_per_byte_year: 1,
1403 ..Rent::default()
1404 };
1405 let min_balance = rent.minimum_balance(NonceState::size());
1406 let fee = 5_000;
1407
1408 {
1411 for (is_nonce, min_balance) in [(true, min_balance), (false, 0)] {
1412 validate_fee_payer_account(
1413 ValidateFeePayerTestParameter {
1414 is_nonce,
1415 payer_init_balance: min_balance + fee,
1416 fee,
1417 expected_result: Ok(()),
1418 payer_post_balance: min_balance,
1419 },
1420 &rent,
1421 );
1422 }
1423 }
1424
1425 {
1428 for is_nonce in [true, false] {
1429 validate_fee_payer_account(
1430 ValidateFeePayerTestParameter {
1431 is_nonce,
1432 payer_init_balance: 0,
1433 fee,
1434 expected_result: Err(TransactionError::AccountNotFound),
1435 payer_post_balance: 0,
1436 },
1437 &rent,
1438 );
1439 }
1440 }
1441
1442 {
1445 for (is_nonce, min_balance) in [(true, min_balance), (false, 0)] {
1446 validate_fee_payer_account(
1447 ValidateFeePayerTestParameter {
1448 is_nonce,
1449 payer_init_balance: min_balance + fee - 1,
1450 fee,
1451 expected_result: Err(TransactionError::InsufficientFundsForFee),
1452 payer_post_balance: min_balance + fee - 1,
1453 },
1454 &rent,
1455 );
1456 }
1457 }
1458
1459 {
1462 validate_fee_payer_account(
1463 ValidateFeePayerTestParameter {
1464 is_nonce: false,
1465 payer_init_balance: u64::MAX,
1466 fee: u64::MAX,
1467 expected_result: Ok(()),
1468 payer_post_balance: 0,
1469 },
1470 &rent,
1471 );
1472 }
1473 }
1474
1475 #[test]
1476 fn test_validate_nonce_fee_payer_with_checked_arithmetic() {
1477 let rent = Rent {
1478 lamports_per_byte_year: 1,
1479 ..Rent::default()
1480 };
1481
1482 validate_fee_payer_account(
1486 ValidateFeePayerTestParameter {
1487 is_nonce: true,
1488 payer_init_balance: u64::MAX,
1489 fee: u64::MAX,
1490 expected_result: Err(TransactionError::InsufficientFundsForFee),
1491 payer_post_balance: u64::MAX,
1492 },
1493 &rent,
1494 );
1495 }
1496
1497 #[test]
1498 fn test_construct_instructions_account() {
1499 let loaded_message = LoadedMessage {
1500 message: Cow::Owned(solana_message::v0::Message::default()),
1501 loaded_addresses: Cow::Owned(LoadedAddresses::default()),
1502 is_writable_account_cache: vec![false],
1503 };
1504 let message = SanitizedMessage::V0(loaded_message);
1505 let shared_data = construct_instructions_account(&message);
1506 let expected = AccountSharedData::from(Account {
1507 data: construct_instructions_data(&message.decompile_instructions()),
1508 owner: sysvar::id(),
1509 ..Account::default()
1510 });
1511 assert_eq!(shared_data, expected);
1512 }
1513
1514 #[test]
1515 fn test_load_transaction_accounts_fee_payer() {
1516 let fee_payer_address = Pubkey::new_unique();
1517 let message = Message {
1518 account_keys: vec![fee_payer_address],
1519 header: MessageHeader::default(),
1520 instructions: vec![],
1521 recent_blockhash: Hash::default(),
1522 };
1523
1524 let sanitized_message = new_unchecked_sanitized_message(message);
1525 let mut mock_bank = TestCallbacks::default();
1526
1527 let fee_payer_balance = 200;
1528 let mut fee_payer_account = AccountSharedData::default();
1529 fee_payer_account.set_lamports(fee_payer_balance);
1530 mock_bank
1531 .accounts_map
1532 .insert(fee_payer_address, fee_payer_account.clone());
1533 let mut account_loader = (&mock_bank).into();
1534
1535 let mut error_metrics = TransactionErrorMetrics::default();
1536
1537 let sanitized_transaction = SanitizedTransaction::new_for_tests(
1538 sanitized_message,
1539 vec![Signature::new_unique()],
1540 false,
1541 );
1542 let result = load_transaction_accounts(
1543 &mut account_loader,
1544 sanitized_transaction.message(),
1545 LoadedTransactionAccount {
1546 loaded_size: fee_payer_account.data().len(),
1547 account: fee_payer_account.clone(),
1548 },
1549 MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES,
1550 &mut error_metrics,
1551 &Rent::default(),
1552 );
1553 assert_eq!(
1554 result.unwrap(),
1555 LoadedTransactionAccounts {
1556 accounts: vec![(fee_payer_address, fee_payer_account)],
1557 program_indices: vec![],
1558 loaded_accounts_data_size: 0,
1559 }
1560 );
1561 }
1562
1563 #[test_case(false; "informal_loaded_size")]
1564 #[test_case(true; "simd186_loaded_size")]
1565 fn test_load_transaction_accounts_native_loader(formalize_loaded_transaction_data_size: bool) {
1566 let key1 = Keypair::new();
1567 let message = Message {
1568 account_keys: vec![key1.pubkey(), native_loader::id()],
1569 header: MessageHeader::default(),
1570 instructions: vec![CompiledInstruction {
1571 program_id_index: 1,
1572 accounts: vec![0],
1573 data: vec![],
1574 }],
1575 recent_blockhash: Hash::default(),
1576 };
1577
1578 let sanitized_message = new_unchecked_sanitized_message(message);
1579 let mut mock_bank = TestCallbacks::default();
1580 mock_bank.feature_set.formalize_loaded_transaction_data_size =
1581 formalize_loaded_transaction_data_size;
1582 mock_bank
1583 .accounts_map
1584 .insert(native_loader::id(), AccountSharedData::default());
1585 let mut fee_payer_account = AccountSharedData::default();
1586 fee_payer_account.set_lamports(200);
1587 mock_bank
1588 .accounts_map
1589 .insert(key1.pubkey(), fee_payer_account.clone());
1590 let mut account_loader = (&mock_bank).into();
1591
1592 let mut error_metrics = TransactionErrorMetrics::default();
1593
1594 let sanitized_transaction = SanitizedTransaction::new_for_tests(
1595 sanitized_message,
1596 vec![Signature::new_unique()],
1597 false,
1598 );
1599
1600 let base_account_size = if formalize_loaded_transaction_data_size {
1601 TRANSACTION_ACCOUNT_BASE_SIZE
1602 } else {
1603 0
1604 };
1605
1606 let result = load_transaction_accounts(
1607 &mut account_loader,
1608 sanitized_transaction.message(),
1609 LoadedTransactionAccount {
1610 account: fee_payer_account.clone(),
1611 loaded_size: base_account_size,
1612 },
1613 MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES,
1614 &mut error_metrics,
1615 &Rent::default(),
1616 );
1617
1618 if formalize_loaded_transaction_data_size {
1619 assert_eq!(
1620 result.unwrap_err(),
1621 TransactionError::ProgramAccountNotFound,
1622 );
1623 } else {
1624 let loaded_accounts_data_size = base_account_size as u32 * 2;
1625 assert_eq!(
1626 result.unwrap(),
1627 LoadedTransactionAccounts {
1628 accounts: vec![
1629 (key1.pubkey(), fee_payer_account),
1630 (
1631 native_loader::id(),
1632 mock_bank.accounts_map[&native_loader::id()].clone()
1633 )
1634 ],
1635 program_indices: vec![u16::MAX],
1636 loaded_accounts_data_size,
1637 }
1638 );
1639 }
1640 }
1641
1642 #[test]
1643 fn test_load_transaction_accounts_program_account_no_data() {
1644 let key1 = Keypair::new();
1645 let key2 = Keypair::new();
1646
1647 let message = Message {
1648 account_keys: vec![key1.pubkey(), key2.pubkey()],
1649 header: MessageHeader::default(),
1650 instructions: vec![CompiledInstruction {
1651 program_id_index: 1,
1652 accounts: vec![0, 1],
1653 data: vec![],
1654 }],
1655 recent_blockhash: Hash::default(),
1656 };
1657
1658 let sanitized_message = new_unchecked_sanitized_message(message);
1659 let mut mock_bank = TestCallbacks::default();
1660 let mut account_data = AccountSharedData::default();
1661 account_data.set_lamports(200);
1662 mock_bank.accounts_map.insert(key1.pubkey(), account_data);
1663 let mut account_loader = (&mock_bank).into();
1664
1665 let mut error_metrics = TransactionErrorMetrics::default();
1666
1667 let sanitized_transaction = SanitizedTransaction::new_for_tests(
1668 sanitized_message,
1669 vec![Signature::new_unique()],
1670 false,
1671 );
1672 let result = load_transaction_accounts(
1673 &mut account_loader,
1674 sanitized_transaction.message(),
1675 LoadedTransactionAccount::default(),
1676 MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES,
1677 &mut error_metrics,
1678 &Rent::default(),
1679 );
1680
1681 assert_eq!(result.err(), Some(TransactionError::ProgramAccountNotFound));
1682 }
1683
1684 #[test]
1685 fn test_load_transaction_accounts_invalid_program_for_execution() {
1686 let key1 = Keypair::new();
1687 let key2 = Keypair::new();
1688
1689 let message = Message {
1690 account_keys: vec![key1.pubkey(), key2.pubkey()],
1691 header: MessageHeader::default(),
1692 instructions: vec![CompiledInstruction {
1693 program_id_index: 0,
1694 accounts: vec![0, 1],
1695 data: vec![],
1696 }],
1697 recent_blockhash: Hash::default(),
1698 };
1699
1700 let sanitized_message = new_unchecked_sanitized_message(message);
1701 let mut mock_bank = TestCallbacks::default();
1702 let mut account_data = AccountSharedData::default();
1703 account_data.set_lamports(200);
1704 mock_bank.accounts_map.insert(key1.pubkey(), account_data);
1705 let mut account_loader = (&mock_bank).into();
1706
1707 let mut error_metrics = TransactionErrorMetrics::default();
1708
1709 let sanitized_transaction = SanitizedTransaction::new_for_tests(
1710 sanitized_message,
1711 vec![Signature::new_unique()],
1712 false,
1713 );
1714 let result = load_transaction_accounts(
1715 &mut account_loader,
1716 sanitized_transaction.message(),
1717 LoadedTransactionAccount::default(),
1718 MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES,
1719 &mut error_metrics,
1720 &Rent::default(),
1721 );
1722
1723 assert_eq!(
1724 result.err(),
1725 Some(TransactionError::InvalidProgramForExecution)
1726 );
1727 }
1728
1729 #[test_case(false; "informal_loaded_size")]
1730 #[test_case(true; "simd186_loaded_size")]
1731 fn test_load_transaction_accounts_native_loader_owner(
1732 formalize_loaded_transaction_data_size: bool,
1733 ) {
1734 let key1 = Keypair::new();
1735 let key2 = Keypair::new();
1736
1737 let message = Message {
1738 account_keys: vec![key2.pubkey(), key1.pubkey()],
1739 header: MessageHeader::default(),
1740 instructions: vec![CompiledInstruction {
1741 program_id_index: 1,
1742 accounts: vec![0],
1743 data: vec![],
1744 }],
1745 recent_blockhash: Hash::default(),
1746 };
1747
1748 let sanitized_message = new_unchecked_sanitized_message(message);
1749 let mut mock_bank = TestCallbacks::default();
1750 mock_bank.feature_set.formalize_loaded_transaction_data_size =
1751 formalize_loaded_transaction_data_size;
1752 let mut account_data = AccountSharedData::default();
1753 account_data.set_owner(native_loader::id());
1754 account_data.set_lamports(1);
1755 account_data.set_executable(true);
1756 mock_bank.accounts_map.insert(key1.pubkey(), account_data);
1757
1758 let mut fee_payer_account = AccountSharedData::default();
1759 fee_payer_account.set_lamports(200);
1760 mock_bank
1761 .accounts_map
1762 .insert(key2.pubkey(), fee_payer_account.clone());
1763 let mut account_loader = (&mock_bank).into();
1764
1765 let mut error_metrics = TransactionErrorMetrics::default();
1766
1767 let sanitized_transaction = SanitizedTransaction::new_for_tests(
1768 sanitized_message,
1769 vec![Signature::new_unique()],
1770 false,
1771 );
1772
1773 let base_account_size = if formalize_loaded_transaction_data_size {
1774 TRANSACTION_ACCOUNT_BASE_SIZE
1775 } else {
1776 0
1777 };
1778
1779 let result = load_transaction_accounts(
1780 &mut account_loader,
1781 sanitized_transaction.message(),
1782 LoadedTransactionAccount {
1783 account: fee_payer_account.clone(),
1784 loaded_size: base_account_size,
1785 },
1786 MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES,
1787 &mut error_metrics,
1788 &Rent::default(),
1789 );
1790
1791 let loaded_accounts_data_size = base_account_size as u32 * 2;
1792
1793 assert_eq!(
1794 result.unwrap(),
1795 LoadedTransactionAccounts {
1796 accounts: vec![
1797 (key2.pubkey(), fee_payer_account),
1798 (
1799 key1.pubkey(),
1800 mock_bank.accounts_map[&key1.pubkey()].clone()
1801 ),
1802 ],
1803 program_indices: vec![1],
1804 loaded_accounts_data_size,
1805 }
1806 );
1807 }
1808
1809 #[test]
1810 fn test_load_transaction_accounts_program_account_not_found_after_all_checks() {
1811 let key1 = Keypair::new();
1812 let key2 = Keypair::new();
1813
1814 let message = Message {
1815 account_keys: vec![key2.pubkey(), key1.pubkey()],
1816 header: MessageHeader::default(),
1817 instructions: vec![CompiledInstruction {
1818 program_id_index: 1,
1819 accounts: vec![0],
1820 data: vec![],
1821 }],
1822 recent_blockhash: Hash::default(),
1823 };
1824
1825 let sanitized_message = new_unchecked_sanitized_message(message);
1826 let mut mock_bank = TestCallbacks::default();
1827 let mut account_data = AccountSharedData::default();
1828 account_data.set_executable(true);
1829 mock_bank.accounts_map.insert(key1.pubkey(), account_data);
1830
1831 let mut account_data = AccountSharedData::default();
1832 account_data.set_lamports(200);
1833 mock_bank.accounts_map.insert(key2.pubkey(), account_data);
1834 let mut account_loader = (&mock_bank).into();
1835
1836 let mut error_metrics = TransactionErrorMetrics::default();
1837
1838 let sanitized_transaction = SanitizedTransaction::new_for_tests(
1839 sanitized_message,
1840 vec![Signature::new_unique()],
1841 false,
1842 );
1843 let result = load_transaction_accounts(
1844 &mut account_loader,
1845 sanitized_transaction.message(),
1846 LoadedTransactionAccount::default(),
1847 MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES,
1848 &mut error_metrics,
1849 &Rent::default(),
1850 );
1851
1852 assert_eq!(result.err(), Some(TransactionError::ProgramAccountNotFound));
1853 }
1854
1855 #[test]
1856 fn test_load_transaction_accounts_program_account_invalid_program_for_execution_last_check() {
1857 let key1 = Keypair::new();
1858 let key2 = Keypair::new();
1859 let key3 = Keypair::new();
1860
1861 let message = Message {
1862 account_keys: vec![key2.pubkey(), key1.pubkey()],
1863 header: MessageHeader::default(),
1864 instructions: vec![CompiledInstruction {
1865 program_id_index: 1,
1866 accounts: vec![0],
1867 data: vec![],
1868 }],
1869 recent_blockhash: Hash::default(),
1870 };
1871
1872 let sanitized_message = new_unchecked_sanitized_message(message);
1873 let mut mock_bank = TestCallbacks::default();
1874 let mut account_data = AccountSharedData::default();
1875 account_data.set_lamports(1);
1876 account_data.set_executable(true);
1877 account_data.set_owner(key3.pubkey());
1878 mock_bank.accounts_map.insert(key1.pubkey(), account_data);
1879
1880 let mut account_data = AccountSharedData::default();
1881 account_data.set_lamports(200);
1882 mock_bank.accounts_map.insert(key2.pubkey(), account_data);
1883 mock_bank
1884 .accounts_map
1885 .insert(key3.pubkey(), AccountSharedData::default());
1886 let mut account_loader = (&mock_bank).into();
1887
1888 let mut error_metrics = TransactionErrorMetrics::default();
1889
1890 let sanitized_transaction = SanitizedTransaction::new_for_tests(
1891 sanitized_message,
1892 vec![Signature::new_unique()],
1893 false,
1894 );
1895 let result = load_transaction_accounts(
1896 &mut account_loader,
1897 sanitized_transaction.message(),
1898 LoadedTransactionAccount::default(),
1899 MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES,
1900 &mut error_metrics,
1901 &Rent::default(),
1902 );
1903
1904 assert_eq!(
1905 result.err(),
1906 Some(TransactionError::InvalidProgramForExecution)
1907 );
1908 }
1909
1910 #[test_case(false; "informal_loaded_size")]
1911 #[test_case(true; "simd186_loaded_size")]
1912 fn test_load_transaction_accounts_program_success_complete(
1913 formalize_loaded_transaction_data_size: bool,
1914 ) {
1915 let key1 = Keypair::new();
1916 let key2 = Keypair::new();
1917
1918 let message = Message {
1919 account_keys: vec![key2.pubkey(), key1.pubkey()],
1920 header: MessageHeader::default(),
1921 instructions: vec![CompiledInstruction {
1922 program_id_index: 1,
1923 accounts: vec![0],
1924 data: vec![],
1925 }],
1926 recent_blockhash: Hash::default(),
1927 };
1928
1929 let sanitized_message = new_unchecked_sanitized_message(message);
1930 let mut mock_bank = TestCallbacks::default();
1931 mock_bank.feature_set.formalize_loaded_transaction_data_size =
1932 formalize_loaded_transaction_data_size;
1933 let mut account_data = AccountSharedData::default();
1934 account_data.set_lamports(1);
1935 account_data.set_executable(true);
1936 account_data.set_owner(bpf_loader::id());
1937 mock_bank.accounts_map.insert(key1.pubkey(), account_data);
1938
1939 let mut fee_payer_account = AccountSharedData::default();
1940 fee_payer_account.set_lamports(200);
1941 mock_bank
1942 .accounts_map
1943 .insert(key2.pubkey(), fee_payer_account.clone());
1944
1945 let mut account_data = AccountSharedData::default();
1946 account_data.set_lamports(1);
1947 account_data.set_executable(true);
1948 account_data.set_owner(native_loader::id());
1949 mock_bank
1950 .accounts_map
1951 .insert(bpf_loader::id(), account_data);
1952 let mut account_loader = (&mock_bank).into();
1953
1954 let mut error_metrics = TransactionErrorMetrics::default();
1955
1956 let sanitized_transaction = SanitizedTransaction::new_for_tests(
1957 sanitized_message,
1958 vec![Signature::new_unique()],
1959 false,
1960 );
1961
1962 let base_account_size = if formalize_loaded_transaction_data_size {
1963 TRANSACTION_ACCOUNT_BASE_SIZE
1964 } else {
1965 0
1966 };
1967
1968 let result = load_transaction_accounts(
1969 &mut account_loader,
1970 sanitized_transaction.message(),
1971 LoadedTransactionAccount {
1972 account: fee_payer_account.clone(),
1973 loaded_size: base_account_size,
1974 },
1975 MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES,
1976 &mut error_metrics,
1977 &Rent::default(),
1978 );
1979
1980 let loaded_accounts_data_size = base_account_size as u32 * 2;
1981
1982 assert_eq!(
1983 result.unwrap(),
1984 LoadedTransactionAccounts {
1985 accounts: vec![
1986 (key2.pubkey(), fee_payer_account),
1987 (
1988 key1.pubkey(),
1989 mock_bank.accounts_map[&key1.pubkey()].clone()
1990 ),
1991 ],
1992 program_indices: vec![1],
1993 loaded_accounts_data_size,
1994 }
1995 );
1996 }
1997
1998 #[test_case(false; "informal_loaded_size")]
1999 #[test_case(true; "simd186_loaded_size")]
2000 fn test_load_transaction_accounts_program_builtin_saturating_add(
2001 formalize_loaded_transaction_data_size: bool,
2002 ) {
2003 let key1 = Keypair::new();
2004 let key2 = Keypair::new();
2005 let key3 = Keypair::new();
2006
2007 let message = Message {
2008 account_keys: vec![key2.pubkey(), key1.pubkey(), key3.pubkey()],
2009 header: MessageHeader::default(),
2010 instructions: vec![
2011 CompiledInstruction {
2012 program_id_index: 1,
2013 accounts: vec![0],
2014 data: vec![],
2015 },
2016 CompiledInstruction {
2017 program_id_index: 1,
2018 accounts: vec![2],
2019 data: vec![],
2020 },
2021 ],
2022 recent_blockhash: Hash::default(),
2023 };
2024
2025 let sanitized_message = new_unchecked_sanitized_message(message);
2026 let mut mock_bank = TestCallbacks::default();
2027 mock_bank.feature_set.formalize_loaded_transaction_data_size =
2028 formalize_loaded_transaction_data_size;
2029 let mut account_data = AccountSharedData::default();
2030 account_data.set_lamports(1);
2031 account_data.set_executable(true);
2032 account_data.set_owner(bpf_loader::id());
2033 mock_bank.accounts_map.insert(key1.pubkey(), account_data);
2034
2035 let mut fee_payer_account = AccountSharedData::default();
2036 fee_payer_account.set_lamports(200);
2037 mock_bank
2038 .accounts_map
2039 .insert(key2.pubkey(), fee_payer_account.clone());
2040
2041 let mut account_data = AccountSharedData::default();
2042 account_data.set_lamports(1);
2043 account_data.set_executable(true);
2044 account_data.set_owner(native_loader::id());
2045 mock_bank
2046 .accounts_map
2047 .insert(bpf_loader::id(), account_data);
2048 let mut account_loader = (&mock_bank).into();
2049
2050 let mut error_metrics = TransactionErrorMetrics::default();
2051
2052 let sanitized_transaction = SanitizedTransaction::new_for_tests(
2053 sanitized_message,
2054 vec![Signature::new_unique()],
2055 false,
2056 );
2057
2058 let base_account_size = if formalize_loaded_transaction_data_size {
2059 TRANSACTION_ACCOUNT_BASE_SIZE
2060 } else {
2061 0
2062 };
2063
2064 let result = load_transaction_accounts(
2065 &mut account_loader,
2066 sanitized_transaction.message(),
2067 LoadedTransactionAccount {
2068 account: fee_payer_account.clone(),
2069 loaded_size: base_account_size,
2070 },
2071 MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES,
2072 &mut error_metrics,
2073 &Rent::default(),
2074 );
2075
2076 let loaded_accounts_data_size = base_account_size as u32 * 2;
2077
2078 let mut account_data = AccountSharedData::default();
2079 account_data.set_rent_epoch(RENT_EXEMPT_RENT_EPOCH);
2080 assert_eq!(
2081 result.unwrap(),
2082 LoadedTransactionAccounts {
2083 accounts: vec![
2084 (key2.pubkey(), fee_payer_account),
2085 (
2086 key1.pubkey(),
2087 mock_bank.accounts_map[&key1.pubkey()].clone()
2088 ),
2089 (key3.pubkey(), account_data),
2090 ],
2091 program_indices: vec![1, 1],
2092 loaded_accounts_data_size,
2093 }
2094 );
2095 }
2096
2097 #[test]
2098 fn test_rent_state_list_len() {
2099 let mint_keypair = Keypair::new();
2100 let mut bank = TestCallbacks::default();
2101 let recipient = Pubkey::new_unique();
2102 let last_block_hash = Hash::new_unique();
2103
2104 let mut system_data = AccountSharedData::default();
2105 system_data.set_lamports(1);
2106 system_data.set_executable(true);
2107 system_data.set_owner(native_loader::id());
2108 bank.accounts_map
2109 .insert(Pubkey::new_from_array([0u8; 32]), system_data);
2110
2111 let mut mint_data = AccountSharedData::default();
2112 mint_data.set_lamports(2);
2113 bank.accounts_map.insert(mint_keypair.pubkey(), mint_data);
2114 bank.accounts_map
2115 .insert(recipient, AccountSharedData::default());
2116 let mut account_loader = (&bank).into();
2117
2118 let tx = transfer(&mint_keypair, &recipient, LAMPORTS_PER_SOL, last_block_hash);
2119 let num_accounts = tx.message().account_keys.len();
2120 let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(tx);
2121 let mut error_metrics = TransactionErrorMetrics::default();
2122 let load_result = load_transaction(
2123 &mut account_loader,
2124 &sanitized_tx.clone(),
2125 Ok(ValidatedTransactionDetails::default()),
2126 &mut error_metrics,
2127 &Rent::default(),
2128 );
2129
2130 let TransactionLoadResult::Loaded(loaded_transaction) = load_result else {
2131 panic!("transaction loading failed");
2132 };
2133
2134 let compute_budget = SVMTransactionExecutionBudget {
2135 compute_unit_limit: u64::from(DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT),
2136 ..SVMTransactionExecutionBudget::default()
2137 };
2138 let rent = Rent::default();
2139 let transaction_context = TransactionContext::new(
2140 loaded_transaction.accounts,
2141 rent.clone(),
2142 compute_budget.max_instruction_stack_depth,
2143 compute_budget.max_instruction_trace_length,
2144 );
2145
2146 assert_eq!(
2147 TransactionAccountStateInfo::new(&transaction_context, sanitized_tx.message(), &rent,)
2148 .len(),
2149 num_accounts,
2150 );
2151 }
2152
2153 #[test_case(false; "informal_loaded_size")]
2154 #[test_case(true; "simd186_loaded_size")]
2155 fn test_load_accounts_success(formalize_loaded_transaction_data_size: bool) {
2156 let key1 = Keypair::new();
2157 let key2 = Keypair::new();
2158 let key3 = Keypair::new();
2159
2160 let message = Message {
2161 account_keys: vec![key2.pubkey(), key1.pubkey(), key3.pubkey()],
2162 header: MessageHeader::default(),
2163 instructions: vec![
2164 CompiledInstruction {
2165 program_id_index: 1,
2166 accounts: vec![0],
2167 data: vec![],
2168 },
2169 CompiledInstruction {
2170 program_id_index: 1,
2171 accounts: vec![2],
2172 data: vec![],
2173 },
2174 ],
2175 recent_blockhash: Hash::default(),
2176 };
2177
2178 let sanitized_message = new_unchecked_sanitized_message(message);
2179 let mut mock_bank = TestCallbacks::default();
2180 mock_bank.feature_set.formalize_loaded_transaction_data_size =
2181 formalize_loaded_transaction_data_size;
2182 let mut account_data = AccountSharedData::default();
2183 account_data.set_lamports(1);
2184 account_data.set_executable(true);
2185 account_data.set_owner(bpf_loader::id());
2186 mock_bank.accounts_map.insert(key1.pubkey(), account_data);
2187
2188 let mut fee_payer_account = AccountSharedData::default();
2189 fee_payer_account.set_lamports(200);
2190 mock_bank
2191 .accounts_map
2192 .insert(key2.pubkey(), fee_payer_account.clone());
2193
2194 let mut account_data = AccountSharedData::default();
2195 account_data.set_lamports(1);
2196 account_data.set_executable(true);
2197 account_data.set_owner(native_loader::id());
2198 mock_bank
2199 .accounts_map
2200 .insert(bpf_loader::id(), account_data);
2201 let mut account_loader = (&mock_bank).into();
2202
2203 let mut error_metrics = TransactionErrorMetrics::default();
2204
2205 let sanitized_transaction = SanitizedTransaction::new_for_tests(
2206 sanitized_message,
2207 vec![Signature::new_unique()],
2208 false,
2209 );
2210
2211 let base_account_size = if formalize_loaded_transaction_data_size {
2212 TRANSACTION_ACCOUNT_BASE_SIZE
2213 } else {
2214 0
2215 };
2216
2217 let validation_result = Ok(ValidatedTransactionDetails {
2218 loaded_fee_payer_account: LoadedTransactionAccount {
2219 account: fee_payer_account,
2220 loaded_size: base_account_size,
2221 },
2222 ..ValidatedTransactionDetails::default()
2223 });
2224
2225 let load_result = load_transaction(
2226 &mut account_loader,
2227 &sanitized_transaction,
2228 validation_result,
2229 &mut error_metrics,
2230 &Rent::default(),
2231 );
2232
2233 let loaded_accounts_data_size = base_account_size as u32 * 2;
2234
2235 let mut account_data = AccountSharedData::default();
2236 account_data.set_rent_epoch(RENT_EXEMPT_RENT_EPOCH);
2237
2238 let TransactionLoadResult::Loaded(loaded_transaction) = load_result else {
2239 panic!("transaction loading failed");
2240 };
2241 assert_eq!(
2242 loaded_transaction,
2243 LoadedTransaction {
2244 accounts: vec![
2245 (
2246 key2.pubkey(),
2247 mock_bank.accounts_map[&key2.pubkey()].clone()
2248 ),
2249 (
2250 key1.pubkey(),
2251 mock_bank.accounts_map[&key1.pubkey()].clone()
2252 ),
2253 (key3.pubkey(), account_data),
2254 ],
2255 program_indices: vec![1, 1],
2256 fee_details: FeeDetails::default(),
2257 rollback_accounts: RollbackAccounts::default(),
2258 compute_budget: SVMTransactionExecutionBudget::default(),
2259 loaded_accounts_data_size,
2260 }
2261 );
2262 }
2263
2264 #[test]
2265 fn test_load_accounts_error() {
2266 let mock_bank = TestCallbacks::default();
2267 let mut account_loader = (&mock_bank).into();
2268 let rent = Rent::default();
2269
2270 let message = Message {
2271 account_keys: vec![Pubkey::new_from_array([0; 32])],
2272 header: MessageHeader::default(),
2273 instructions: vec![CompiledInstruction {
2274 program_id_index: 0,
2275 accounts: vec![],
2276 data: vec![],
2277 }],
2278 recent_blockhash: Hash::default(),
2279 };
2280
2281 let sanitized_message = new_unchecked_sanitized_message(message);
2282 let sanitized_transaction = SanitizedTransaction::new_for_tests(
2283 sanitized_message,
2284 vec![Signature::new_unique()],
2285 false,
2286 );
2287
2288 let validation_result = Ok(ValidatedTransactionDetails::default());
2289 let load_result = load_transaction(
2290 &mut account_loader,
2291 &sanitized_transaction,
2292 validation_result.clone(),
2293 &mut TransactionErrorMetrics::default(),
2294 &rent,
2295 );
2296
2297 assert!(matches!(
2298 load_result,
2299 TransactionLoadResult::FeesOnly(FeesOnlyTransaction {
2300 load_error: TransactionError::ProgramAccountNotFound,
2301 ..
2302 }),
2303 ));
2304
2305 let validation_result = Err(TransactionError::InvalidWritableAccount);
2306
2307 let load_result = load_transaction(
2308 &mut account_loader,
2309 &sanitized_transaction,
2310 validation_result,
2311 &mut TransactionErrorMetrics::default(),
2312 &rent,
2313 );
2314
2315 assert!(matches!(
2316 load_result,
2317 TransactionLoadResult::NotLoaded(TransactionError::InvalidWritableAccount),
2318 ));
2319 }
2320
2321 #[test]
2322 fn test_update_rent_exempt_status_for_account() {
2323 let rent = Rent::default();
2324
2325 let min_exempt_balance = rent.minimum_balance(0);
2326 let mut account = AccountSharedData::from(Account {
2327 lamports: min_exempt_balance,
2328 ..Account::default()
2329 });
2330
2331 update_rent_exempt_status_for_account(&rent, &mut account);
2332 assert_eq!(account.rent_epoch(), RENT_EXEMPT_RENT_EPOCH);
2333 }
2334
2335 #[test]
2336 fn test_update_rent_exempt_status_for_rent_paying_account() {
2337 let rent = Rent::default();
2338
2339 let mut account = AccountSharedData::from(Account {
2340 lamports: 1,
2341 ..Account::default()
2342 });
2343
2344 update_rent_exempt_status_for_account(&rent, &mut account);
2345 assert_eq!(account.rent_epoch(), 0);
2346 assert_eq!(account.lamports(), 1);
2347 }
2348
2349 #[test]
2352 fn test_inspect_account_non_fee_payer() {
2353 let mut mock_bank = TestCallbacks::default();
2354
2355 let address0 = Pubkey::new_unique(); let address1 = Pubkey::new_unique(); let address2 = Pubkey::new_unique(); let address3 = Pubkey::new_unique(); let mut account0 = AccountSharedData::default();
2361 account0.set_lamports(1_000_000_000);
2362 mock_bank.accounts_map.insert(address0, account0.clone());
2363
2364 let mut account1 = AccountSharedData::default();
2365 account1.set_lamports(2_000_000_000);
2366 mock_bank.accounts_map.insert(address1, account1.clone());
2367
2368 let mut account3 = AccountSharedData::default();
2371 account3.set_lamports(4_000_000_000);
2372 account3.set_executable(true);
2373 account3.set_owner(bpf_loader::id());
2374 mock_bank.accounts_map.insert(address3, account3.clone());
2375 let mut account_loader = (&mock_bank).into();
2376
2377 let message = Message {
2378 account_keys: vec![address0, address1, address2, address3],
2379 header: MessageHeader::default(),
2380 instructions: vec![
2381 CompiledInstruction {
2382 program_id_index: 3,
2383 accounts: vec![0],
2384 data: vec![],
2385 },
2386 CompiledInstruction {
2387 program_id_index: 3,
2388 accounts: vec![1, 2],
2389 data: vec![],
2390 },
2391 CompiledInstruction {
2392 program_id_index: 3,
2393 accounts: vec![1],
2394 data: vec![],
2395 },
2396 ],
2397 recent_blockhash: Hash::new_unique(),
2398 };
2399 let sanitized_message = new_unchecked_sanitized_message(message);
2400 let sanitized_transaction = SanitizedTransaction::new_for_tests(
2401 sanitized_message,
2402 vec![Signature::new_unique()],
2403 false,
2404 );
2405 let validation_result = Ok(ValidatedTransactionDetails {
2406 loaded_fee_payer_account: LoadedTransactionAccount {
2407 account: account0.clone(),
2408 ..LoadedTransactionAccount::default()
2409 },
2410 ..ValidatedTransactionDetails::default()
2411 });
2412 let _load_results = load_transaction(
2413 &mut account_loader,
2414 &sanitized_transaction,
2415 validation_result,
2416 &mut TransactionErrorMetrics::default(),
2417 &Rent::default(),
2418 );
2419
2420 let mut actual_inspected_accounts: Vec<_> = mock_bank
2422 .inspected_accounts
2423 .borrow()
2424 .iter()
2425 .map(|(k, v)| (*k, v.clone()))
2426 .collect();
2427 actual_inspected_accounts.sort_unstable_by(|a, b| a.0.cmp(&b.0));
2428
2429 let mut expected_inspected_accounts = vec![
2430 (address1, vec![(Some(account1), true)]),
2432 (address2, vec![(None, true)]),
2433 (address3, vec![(Some(account3), false)]),
2434 ];
2435 expected_inspected_accounts.sort_unstable_by(|a, b| a.0.cmp(&b.0));
2436
2437 assert_eq!(actual_inspected_accounts, expected_inspected_accounts,);
2438 }
2439
2440 #[test]
2441 fn test_load_transaction_accounts_data_sizes() {
2442 let mut mock_bank = TestCallbacks::default();
2443 mock_bank.feature_set.formalize_loaded_transaction_data_size = false;
2444
2445 let loader_v2 = bpf_loader::id();
2446 let loader_v3 = bpf_loader_upgradeable::id();
2447 let program1_keypair = Keypair::new();
2448 let program1 = program1_keypair.pubkey();
2449 let program2 = Pubkey::new_unique();
2450 let programdata2 = Pubkey::new_unique();
2451 use solana_account::state_traits::StateMut;
2452
2453 let program2_size = std::mem::size_of::<UpgradeableLoaderState>() as u32;
2454 let mut program2_account = AccountSharedData::default();
2455 program2_account.set_owner(loader_v3);
2456 program2_account.set_lamports(LAMPORTS_PER_SOL);
2457 program2_account.set_executable(true);
2458 program2_account.set_data(vec![0; program2_size as usize]);
2459 program2_account
2460 .set_state(&UpgradeableLoaderState::Program {
2461 programdata_address: programdata2,
2462 })
2463 .unwrap();
2464 mock_bank.accounts_map.insert(program2, program2_account);
2465 let mut programdata2_account = AccountSharedData::default();
2466 programdata2_account.set_owner(loader_v3);
2467 programdata2_account.set_lamports(LAMPORTS_PER_SOL);
2468 programdata2_account.set_data(vec![0; program2_size as usize]);
2469 programdata2_account
2470 .set_state(&UpgradeableLoaderState::ProgramData {
2471 slot: 0,
2472 upgrade_authority_address: None,
2473 })
2474 .unwrap();
2475 let mut programdata = programdata2_account.data().to_vec();
2476 let mut file =
2477 File::open("tests/example-programs/hello-solana/hello_solana_program.so").unwrap();
2478 file.read_to_end(&mut programdata).unwrap();
2479 let programdata2_size = programdata.len() as u32;
2480 programdata2_account.set_data(programdata);
2481 mock_bank
2482 .accounts_map
2483 .insert(programdata2, programdata2_account);
2484
2485 let mut next_size = 1;
2486 let mut make_account = |pubkey, owner, executable| {
2487 let size = next_size;
2488 let account = AccountSharedData::create(
2489 LAMPORTS_PER_SOL,
2490 vec![0; size],
2491 owner,
2492 executable,
2493 u64::MAX,
2494 );
2495
2496 mock_bank.accounts_map.insert(pubkey, account.clone());
2497
2498 next_size *= 4;
2501
2502 (size as u32, account)
2503 };
2504
2505 let fee_payer_keypair = Keypair::new();
2506 let fee_payer = fee_payer_keypair.pubkey();
2507 let (fee_payer_size, fee_payer_account) =
2508 make_account(fee_payer, system_program::id(), false);
2509
2510 let account1 = Pubkey::new_unique();
2511 let (account1_size, _) = make_account(account1, program1, false);
2512
2513 let account2 = Pubkey::new_unique();
2514 let (account2_size, _) = make_account(account2, program2, false);
2515
2516 let (native_loader_size, _) = make_account(native_loader::id(), native_loader::id(), true);
2517 let (bpf_loader_size, _) = make_account(loader_v2, native_loader::id(), true);
2518 let (upgradeable_loader_size, _) = make_account(loader_v3, native_loader::id(), true);
2519
2520 let (program1_size, _) = make_account(program1, loader_v2, true);
2521
2522 let mut program_accounts = HashMap::new();
2523 program_accounts.insert(program1, (&loader_v2, 0));
2524 program_accounts.insert(program2, (&loader_v3, 0));
2525 let test_transaction_data_size = |transaction, expected_size| {
2526 let mut account_loader = AccountLoader::new_with_loaded_accounts_capacity(
2527 None,
2528 &mock_bank,
2529 &mock_bank.feature_set,
2530 0,
2531 );
2532
2533 let loaded_transaction_accounts = load_transaction_accounts(
2534 &mut account_loader,
2535 &transaction,
2536 LoadedTransactionAccount {
2537 account: fee_payer_account.clone(),
2538 loaded_size: fee_payer_size as usize,
2539 },
2540 MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES,
2541 &mut TransactionErrorMetrics::default(),
2542 &Rent::default(),
2543 )
2544 .unwrap();
2545
2546 assert_eq!(
2547 loaded_transaction_accounts.loaded_accounts_data_size,
2548 expected_size
2549 );
2550 };
2551
2552 let test_data_size = |instructions: Vec<_>, expected_size| {
2553 let transaction = SanitizedTransaction::from_transaction_for_tests(
2554 Transaction::new_signed_with_payer(
2555 &instructions,
2556 Some(&fee_payer),
2557 &[&fee_payer_keypair],
2558 Hash::default(),
2559 ),
2560 );
2561
2562 test_transaction_data_size(transaction, expected_size)
2563 };
2564
2565 for account_meta in [AccountMeta::new, AccountMeta::new_readonly] {
2566 let ixns = vec![Instruction::new_with_bytes(program1, &[], vec![])];
2568 test_data_size(ixns, program1_size + bpf_loader_size + fee_payer_size);
2569
2570 let ixns = vec![
2572 Instruction::new_with_bytes(program1, &[], vec![account_meta(account1, false)]),
2573 Instruction::new_with_bytes(program2, &[], vec![account_meta(account2, false)]),
2574 ];
2575 test_data_size(
2576 ixns,
2577 account1_size
2578 + account2_size
2579 + program1_size
2580 + program2_size
2581 + bpf_loader_size
2582 + upgradeable_loader_size
2583 + fee_payer_size,
2584 );
2585
2586 let ixns = vec![
2588 Instruction::new_with_bytes(program2, &[], vec![]),
2589 Instruction::new_with_bytes(program2, &[], vec![]),
2590 ];
2591 test_data_size(
2592 ixns,
2593 program2_size + upgradeable_loader_size + fee_payer_size,
2594 );
2595
2596 let ixns = vec![Instruction::new_with_bytes(bpf_loader::id(), &[], vec![])];
2598 test_data_size(ixns, bpf_loader_size + fee_payer_size);
2599
2600 let ixns = vec![Instruction::new_with_bytes(
2602 bpf_loader::id(),
2603 &[],
2604 vec![account_meta(native_loader::id(), false)],
2605 )];
2606 test_data_size(ixns, bpf_loader_size + native_loader_size + fee_payer_size);
2607
2608 let ixns = vec![Instruction::new_with_bytes(
2610 native_loader::id(),
2611 &[],
2612 vec![],
2613 )];
2614 test_data_size(ixns, native_loader_size + fee_payer_size);
2615
2616 let ixns = vec![Instruction::new_with_bytes(
2618 native_loader::id(),
2619 &[],
2620 vec![account_meta(native_loader::id(), false)],
2621 )];
2622 test_data_size(ixns, native_loader_size + fee_payer_size);
2623
2624 let ixns = vec![Instruction::new_with_bytes(
2626 program2,
2627 &[],
2628 vec![account_meta(bpf_loader_upgradeable::id(), false)],
2629 )];
2630 test_data_size(
2631 ixns,
2632 upgradeable_loader_size * 2 + program2_size + fee_payer_size,
2633 );
2634
2635 let ixns = vec![
2637 Instruction::new_with_bytes(bpf_loader_upgradeable::id(), &[], vec![]),
2638 Instruction::new_with_bytes(program2, &[], vec![]),
2639 ];
2640 test_data_size(
2641 ixns,
2642 upgradeable_loader_size * 2 + program2_size + fee_payer_size,
2643 );
2644
2645 let ixns = vec![
2647 Instruction::new_with_bytes(
2648 program1,
2649 &[],
2650 vec![
2651 account_meta(bpf_loader::id(), false),
2652 account_meta(bpf_loader_upgradeable::id(), false),
2653 ],
2654 ),
2655 Instruction::new_with_bytes(program2, &[], vec![account_meta(account1, false)]),
2656 Instruction::new_with_bytes(
2657 bpf_loader_upgradeable::id(),
2658 &[],
2659 vec![account_meta(account1, false)],
2660 ),
2661 ];
2662 test_data_size(
2663 ixns,
2664 account1_size
2665 + program1_size
2666 + program2_size
2667 + bpf_loader_size * 2
2668 + upgradeable_loader_size * 2
2669 + fee_payer_size,
2670 );
2671
2672 let ixns = vec![Instruction::new_with_bytes(
2674 program1,
2675 &[],
2676 vec![account_meta(fee_payer, false)],
2677 )];
2678 test_data_size(ixns, program1_size + bpf_loader_size + fee_payer_size);
2679
2680 let ixns = vec![Instruction::new_with_bytes(
2682 program2,
2683 &[],
2684 vec![account_meta(programdata2, false)],
2685 )];
2686 test_data_size(
2687 ixns,
2688 program2_size + programdata2_size + upgradeable_loader_size + fee_payer_size,
2689 );
2690
2691 let ixns = vec![Instruction::new_with_bytes(
2693 program2,
2694 &[],
2695 vec![
2696 account_meta(program2, false),
2697 account_meta(programdata2, false),
2698 ],
2699 )];
2700 test_data_size(
2701 ixns,
2702 program2_size + programdata2_size + upgradeable_loader_size + fee_payer_size,
2703 );
2704 }
2705 }
2706
2707 #[test]
2708 fn test_account_loader_wrappers() {
2709 let fee_payer = Pubkey::new_unique();
2710 let mut fee_payer_account = AccountSharedData::default();
2711 fee_payer_account.set_rent_epoch(u64::MAX);
2712 fee_payer_account.set_lamports(5000);
2713
2714 let mut mock_bank = TestCallbacks::default();
2715 mock_bank
2716 .accounts_map
2717 .insert(fee_payer, fee_payer_account.clone());
2718
2719 let mut account_loader: AccountLoader<_> = (&mock_bank).into();
2721 assert_eq!(
2722 account_loader
2723 .load_transaction_account(&fee_payer, false)
2724 .unwrap()
2725 .account,
2726 fee_payer_account
2727 );
2728
2729 let mut account_loader: AccountLoader<_> = (&mock_bank).into();
2730 assert_eq!(
2731 account_loader
2732 .load_transaction_account(&fee_payer, true)
2733 .unwrap()
2734 .account,
2735 fee_payer_account
2736 );
2737
2738 let mut account_loader: AccountLoader<_> = (&mock_bank).into();
2739 assert_eq!(
2740 account_loader.load_account(&fee_payer).unwrap(),
2741 fee_payer_account
2742 );
2743
2744 let account_loader: AccountLoader<_> = (&mock_bank).into();
2745 assert_eq!(
2746 account_loader
2747 .get_account_shared_data(&fee_payer)
2748 .unwrap()
2749 .0,
2750 fee_payer_account
2751 );
2752
2753 let mut account_loader: AccountLoader<_> = (&mock_bank).into();
2755 account_loader.load_account(&fee_payer).unwrap();
2756
2757 assert_eq!(
2758 account_loader
2759 .load_transaction_account(&fee_payer, false)
2760 .unwrap()
2761 .account,
2762 fee_payer_account
2763 );
2764 assert_eq!(
2765 account_loader
2766 .load_transaction_account(&fee_payer, true)
2767 .unwrap()
2768 .account,
2769 fee_payer_account
2770 );
2771 assert_eq!(
2772 account_loader.load_account(&fee_payer).unwrap(),
2773 fee_payer_account
2774 );
2775 assert_eq!(
2776 account_loader
2777 .get_account_shared_data(&fee_payer)
2778 .unwrap()
2779 .0,
2780 fee_payer_account
2781 );
2782
2783 fee_payer_account.set_lamports(0);
2785 account_loader.update_accounts_for_failed_tx(&RollbackAccounts::FeePayerOnly {
2786 fee_payer: (fee_payer, fee_payer_account),
2787 });
2788
2789 assert_eq!(
2790 account_loader.load_transaction_account(&fee_payer, false),
2791 None
2792 );
2793 assert_eq!(
2794 account_loader.load_transaction_account(&fee_payer, true),
2795 None
2796 );
2797 assert_eq!(account_loader.load_account(&fee_payer), None);
2798 assert_eq!(account_loader.get_account_shared_data(&fee_payer), None);
2799 }
2800
2801 #[test]
2804 fn test_load_transaction_accounts_data_sizes_simd186() {
2805 let mut rng = rand0_7::thread_rng();
2806 let mut mock_bank = TestCallbacks::default();
2807
2808 for _ in 0..128 {
2810 let account = AccountSharedData::create(
2811 1,
2812 vec![0; rng.gen_range(0, 128)],
2813 Pubkey::new_unique(),
2814 rng.gen(),
2815 u64::MAX,
2816 );
2817 mock_bank.accounts_map.insert(Pubkey::new_unique(), account);
2818 }
2819
2820 let mut fee_payers = vec![];
2822 for _ in 0..8 {
2823 let fee_payer = Pubkey::new_unique();
2824 let account = AccountSharedData::create(
2825 LAMPORTS_PER_SOL,
2826 vec![0; rng.gen_range(0, 32)],
2827 system_program::id(),
2828 rng.gen(),
2829 u64::MAX,
2830 );
2831 mock_bank.accounts_map.insert(fee_payer, account);
2832 fee_payers.push(fee_payer);
2833 }
2834
2835 let mut loader_owned_accounts = vec![];
2837 let mut programdata_tracker = AHashMap::new();
2838 for loader in PROGRAM_OWNERS {
2839 for _ in 0..16 {
2840 let program_id = Pubkey::new_unique();
2841 let mut account = AccountSharedData::create(
2842 1,
2843 vec![0; rng.gen_range(0, 512)],
2844 *loader,
2845 rng.gen(),
2846 u64::MAX,
2847 );
2848
2849 if *loader == bpf_loader_upgradeable::id() && account.data().len() >= 64 {
2855 let programdata_address = Pubkey::new_unique();
2856 let has_programdata = rng.gen();
2857
2858 if has_programdata {
2859 let programdata_account = AccountSharedData::create(
2860 1,
2861 vec![0; rng.gen_range(0, 512)],
2862 *loader,
2863 rng.gen(),
2864 u64::MAX,
2865 );
2866 programdata_tracker.insert(
2867 program_id,
2868 (programdata_address, programdata_account.data().len()),
2869 );
2870 mock_bank
2871 .accounts_map
2872 .insert(programdata_address, programdata_account);
2873 loader_owned_accounts.push(programdata_address);
2874 }
2875
2876 if has_programdata || rng.gen() {
2877 account
2878 .set_state(&UpgradeableLoaderState::Program {
2879 programdata_address,
2880 })
2881 .unwrap();
2882 }
2883 }
2884
2885 mock_bank.accounts_map.insert(program_id, account);
2886 loader_owned_accounts.push(program_id);
2887 }
2888 }
2889
2890 let mut all_accounts = mock_bank.accounts_map.keys().copied().collect::<Vec<_>>();
2891
2892 for _ in 0..32 {
2895 all_accounts.push(Pubkey::new_unique());
2896 }
2897
2898 let mut account_loader = (&mock_bank).into();
2899
2900 for _ in 0..1024 {
2911 let mut instructions = vec![];
2912 for _ in 0..rng.gen_range(1, 8) {
2913 let mut accounts = vec![];
2914 for _ in 0..rng.gen_range(1, 16) {
2915 all_accounts.shuffle(&mut rng);
2916 let pubkey = all_accounts[0];
2917
2918 accounts.push(AccountMeta {
2919 pubkey,
2920 is_writable: rng.gen(),
2921 is_signer: rng.gen() && rng.gen(),
2922 });
2923 }
2924
2925 loader_owned_accounts.shuffle(&mut rng);
2926 let program_id = loader_owned_accounts[0];
2927 instructions.push(Instruction {
2928 accounts,
2929 program_id,
2930 data: vec![],
2931 });
2932 }
2933
2934 fee_payers.shuffle(&mut rng);
2935 let fee_payer = fee_payers[0];
2936 let fee_payer_account = mock_bank.accounts_map.get(&fee_payer).cloned().unwrap();
2937
2938 let transaction = SanitizedTransaction::from_transaction_for_tests(
2939 Transaction::new_with_payer(&instructions, Some(&fee_payer)),
2940 );
2941
2942 let mut expected_size = 0;
2943 let mut counted_programdatas = transaction
2944 .account_keys()
2945 .iter()
2946 .copied()
2947 .collect::<AHashSet<_>>();
2948
2949 for pubkey in transaction.account_keys().iter() {
2950 if let Some(account) = mock_bank.accounts_map.get(pubkey) {
2951 expected_size += TRANSACTION_ACCOUNT_BASE_SIZE + account.data().len();
2952 };
2953
2954 if let Some((programdata_address, programdata_size)) =
2955 programdata_tracker.get(pubkey)
2956 {
2957 if counted_programdatas.get(programdata_address).is_none() {
2958 expected_size += TRANSACTION_ACCOUNT_BASE_SIZE + programdata_size;
2959 counted_programdatas.insert(*programdata_address);
2960 }
2961 }
2962 }
2963
2964 assert!(expected_size <= MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES.get() as usize);
2965
2966 let loaded_transaction_accounts = load_transaction_accounts(
2967 &mut account_loader,
2968 &transaction,
2969 LoadedTransactionAccount {
2970 loaded_size: TRANSACTION_ACCOUNT_BASE_SIZE + fee_payer_account.data().len(),
2971 account: fee_payer_account,
2972 },
2973 MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES,
2974 &mut TransactionErrorMetrics::default(),
2975 &Rent::default(),
2976 )
2977 .unwrap();
2978
2979 assert_eq!(
2980 loaded_transaction_accounts.loaded_accounts_data_size,
2981 expected_size as u32,
2982 );
2983 }
2984 }
2985
2986 #[test]
2987 fn test_loader_aliasing() {
2988 let mut mock_bank = TestCallbacks::default();
2989
2990 let hit_address = Pubkey::new_unique();
2991 let miss_address = Pubkey::new_unique();
2992
2993 let expected_hit_account = AccountSharedData::default();
2994 mock_bank
2995 .accounts_map
2996 .insert(hit_address, expected_hit_account.clone());
2997
2998 let mut account_loader: AccountLoader<_> = (&mock_bank).into();
2999
3000 account_loader.load_account(&hit_address);
3002 let actual_hit_account = account_loader.loaded_accounts.get(&hit_address);
3003
3004 assert_eq!(actual_hit_account, Some(&expected_hit_account));
3005 assert!(Arc::ptr_eq(
3006 &actual_hit_account.unwrap().data_clone(),
3007 &expected_hit_account.data_clone()
3008 ));
3009
3010 account_loader.load_account(&hit_address);
3012 let actual_hit_account = account_loader.loaded_accounts.get(&hit_address);
3013
3014 assert_eq!(actual_hit_account, Some(&expected_hit_account));
3015 assert!(Arc::ptr_eq(
3016 &actual_hit_account.unwrap().data_clone(),
3017 &expected_hit_account.data_clone()
3018 ));
3019
3020 account_loader.load_account(&miss_address);
3022 let expected_miss_account = account_loader
3023 .loaded_accounts
3024 .get(&miss_address)
3025 .unwrap()
3026 .clone();
3027
3028 assert!(!Arc::ptr_eq(
3029 &expected_miss_account.data_clone(),
3030 &expected_hit_account.data_clone()
3031 ));
3032
3033 account_loader.load_account(&miss_address);
3035 let actual_miss_account = account_loader.loaded_accounts.get(&miss_address);
3036
3037 assert_eq!(actual_miss_account, Some(&expected_miss_account));
3038 assert!(Arc::ptr_eq(
3039 &actual_miss_account.unwrap().data_clone(),
3040 &expected_miss_account.data_clone()
3041 ));
3042 }
3043}