solana_svm/
account_loader.rs

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// Per SIMD-0186, all accounts are assigned a base size of 64 bytes to cover
44// the storage cost of metadata.
45#[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
46pub(crate) const TRANSACTION_ACCOUNT_BASE_SIZE: usize = 64;
47
48// Per SIMD-0186, resolved address lookup tables are assigned a base size of 8248
49// bytes: 8192 bytes for the maximum table size plus 56 bytes for metadata.
50const ADDRESS_LOOKUP_TABLE_BASE_SIZE: usize = 8248;
51
52// for the load instructions
53pub type TransactionCheckResult = Result<CheckedTransactionDetails>;
54type TransactionValidationResult = Result<ValidatedTransactionDetails>;
55
56#[derive(PartialEq, Eq, Debug)]
57pub(crate) enum TransactionLoadResult {
58    /// All transaction accounts were loaded successfully
59    Loaded(LoadedTransaction),
60    /// Some transaction accounts needed for execution were unable to be loaded
61    /// but the fee payer and any nonce account needed for fee collection were
62    /// loaded successfully
63    FeesOnly(FeesOnlyTransaction),
64    /// Some transaction accounts needed for fee collection were unable to be
65    /// loaded
66    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// This is an internal SVM type that tracks account changes throughout a
156// transaction batch and obviates the need to load accounts from accounts-db
157// more than once. It effectively wraps an `impl TransactionProcessingCallback`
158// type, and itself implements `TransactionProcessingCallback`, behaving
159// exactly like the implementor of the trait, but also returning up-to-date
160// account states mid-batch.
161#[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    // create a new AccountLoader for the transaction batch
170    #[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        // SlotHistory may be overridden for simulation.
180        // No other uses of AccountOverrides are expected.
181        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    // Load an account either from our own store or accounts-db and inspect it on behalf of Bank.
195    // Inspection is required prior to any modifications to the account. This function is used
196    // by load_transaction() and validate_transaction_fee_payer() for that purpose. It returns
197    // a different type than other AccountLoader load functions, which should prevent accidental
198    // mix and match of them.
199    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        // Inspect prior to collecting rent, since rent collection can modify
213        // the account.
214        //
215        // Note that though rent collection is disabled, we still set the rent
216        // epoch of rent exempt if the account is rent-exempt but its rent epoch
217        // is not set to u64::MAX. In other words, an account can be updated
218        // during rent collection. Therefore, we must inspect prior to collecting rent.
219        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    // Load an account as above, with no inspection and no LoadedTransactionAccount wrapper.
236    // This is a general purpose function suitable for usage outside initial transaction loading.
237    pub(crate) fn load_account(&mut self, account_key: &Pubkey) -> Option<AccountSharedData> {
238        match self.do_load(account_key) {
239            // Exists, from AccountLoader.
240            (Some(account), false) => Some(account),
241            // Not allocated, but has an AccountLoader placeholder already.
242            (None, false) => None,
243            // Exists in accounts-db. Store it in AccountLoader for future loads.
244            (Some(account), true) => {
245                self.loaded_accounts.insert(*account_key, account.clone());
246                Some(account)
247            }
248            // Does not exist and has never been seen.
249            (None, true) => {
250                self.loaded_accounts
251                    .insert(*account_key, AccountSharedData::default());
252                None
253            }
254        }
255    }
256
257    // Internal helper for core loading logic to prevent code duplication. Returns a bool
258    // indicating whether an accounts-db lookup was performed, which allows wrappers with
259    // &mut self to insert the account. Wrappers with &self ignore it.
260    fn do_load(&self, account_key: &Pubkey) -> (Option<AccountSharedData>, bool) {
261        if let Some(account) = self.loaded_accounts.get(account_key) {
262            // If lamports is 0, a previous transaction deallocated this account.
263            // We return None instead of the account we found so it can be created fresh.
264            // We *never* remove accounts, or else we would fetch stale state from accounts-db.
265            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            // Accounts that are invoked and also not passed as an instruction
314            // account to a program don't need to be stored because it's assumed
315            // to be impossible for a committable transaction to modify an
316            // invoked account if said account isn't passed to some program.
317            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
326// Program loaders and parsers require a type that impls TransactionProcessingCallback,
327// because they are used in both SVM and by Bank. We impl it, with the consequence
328// that if we fall back to accounts-db, we cannot store the state for future loads.
329// In general, most accounts we load this way should already be in our accounts store.
330// Once SIMD-0186 is implemented, 100% of accounts will be.
331impl<CB: TransactionProcessingCallback> TransactionProcessingCallback for AccountLoader<'_, CB> {
332    fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option<(AccountSharedData, Slot)> {
333        // The returned last-modification-slot is a dummy value for now,
334        // but will later be used in IndexImplementation::V2 of the global program cache.
335        self.do_load(pubkey).0.map(|account| (account, 0))
336    }
337}
338
339// NOTE this is a required subtrait of TransactionProcessingCallback.
340// It may make sense to break out a second subtrait just for the above two functions,
341// but this would be a nontrivial breaking change and require careful consideration.
342impl<CB: TransactionProcessingCallback> solana_svm_callback::InvokeContextCallback
343    for AccountLoader<'_, CB>
344{
345}
346
347/// Set the rent epoch to u64::MAX if the account is rent exempt.
348///
349/// TODO: This function is used to update the rent epoch of an account. Once we
350/// completely switched to lthash, where rent_epoch is ignored in accounts
351/// hashing, we can remove this function.
352pub fn update_rent_exempt_status_for_account(rent: &Rent, account: &mut AccountSharedData) {
353    // Now that rent fee collection is disabled, we won't collect rent for any
354    // account. If there are any rent paying accounts, their `rent_epoch` won't
355    // change either. However, if the account itself is rent-exempted but its
356    // `rent_epoch` is not u64::MAX, we will set its `rent_epoch` to u64::MAX.
357    // In such case, the behavior stays the same as before.
358    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
365/// Check whether the payer_account is capable of paying the fee. The
366/// side effect is to subtract the fee amount from the payer_account
367/// balance of lamports. If the payer_acount is not able to pay the
368/// fee, the error_metrics is incremented, and a specific error is
369/// returned.
370pub 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            // Should we ever allow a fees charge to zero a nonce account's
390            // balance. The state MUST be set to uninitialized in that case
391            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    // Transactions pay a base fee per address lookup table.
538    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            // This has been annotated branch-by-branch because collapsing the logic is infeasible.
560            // Its purpose is to ensure programdata accounts are counted once and *only* once per
561            // transaction. By checking account_keys, we never double-count a programdata account
562            // that was explictly included in the transaction. We also use a hashset to gracefully
563            // handle cases that LoaderV3 presumably makes impossible, such as self-referential
564            // program accounts or multiply-referenced programdata accounts, for added safety.
565            //
566            // If in the future LoaderV3 programs are migrated to LoaderV4, this entire code block
567            // can be deleted.
568            //
569            // If this is a valid LoaderV3 program...
570            if bpf_loader_upgradeable::check_id(account.owner()) {
571                if let Ok(UpgradeableLoaderState::Program {
572                    programdata_address,
573                }) = account.state()
574                {
575                    // ...its programdata was not already counted and will not later be counted...
576                    if !account_keys.iter().any(|key| programdata_address == *key)
577                        && !additional_loaded_accounts.contains(&programdata_address)
578                    {
579                        // ...and the programdata account exists (if it doesn't, it is *not* a load failure)...
580                        if let Some(programdata_account) =
581                            account_loader.load_account(&programdata_address)
582                        {
583                            // ...count programdata toward this transaction's total size.
584                            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    // Since the fee payer is always the first account, collect it first.
602    // We can use it directly because it was already loaded during validation.
603    collect_loaded_account(
604        account_loader,
605        message.fee_payer(),
606        loaded_fee_payer_account,
607    )?;
608
609    // Attempt to load and collect remaining non-fee payer accounts.
610    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    // Since the fee payer is always the first account, collect it first.
667    // We can use it directly because it was already loaded during validation.
668    collect_loaded_account(message.fee_payer(), loaded_fee_payer_account)?;
669
670    // Attempt to load and collect remaining non-fee payer accounts
671    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                // Just as with an empty vector, trying to borrow the program account will fail
682                // with a u16::MAX
683                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        // Since the instructions sysvar is constructed by the SVM and modified
737        // for each transaction instruction, it cannot be loaded.
738        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
761/// Accumulate loaded account data size into `accumulated_accounts_data_size`.
762/// Returns TransactionErr::MaxLoadedAccountsDataSizeExceeded if
763/// `accumulated_accounts_data_size` exceeds
764/// `requested_loaded_accounts_data_size_limit`.
765fn 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>, /* is_writable */ 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        // OK - loaded data size is up to limit
1341        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        // fail - loading more data that would exceed limit
1351        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        // If payer account has sufficient balance, expect successful fee deduction,
1405        // regardless feature gate status, or if payer is nonce account.
1406        {
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        // If payer account has no balance, expected AccountNotFound Error
1422        // regardless feature gate status, or if payer is nonce account.
1423        {
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        // If payer account has insufficient balance, expect InsufficientFundsForFee error
1439        // regardless feature gate status, or if payer is nonce account.
1440        {
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        // normal payer account has balance of u64::MAX, so does fee; since it does not  require
1456        // min_balance, expect successful fee deduction, regardless of feature gate status
1457        {
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        // nonce payer account has balance of u64::MAX, so does fee; due to nonce account
1479        // requires additional min_balance, expect InsufficientFundsForFee error if feature gate is
1480        // enabled
1481        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    // Ensure `TransactionProcessingCallback::inspect_account()` is called when
2346    // loading accounts for transaction processing.
2347    #[test]
2348    fn test_inspect_account_non_fee_payer() {
2349        let mut mock_bank = TestCallbacks::default();
2350
2351        let address0 = Pubkey::new_unique(); // <-- fee payer
2352        let address1 = Pubkey::new_unique(); // <-- initially alive
2353        let address2 = Pubkey::new_unique(); // <-- initially dead
2354        let address3 = Pubkey::new_unique(); // <-- program
2355
2356        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        // account2 *not* added to the bank's accounts_map
2365
2366        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        // ensure the loaded accounts are inspected
2417        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            // *not* key0, since it is loaded during fee payer validation
2427            (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            // accounts are counted at most twice
2495            // by multiplying account size by 4, we ensure all totals are unique
2496            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            // one program plus loader
2563            let ixns = vec![Instruction::new_with_bytes(program1, &[], vec![])];
2564            test_data_size(ixns, program1_size + bpf_loader_size + fee_payer_size);
2565
2566            // two programs, two loaders, two accounts
2567            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            // program and loader counted once
2583            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            // native loader not counted if loader
2593            let ixns = vec![Instruction::new_with_bytes(bpf_loader::id(), &[], vec![])];
2594            test_data_size(ixns, bpf_loader_size + fee_payer_size);
2595
2596            // native loader counted if instruction
2597            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            // native loader counted if invoked
2605            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            // native loader counted once if invoked and instruction
2613            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            // loader counted twice if included in instruction
2621            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            // loader counted twice even if included first
2632            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            // cover that case with multiple loaders to be sure
2642            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            // fee-payer counted once
2669            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            // programdata as instruction account counts it once
2677            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            // program and programdata as instruction accounts behaves the same
2688            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        // test without stored account
2716        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        // test with stored account
2750        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        // drop the account and ensure all deliver the updated state
2780        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    // note all magic numbers (how many accounts, how many instructions, how big to size buffers) are arbitrary
2798    // other than trying not to swamp programs with blank accounts and keep transaction size below the 64mb limit
2799    #[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        // arbitrary accounts
2805        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        // fee-payers
2817        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        // programs
2832        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                // give half loaderv3 accounts (if theyre long enough) a valid programdata
2846                // a quarter a dead pointer and a quarter nothing
2847                // we set executable like a program because after the flag is disabled...
2848                // ...programdata and buffer accounts can be used as program ids without aborting loading
2849                // this will always fail at execution but we are merely testing the data size accounting here
2850                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        // append some to-be-created accounts
2889        // this is to test that their size is 0 rather than 64
2890        for _ in 0..32 {
2891            all_accounts.push(Pubkey::new_unique());
2892        }
2893
2894        let mut account_loader = (&mock_bank).into();
2895
2896        // now generate arbitrary transactions using this accounts
2897        // we ensure valid fee-payers and that all program ids are loader-owned
2898        // otherwise any account can appear anywhere
2899        // some edge cases we hope to hit (not necessarily all in every run):
2900        // * programs used multiple times as program ids and/or normal accounts are counted once
2901        // * loaderv3 programdata used explicitly zero one or multiple times is counted once
2902        // * loaderv3 programs with missing programdata are allowed through
2903        // * loaderv3 programdata used as program id does nothing weird
2904        // * loaderv3 programdata used as a regular account does nothing weird
2905        // * the programdata conditions hold regardless of ordering
2906        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        // load hits accounts-db, same account is stored
2997        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        // reload doesnt affect this
3007        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        // load misses accounts-db, placeholder is inserted
3017        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        // reload keeps the same placeholder
3030        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}