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