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