Skip to main content

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        rent_calculator::{RENT_EXEMPT_RENT_EPOCH, check_static_account_rent_state_transition},
7        rollback_accounts::RollbackAccounts,
8        transaction_error_metrics::TransactionErrorMetrics,
9    },
10    ahash::{AHashMap, AHashSet},
11    solana_account::{Account, AccountSharedData, ReadableAccount, WritableAccount},
12    solana_clock::Slot,
13    solana_fee_structure::FeeDetails,
14    solana_instruction::{BorrowedAccountMeta, BorrowedInstruction},
15    solana_instructions_sysvar::construct_instructions_data,
16    solana_loader_v3_interface::state::UpgradeableLoaderState,
17    solana_nonce::state::State as NonceState,
18    solana_nonce_account::{SystemAccountKind, get_system_account_kind},
19    solana_program_runtime::execution_budget::{
20        SVMTransactionExecutionAndFeeBudgetLimits, SVMTransactionExecutionBudget,
21    },
22    solana_pubkey::Pubkey,
23    solana_rent::Rent,
24    solana_sdk_ids::{
25        bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, loader_v4, native_loader,
26        sysvar::{self, slot_history},
27    },
28    solana_svm_callback::{AccountState, TransactionProcessingCallback},
29    solana_svm_feature_set::SVMFeatureSet,
30    solana_svm_transaction::svm_message::SVMMessage,
31    solana_transaction_context::{IndexOfAccount, transaction_accounts::KeyedAccountSharedData},
32    solana_transaction_error::{TransactionError, TransactionResult as Result},
33};
34
35// Per SIMD-0186, all accounts are assigned a base size of 64 bytes to cover
36// the storage cost of metadata.
37#[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
38pub(crate) const TRANSACTION_ACCOUNT_BASE_SIZE: usize = 64;
39
40// Valid program owners (loaders).
41pub const PROGRAM_OWNERS: &[Pubkey] = &[
42    bpf_loader_upgradeable::id(),
43    bpf_loader::id(),
44    bpf_loader_deprecated::id(),
45    loader_v4::id(),
46];
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// Result of pre-SVM (runtime) transaction checks.
53pub type TransactionCheckResult = Result<CheckedTransactionDetails>;
54
55// Combined result of runtime, fee-payer, and nonce checks.
56#[allow(clippy::large_enum_variant)]
57#[derive(PartialEq, Eq, Debug, Clone)]
58pub(crate) enum TransactionValidationResult {
59    Loadable(ValidatedTransactionDetails),
60    NoOp(NoOpTransaction),
61    Unprocessable(TransactionError),
62}
63
64#[derive(PartialEq, Eq, Debug)]
65pub(crate) enum TransactionLoadResult {
66    /// All transaction accounts were loaded successfully.
67    Loaded(LoadedTransaction),
68    /// Some transaction accounts needed for execution were unable to be loaded
69    /// but the fee payer and any nonce account needed for fee collection were
70    /// loaded successfully.
71    FeesOnly(FeesOnlyTransaction),
72    /// This transaction will be processed but entail no account state changes.
73    /// With SIMD-0290 enabled, invalid fee-payers for blockhash transactions are processable.
74    /// With SIMD-0297 enabled, invalid nonce accounts are processable.
75    NoOp(NoOpTransaction),
76    /// Mandatory checks failed; this transaction will be discarded as unprocessable.
77    Unprocessable(TransactionError),
78}
79
80#[derive(PartialEq, Eq, Debug, Clone)]
81#[cfg_attr(
82    feature = "svm-internal",
83    qualifier_attr::field_qualifiers(nonce_address(pub))
84)]
85pub struct CheckedTransactionDetails {
86    pub(crate) nonce_address: Option<Pubkey>,
87    pub(crate) compute_budget_and_limits: SVMTransactionExecutionAndFeeBudgetLimits,
88}
89
90#[cfg(feature = "dev-context-only-utils")]
91impl Default for CheckedTransactionDetails {
92    fn default() -> Self {
93        Self {
94            nonce_address: None,
95            compute_budget_and_limits: SVMTransactionExecutionAndFeeBudgetLimits {
96                budget: SVMTransactionExecutionBudget::default(),
97                loaded_accounts_data_size_limit: 32,
98                fee_details: FeeDetails::default(),
99            },
100        }
101    }
102}
103
104impl CheckedTransactionDetails {
105    pub fn new(
106        nonce_address: Option<Pubkey>,
107        compute_budget_and_limits: SVMTransactionExecutionAndFeeBudgetLimits,
108    ) -> Self {
109        Self {
110            nonce_address,
111            compute_budget_and_limits,
112        }
113    }
114}
115
116#[derive(PartialEq, Eq, Debug, Clone)]
117pub(crate) struct ValidatedTransactionDetails {
118    pub(crate) rollback_accounts: RollbackAccounts,
119    pub(crate) compute_budget: SVMTransactionExecutionBudget,
120    pub(crate) loaded_accounts_bytes_limit: u32,
121    pub(crate) fee_details: FeeDetails,
122    pub(crate) loaded_fee_payer_account: LoadedTransactionAccount,
123}
124
125#[cfg(feature = "dev-context-only-utils")]
126impl Default for ValidatedTransactionDetails {
127    fn default() -> Self {
128        Self {
129            rollback_accounts: RollbackAccounts::default(),
130            compute_budget: SVMTransactionExecutionBudget::default(),
131            loaded_accounts_bytes_limit:
132                solana_program_runtime::execution_budget::MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES.get(),
133            fee_details: FeeDetails::default(),
134            loaded_fee_payer_account: LoadedTransactionAccount::default(),
135        }
136    }
137}
138
139#[derive(PartialEq, Eq, Debug, Clone)]
140#[cfg_attr(feature = "dev-context-only-utils", derive(Default))]
141pub(crate) struct LoadedTransactionAccount {
142    pub(crate) account: AccountSharedData,
143    pub(crate) loaded_size: usize,
144}
145
146#[derive(PartialEq, Eq, Debug, Clone)]
147#[cfg_attr(feature = "dev-context-only-utils", derive(Default))]
148#[cfg_attr(
149    feature = "dev-context-only-utils",
150    field_qualifiers(compute_budget(pub))
151)]
152pub struct LoadedTransaction {
153    pub accounts: Vec<KeyedAccountSharedData>,
154    /// Parallel to `accounts`: whether each account must be written back. Empty
155    /// until execution.
156    pub touched_flags: Box<[bool]>,
157    pub fee_details: FeeDetails,
158    pub rollback_accounts: RollbackAccounts,
159    pub(crate) compute_budget: SVMTransactionExecutionBudget,
160    pub loaded_accounts_data_size: u32,
161}
162
163#[derive(PartialEq, Eq, Debug, Clone)]
164pub struct FeesOnlyTransaction {
165    pub load_error: TransactionError,
166    pub rollback_accounts: RollbackAccounts,
167    pub fee_details: FeeDetails,
168    pub loaded_accounts_data_size: u32,
169}
170
171#[derive(PartialEq, Eq, Debug, Clone)]
172pub struct NoOpTransaction {
173    pub validation_error: TransactionError,
174    pub fee_payer_balance: Option<u64>,
175    pub compute_unit_limit: u64,
176    pub loaded_accounts_bytes_limit: u32,
177}
178
179// This is an internal SVM type that tracks account changes throughout a
180// transaction batch and obviates the need to load accounts from accounts-db
181// more than once. It effectively wraps an `impl TransactionProcessingCallback`
182// type, and itself implements `TransactionProcessingCallback`, behaving
183// exactly like the implementor of the trait, but also returning up-to-date
184// account states mid-batch.
185#[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
186pub(crate) struct AccountLoader<'a, CB: TransactionProcessingCallback> {
187    loaded_accounts: AHashMap<Pubkey, (AccountSharedData, Slot)>,
188    callbacks: &'a CB,
189    pub(crate) feature_set: &'a SVMFeatureSet,
190}
191
192impl<'a, CB: TransactionProcessingCallback> AccountLoader<'a, CB> {
193    // create a new AccountLoader for the transaction batch
194    #[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
195    pub(crate) fn new_with_loaded_accounts_capacity(
196        account_overrides: Option<&'a AccountOverrides>,
197        callbacks: &'a CB,
198        feature_set: &'a SVMFeatureSet,
199        capacity: usize,
200    ) -> AccountLoader<'a, CB> {
201        let mut loaded_accounts = AHashMap::with_capacity(capacity);
202
203        // SlotHistory may be overridden for simulation.
204        // No other uses of AccountOverrides are expected.
205        if let Some(slot_history) =
206            account_overrides.and_then(|overrides| overrides.get(&slot_history::id()))
207        {
208            loaded_accounts.insert(slot_history::id(), (slot_history.clone(), 0));
209        }
210
211        Self {
212            loaded_accounts,
213            callbacks,
214            feature_set,
215        }
216    }
217
218    // Load an account either from our own store or accounts-db and inspect it on behalf of Bank.
219    // Inspection is required prior to any modifications to the account. This function is used
220    // by load_transaction() and validate_transaction_fee_payer() for that purpose. It returns
221    // a different type than other AccountLoader load functions, which should prevent accidental
222    // mix and match of them.
223    pub(crate) fn load_transaction_account(
224        &mut self,
225        account_key: &Pubkey,
226        is_writable: bool,
227    ) -> Option<LoadedTransactionAccount> {
228        let account = self.load_account(account_key);
229
230        // Inspect prior to collecting rent, since rent collection can modify
231        // the account.
232        //
233        // Note that though rent collection is disabled, we still set the rent
234        // epoch of rent exempt if the account is rent-exempt but its rent epoch
235        // is not set to u64::MAX. In other words, an account can be updated
236        // during rent collection. Therefore, we must inspect prior to collecting rent.
237        self.callbacks.inspect_account(
238            account_key,
239            if let Some(ref account) = account {
240                AccountState::Alive(account)
241            } else {
242                AccountState::Dead
243            },
244            is_writable,
245        );
246
247        account.map(|account| LoadedTransactionAccount {
248            loaded_size: TRANSACTION_ACCOUNT_BASE_SIZE.saturating_add(account.data().len()),
249            account,
250        })
251    }
252
253    // Load an account as above, with no inspection and no LoadedTransactionAccount wrapper.
254    // This is a general purpose function suitable for usage outside initial transaction loading.
255    pub(crate) fn load_account(&mut self, account_key: &Pubkey) -> Option<AccountSharedData> {
256        match self.do_load(account_key) {
257            // Exists, from AccountLoader.
258            (Some((account, _last_modification_slot)), false) => Some(account),
259            // Not allocated, but has an AccountLoader placeholder already.
260            (None, false) => None,
261            // Exists in accounts-db. Store it in AccountLoader for future loads.
262            (Some((account, last_modification_slot)), true) => {
263                self.loaded_accounts
264                    .insert(*account_key, (account.clone(), last_modification_slot));
265                Some(account)
266            }
267            // Does not exist and has never been seen.
268            (None, true) => {
269                self.loaded_accounts
270                    .insert(*account_key, (AccountSharedData::default(), 0));
271                None
272            }
273        }
274    }
275
276    // Internal helper for core loading logic to prevent code duplication. Returns a bool
277    // indicating whether an accounts-db lookup was performed, which allows wrappers with
278    // &mut self to insert the account. Wrappers with &self ignore it.
279    fn do_load(&self, account_key: &Pubkey) -> (Option<(AccountSharedData, Slot)>, bool) {
280        if let Some((account, slot)) = self.loaded_accounts.get(account_key) {
281            // If lamports is 0, a previous transaction deallocated this account.
282            // We return None instead of the account we found so it can be created fresh.
283            // We *never* remove accounts, or else we would fetch stale state from accounts-db.
284            let option_account = if account.lamports() == 0 {
285                None
286            } else {
287                Some((account.clone(), *slot))
288            };
289
290            (option_account, false)
291        } else if let Some((account, slot)) = self.callbacks.get_account_shared_data(account_key) {
292            (Some((account, slot)), true)
293        } else {
294            (None, true)
295        }
296    }
297
298    pub(crate) fn update_accounts_for_failed_tx(
299        &mut self,
300        rollback_accounts: &RollbackAccounts,
301        current_slot: Slot,
302    ) {
303        for (account_address, account) in rollback_accounts {
304            self.loaded_accounts
305                .insert(*account_address, (account.clone(), current_slot));
306        }
307    }
308
309    pub(crate) fn update_accounts_for_successful_tx(
310        &mut self,
311        message: &impl SVMMessage,
312        transaction_accounts: &[KeyedAccountSharedData],
313        touched_flags: &[bool],
314        current_slot: Slot,
315    ) {
316        for (i, (address, account)) in (0..message.account_keys().len()).zip(transaction_accounts) {
317            if !message.is_writable(i) {
318                continue;
319            }
320
321            // Skip write-locked accounts the transaction left unmodified.
322            if !touched_flags[i] {
323                continue;
324            }
325
326            // Accounts that are invoked and also not passed as an instruction
327            // account to a program don't need to be stored because it's assumed
328            // to be impossible for a committable transaction to modify an
329            // invoked account if said account isn't passed to some program.
330            if message.is_invoked(i) && !message.is_instruction_account(i) {
331                continue;
332            }
333
334            self.loaded_accounts
335                .insert(*address, (account.clone(), current_slot));
336        }
337    }
338}
339
340// Program loaders and parsers require a type that impls TransactionProcessingCallback,
341// because they are used in both SVM and by Bank. We impl it, with the consequence
342// that if we fall back to accounts-db, we cannot store the state for future loads.
343// In practice, all accounts we load this way will already be in our accounts store.
344impl<CB: TransactionProcessingCallback> TransactionProcessingCallback for AccountLoader<'_, CB> {
345    fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option<(AccountSharedData, Slot)> {
346        self.do_load(pubkey).0
347    }
348}
349
350/// Set the rent epoch to u64::MAX if the account is rent exempt.
351///
352/// TODO: This function is used to update the rent epoch of an account. Once we
353/// completely switched to lthash, where rent_epoch is ignored in accounts
354/// hashing, we can remove this function.
355pub fn update_rent_exempt_status_for_account(rent: &Rent, account: &mut AccountSharedData) {
356    // Now that rent fee collection is disabled, we won't collect rent for any
357    // account. If there are any rent paying accounts, their `rent_epoch` won't
358    // change either. However, if the account itself is rent-exempted but its
359    // `rent_epoch` is not u64::MAX, we will set its `rent_epoch` to u64::MAX.
360    // In such case, the behavior stays the same as before.
361    if account.rent_epoch() != RENT_EXEMPT_RENT_EPOCH
362        && rent.is_exempt(account.lamports(), account.data().len())
363    {
364        account.set_rent_epoch(RENT_EXEMPT_RENT_EPOCH);
365    }
366}
367
368/// Check whether the payer_account is capable of paying the fee. The
369/// side effect is to subtract the fee amount from the payer_account
370/// balance of lamports. If the payer_account is not able to pay the
371/// fee, the error_metrics is incremented, and a specific error is
372/// returned.
373pub fn validate_fee_payer(
374    payer_account: &mut AccountSharedData,
375    payer_index: IndexOfAccount,
376    error_metrics: &mut TransactionErrorMetrics,
377    rent: &Rent,
378    fee: u64,
379    relax_post_exec_min_balance_check: bool,
380) -> Result<()> {
381    if payer_account.lamports() == 0 {
382        error_metrics.account_not_found += 1;
383        return Err(TransactionError::AccountNotFound);
384    }
385    let system_account_kind = get_system_account_kind(payer_account).ok_or_else(|| {
386        error_metrics.invalid_account_for_fee += 1;
387        TransactionError::InvalidAccountForFee
388    })?;
389    let min_balance = match system_account_kind {
390        SystemAccountKind::System => 0,
391        SystemAccountKind::Nonce => {
392            // Should we ever allow a fees charge to zero a nonce account's
393            // balance. The state MUST be set to uninitialized in that case
394            rent.minimum_balance(NonceState::size())
395        }
396    };
397
398    payer_account
399        .lamports()
400        .checked_sub(min_balance)
401        .and_then(|v| v.checked_sub(fee))
402        .ok_or_else(|| {
403            error_metrics.insufficient_funds += 1;
404            TransactionError::InsufficientFundsForFee
405        })?;
406
407    let pre_balance = payer_account.lamports();
408    payer_account
409        .checked_sub_lamports(fee)
410        .map_err(|_| TransactionError::InsufficientFundsForFee)?;
411    let post_balance = payer_account.lamports();
412
413    check_static_account_rent_state_transition(
414        pre_balance,
415        post_balance,
416        payer_account.data().len(),
417        rent,
418        payer_index,
419        relax_post_exec_min_balance_check,
420    )
421}
422
423pub(crate) fn load_transaction<CB: TransactionProcessingCallback>(
424    account_loader: &mut AccountLoader<CB>,
425    message: &impl SVMMessage,
426    validation_result: TransactionValidationResult,
427    error_metrics: &mut TransactionErrorMetrics,
428    rent: &Rent,
429) -> TransactionLoadResult {
430    match validation_result {
431        TransactionValidationResult::Unprocessable(e) => TransactionLoadResult::Unprocessable(e),
432        TransactionValidationResult::NoOp(tx_details) => TransactionLoadResult::NoOp(tx_details),
433        TransactionValidationResult::Loadable(tx_details) => {
434            let mut loaded_transaction_data_size =
435                LoadedTransactionDataSize::with_max_size(tx_details.loaded_accounts_bytes_limit);
436
437            let load_result = load_transaction_accounts(
438                account_loader,
439                message,
440                tx_details.loaded_fee_payer_account,
441                &mut loaded_transaction_data_size,
442                error_metrics,
443                rent,
444            );
445
446            match load_result {
447                Ok(accounts) => TransactionLoadResult::Loaded(LoadedTransaction {
448                    accounts,
449                    // Populated after execution by execute_loaded_transaction.
450                    touched_flags: Box::default(),
451                    fee_details: tx_details.fee_details,
452                    rollback_accounts: tx_details.rollback_accounts,
453                    compute_budget: tx_details.compute_budget,
454                    loaded_accounts_data_size: loaded_transaction_data_size.into(),
455                }),
456                Err(err) => TransactionLoadResult::FeesOnly(FeesOnlyTransaction {
457                    load_error: err,
458                    fee_details: tx_details.fee_details,
459                    loaded_accounts_data_size: if account_loader
460                        .feature_set
461                        .define_ltds_fee_only_semantics
462                    {
463                        loaded_transaction_data_size.into()
464                    } else {
465                        tx_details.rollback_accounts.data_size() as u32
466                    },
467                    rollback_accounts: tx_details.rollback_accounts,
468                }),
469            }
470        }
471    }
472}
473
474#[derive(PartialEq, Eq, Debug, Clone)]
475struct LoadedTransactionDataSize {
476    loaded_accounts_data_size: u32,
477    requested_loaded_accounts_data_size_limit: u32,
478}
479
480impl LoadedTransactionDataSize {
481    fn with_max_size(requested_loaded_accounts_data_size_limit: u32) -> Self {
482        Self {
483            loaded_accounts_data_size: 0,
484            requested_loaded_accounts_data_size_limit,
485        }
486    }
487
488    fn increase_calculated_data_size(
489        &mut self,
490        data_size_delta: usize,
491        error_metrics: &mut TransactionErrorMetrics,
492    ) -> Result<()> {
493        // this branch is unreachable in practice (though not by construction),
494        // since it would imply an account >4gb in size
495        let Ok(data_size_delta) = u32::try_from(data_size_delta) else {
496            self.loaded_accounts_data_size = u32::MAX;
497            error_metrics.max_loaded_accounts_data_size_exceeded += 1;
498            return Err(TransactionError::MaxLoadedAccountsDataSizeExceeded);
499        };
500
501        self.loaded_accounts_data_size = self
502            .loaded_accounts_data_size
503            .saturating_add(data_size_delta);
504
505        if self.loaded_accounts_data_size > self.requested_loaded_accounts_data_size_limit {
506            error_metrics.max_loaded_accounts_data_size_exceeded += 1;
507            Err(TransactionError::MaxLoadedAccountsDataSizeExceeded)
508        } else {
509            Ok(())
510        }
511    }
512}
513
514impl From<LoadedTransactionDataSize> for u32 {
515    fn from(value: LoadedTransactionDataSize) -> Self {
516        value
517            .loaded_accounts_data_size
518            .min(value.requested_loaded_accounts_data_size_limit)
519    }
520}
521
522fn load_transaction_accounts<CB: TransactionProcessingCallback>(
523    account_loader: &mut AccountLoader<CB>,
524    message: &impl SVMMessage,
525    loaded_fee_payer_account: LoadedTransactionAccount,
526    loaded_tx_data_size: &mut LoadedTransactionDataSize,
527    error_metrics: &mut TransactionErrorMetrics,
528    rent: &Rent,
529) -> Result<Vec<KeyedAccountSharedData>> {
530    let account_keys = message.account_keys();
531    let mut loaded_transaction_accounts = Vec::with_capacity(account_keys.len());
532    let mut additional_loaded_accounts: AHashSet<Pubkey> = AHashSet::new();
533
534    // Transactions pay a base fee per address lookup table.
535    loaded_tx_data_size.increase_calculated_data_size(
536        message
537            .num_lookup_tables()
538            .saturating_mul(ADDRESS_LOOKUP_TABLE_BASE_SIZE),
539        error_metrics,
540    )?;
541
542    let mut collect_loaded_account =
543        |account_loader: &mut AccountLoader<CB>, key: &Pubkey, loaded_account| -> Result<()> {
544            let LoadedTransactionAccount {
545                account,
546                loaded_size,
547            } = loaded_account;
548
549            loaded_tx_data_size.increase_calculated_data_size(loaded_size, error_metrics)?;
550
551            // This has been annotated branch-by-branch because collapsing the logic is infeasible.
552            // Its purpose is to ensure programdata accounts are counted once and *only* once per
553            // transaction. By checking account_keys, we never double-count a programdata account
554            // that was explicitly included in the transaction. We also use a hashset to gracefully
555            // handle cases that LoaderV3 presumably makes impossible, such as self-referential
556            // program accounts or multiply-referenced programdata accounts, for added safety.
557            //
558            // If in the future LoaderV3 programs are migrated to LoaderV4, this entire code block
559            // can be deleted.
560            //
561            // If this is a valid LoaderV3 program...
562            if bpf_loader_upgradeable::check_id(account.owner())
563                && let Ok(UpgradeableLoaderState::Program {
564                    programdata_address,
565                }) = bincode::deserialize(account.data())
566            {
567                // ...its programdata was not already counted and will not later be counted...
568                if !account_keys.iter().any(|key| programdata_address == *key)
569                    && !additional_loaded_accounts.contains(&programdata_address)
570                {
571                    // ...and the programdata account exists (if it doesn't, it is *not* a load failure)...
572                    if let Some(programdata_account) =
573                        account_loader.load_account(&programdata_address)
574                    {
575                        // ...count programdata toward this transaction's total size.
576                        loaded_tx_data_size.increase_calculated_data_size(
577                            TRANSACTION_ACCOUNT_BASE_SIZE
578                                .saturating_add(programdata_account.data().len()),
579                            error_metrics,
580                        )?;
581                        additional_loaded_accounts.insert(programdata_address);
582                    }
583                }
584            }
585
586            loaded_transaction_accounts.push((*key, account));
587
588            Ok(())
589        };
590
591    // Since the fee payer is always the first account, collect it first.
592    // We can use it directly because it was already loaded during validation.
593    collect_loaded_account(
594        account_loader,
595        message.fee_payer(),
596        loaded_fee_payer_account,
597    )?;
598
599    // Attempt to load and collect remaining non-fee payer accounts.
600    for (account_index, account_key) in account_keys.iter().enumerate().skip(1) {
601        let loaded_account =
602            load_transaction_account(account_loader, message, account_key, account_index, rent)?;
603        collect_loaded_account(account_loader, account_key, loaded_account)?;
604    }
605
606    for (program_id, _) in message.program_instructions_iter() {
607        let Some(program_account) = account_loader.load_account(program_id) else {
608            error_metrics.account_not_found += 1;
609            return Err(TransactionError::ProgramAccountNotFound);
610        };
611
612        let owner_id = program_account.owner();
613        if !native_loader::check_id(owner_id) && !PROGRAM_OWNERS.contains(owner_id) {
614            error_metrics.invalid_program_for_execution += 1;
615            return Err(TransactionError::InvalidProgramForExecution);
616        }
617    }
618
619    Ok(loaded_transaction_accounts)
620}
621
622fn load_transaction_account<CB: TransactionProcessingCallback>(
623    account_loader: &mut AccountLoader<CB>,
624    message: &impl SVMMessage,
625    account_key: &Pubkey,
626    account_index: usize,
627    rent: &Rent,
628) -> Result<LoadedTransactionAccount> {
629    let is_writable = message.is_writable(account_index);
630    if solana_sdk_ids::sysvar::instructions::check_id(account_key) {
631        // Since the instructions sysvar is constructed by the SVM and modified
632        // for each transaction instruction, it cannot be loaded.
633        Ok(LoadedTransactionAccount {
634            loaded_size: 0,
635            account: construct_instructions_account(message)?,
636        })
637    } else if let Some(mut loaded_account) =
638        account_loader.load_transaction_account(account_key, is_writable)
639    {
640        if is_writable {
641            update_rent_exempt_status_for_account(rent, &mut loaded_account.account);
642        }
643        Ok(loaded_account)
644    } else {
645        let mut default_account = AccountSharedData::default();
646        default_account.set_rent_epoch(RENT_EXEMPT_RENT_EPOCH);
647        Ok(LoadedTransactionAccount {
648            loaded_size: default_account.data().len(),
649            account: default_account,
650        })
651    }
652}
653
654pub(crate) fn construct_instructions_account(
655    message: &impl SVMMessage,
656) -> Result<AccountSharedData> {
657    let account_keys = message.account_keys();
658    let mut decompiled_instructions = Vec::with_capacity(message.num_instructions());
659    for (program_id, instruction) in message.program_instructions_iter() {
660        let accounts = instruction
661            .accounts
662            .iter()
663            .map(|account_index| {
664                let account_index = usize::from(*account_index);
665                BorrowedAccountMeta {
666                    is_signer: message.is_signer(account_index),
667                    is_writable: message.is_writable(account_index),
668                    pubkey: account_keys.get(account_index).unwrap(),
669                }
670            })
671            .collect();
672
673        decompiled_instructions.push(BorrowedInstruction {
674            accounts,
675            data: instruction.data,
676            program_id,
677        });
678    }
679
680    Ok(AccountSharedData::from(Account {
681        data: construct_instructions_data(&decompiled_instructions)
682            .map_err(|_err| TransactionError::MaxLoadedAccountsDataSizeExceeded)?,
683        owner: sysvar::id(),
684        ..Account::default()
685    }))
686}
687
688#[cfg(test)]
689mod tests {
690    use {
691        super::*,
692        crate::transaction_account_state_info::TransactionAccountStateInfo,
693        rand::prelude::*,
694        solana_account::{Account, AccountSharedData, ReadableAccount, WritableAccount},
695        solana_hash::Hash,
696        solana_instruction::{AccountMeta, Instruction},
697        solana_keypair::Keypair,
698        solana_loader_v3_interface::state::UpgradeableLoaderState,
699        solana_message::{
700            LegacyMessage, Message, MessageHeader, SanitizedMessage,
701            compiled_instruction::CompiledInstruction,
702            v0::{LoadedAddresses, LoadedMessage},
703            v1,
704        },
705        solana_native_token::LAMPORTS_PER_SOL,
706        solana_nonce::{self as nonce, versions::Versions as NonceVersions},
707        solana_program_runtime::execution_budget::{
708            DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT, MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES,
709        },
710        solana_pubkey::Pubkey,
711        solana_rent::Rent,
712        solana_sdk_ids::{
713            bpf_loader, bpf_loader_upgradeable, native_loader, system_program, sysvar,
714        },
715        solana_signature::Signature,
716        solana_signer::Signer,
717        solana_svm_callback::TransactionProcessingCallback,
718        solana_system_transaction::transfer,
719        solana_transaction::{Transaction, sanitized::SanitizedTransaction},
720        solana_transaction_context::{
721            transaction::TransactionContext, transaction_accounts::KeyedAccountSharedData,
722        },
723        solana_transaction_error::{TransactionError, TransactionResult as Result},
724        std::{
725            borrow::Cow,
726            cell::RefCell,
727            collections::{HashMap, HashSet},
728            sync::Arc,
729        },
730    };
731
732    fn setup_test_logger() {
733        let _ = env_logger::Builder::from_env(env_logger::Env::new().default_filter_or("error"))
734            .format_timestamp_nanos()
735            .is_test(true)
736            .try_init();
737    }
738
739    #[derive(Clone)]
740    struct TestCallbacks {
741        accounts_map: HashMap<Pubkey, (AccountSharedData, Slot)>,
742        #[allow(clippy::type_complexity)]
743        inspected_accounts:
744            RefCell<HashMap<Pubkey, Vec<(Option<AccountSharedData>, /* is_writable */ bool)>>>,
745        feature_set: SVMFeatureSet,
746    }
747
748    impl Default for TestCallbacks {
749        fn default() -> Self {
750            Self {
751                accounts_map: HashMap::default(),
752                inspected_accounts: RefCell::default(),
753                feature_set: SVMFeatureSet::all_enabled(),
754            }
755        }
756    }
757
758    impl TransactionProcessingCallback for TestCallbacks {
759        fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option<(AccountSharedData, Slot)> {
760            self.accounts_map
761                .get(pubkey)
762                .map(|(account, slot)| (account.clone(), *slot))
763        }
764
765        fn inspect_account(
766            &self,
767            address: &Pubkey,
768            account_state: AccountState,
769            is_writable: bool,
770        ) {
771            let account = match account_state {
772                AccountState::Dead => None,
773                AccountState::Alive(account) => Some(account.clone()),
774            };
775            self.inspected_accounts
776                .borrow_mut()
777                .entry(*address)
778                .or_default()
779                .push((account, is_writable));
780        }
781    }
782
783    impl<'a> From<&'a TestCallbacks> for AccountLoader<'a, TestCallbacks> {
784        fn from(callbacks: &'a TestCallbacks) -> AccountLoader<'a, TestCallbacks> {
785            AccountLoader::new_with_loaded_accounts_capacity(
786                None,
787                callbacks,
788                &callbacks.feature_set,
789                0,
790            )
791        }
792    }
793
794    fn load_accounts_with_features_and_rent(
795        tx: Transaction,
796        accounts: &[KeyedAccountSharedData],
797        rent: &Rent,
798        error_metrics: &mut TransactionErrorMetrics,
799        feature_set: SVMFeatureSet,
800    ) -> TransactionLoadResult {
801        let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(tx);
802        let fee_payer_account = accounts[0].1.clone();
803        let mut accounts_map = HashMap::new();
804        for (pubkey, account) in accounts {
805            accounts_map.insert(*pubkey, (account.clone(), 1));
806        }
807        let callbacks = TestCallbacks {
808            accounts_map,
809            ..Default::default()
810        };
811        let mut account_loader: AccountLoader<TestCallbacks> = (&callbacks).into();
812        account_loader.feature_set = &feature_set;
813        load_transaction(
814            &mut account_loader,
815            &sanitized_tx,
816            TransactionValidationResult::Loadable(ValidatedTransactionDetails {
817                loaded_fee_payer_account: LoadedTransactionAccount {
818                    account: fee_payer_account,
819                    ..LoadedTransactionAccount::default()
820                },
821                ..ValidatedTransactionDetails::default()
822            }),
823            error_metrics,
824            rent,
825        )
826    }
827
828    fn new_unchecked_sanitized_message(message: Message) -> SanitizedMessage {
829        SanitizedMessage::Legacy(LegacyMessage::new(message, &HashSet::new()))
830    }
831
832    #[test]
833    fn test_update_accounts_for_successful_tx_skips_untouched() {
834        let fee_payer = Keypair::new();
835        let (touched_key, untouched_key) = (Pubkey::new_unique(), Pubkey::new_unique());
836        let program = Pubkey::new_unique();
837
838        // Writable accounts at indices 0, 1, 2; readonly program at index 3.
839        let instructions = vec![CompiledInstruction::new(3, &(), vec![1, 2])];
840        let message = new_unchecked_sanitized_message(Message::new_with_compiled_instructions(
841            1, // num_required_signatures
842            0, // num_readonly_signed_accounts
843            1, // num_readonly_unsigned_accounts -> only the program is readonly
844            vec![fee_payer.pubkey(), touched_key, untouched_key, program],
845            Hash::default(),
846            instructions,
847        ));
848        let transaction_accounts = [
849            (
850                fee_payer.pubkey(),
851                AccountSharedData::new(100, 0, &Pubkey::default()),
852            ),
853            (
854                touched_key,
855                AccountSharedData::new(1, 0, &Pubkey::default()),
856            ),
857            (
858                untouched_key,
859                AccountSharedData::new(2, 0, &Pubkey::default()),
860            ),
861            (program, AccountSharedData::new(3, 0, &Pubkey::default())),
862        ];
863
864        // Fee payer (index 0) and the VM-modified account (index 1) are marked;
865        // the writable account at index 2 is left untouched.
866        let touched_flags: Box<[bool]> = [true, true, false, false].into();
867
868        let callbacks = TestCallbacks::default();
869        let mut account_loader: AccountLoader<TestCallbacks> = (&callbacks).into();
870        account_loader.update_accounts_for_successful_tx(
871            &message,
872            &transaction_accounts,
873            &touched_flags,
874            5,
875        );
876
877        // Only touched writable accounts propagate to the in-batch loader cache.
878        assert!(
879            account_loader
880                .loaded_accounts
881                .contains_key(&fee_payer.pubkey())
882        );
883        assert!(account_loader.loaded_accounts.contains_key(&touched_key));
884        assert!(!account_loader.loaded_accounts.contains_key(&untouched_key));
885        assert!(!account_loader.loaded_accounts.contains_key(&program));
886    }
887
888    #[test]
889    fn test_load_accounts_unknown_program_id() {
890        let mut accounts: Vec<KeyedAccountSharedData> = Vec::new();
891        let mut error_metrics = TransactionErrorMetrics::default();
892
893        let keypair = Keypair::new();
894        let key0 = keypair.pubkey();
895        let key1 = Pubkey::from([5u8; 32]);
896
897        let account = AccountSharedData::new(1, 0, &Pubkey::default());
898        accounts.push((key0, account));
899
900        let account = AccountSharedData::new(2, 1, &Pubkey::default());
901        accounts.push((key1, account));
902
903        let instructions = vec![CompiledInstruction::new(1, &(), vec![0])];
904        let tx = Transaction::new_with_compiled_instructions(
905            &[&keypair],
906            &[],
907            Hash::default(),
908            vec![Pubkey::default()],
909            instructions,
910        );
911
912        let feature_set = SVMFeatureSet::all_enabled();
913        let load_results = load_accounts_with_features_and_rent(
914            tx,
915            &accounts,
916            &Rent::default(),
917            &mut error_metrics,
918            feature_set,
919        );
920
921        assert_eq!(error_metrics.account_not_found.0, 1);
922        assert!(matches!(
923            load_results,
924            TransactionLoadResult::FeesOnly(FeesOnlyTransaction {
925                load_error: TransactionError::ProgramAccountNotFound,
926                ..
927            }),
928        ));
929    }
930
931    #[test]
932    fn test_load_accounts_no_loaders() {
933        let mut accounts: Vec<KeyedAccountSharedData> = Vec::new();
934        let mut error_metrics = TransactionErrorMetrics::default();
935
936        let keypair = Keypair::new();
937        let key0 = keypair.pubkey();
938        let key1 = Pubkey::from([5u8; 32]);
939
940        let mut account = AccountSharedData::new(1, 0, &Pubkey::default());
941        account.set_rent_epoch(1);
942        accounts.push((key0, account));
943
944        let mut account = AccountSharedData::new(2, 1, &Pubkey::default());
945        account.set_rent_epoch(1);
946        accounts.push((key1, account));
947
948        let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
949        let tx = Transaction::new_with_compiled_instructions(
950            &[&keypair],
951            &[key1],
952            Hash::default(),
953            vec![native_loader::id()],
954            instructions,
955        );
956
957        let feature_set = SVMFeatureSet::all_enabled();
958        let loaded_accounts = load_accounts_with_features_and_rent(
959            tx,
960            &accounts,
961            &Rent::default(),
962            &mut error_metrics,
963            feature_set,
964        );
965
966        match &loaded_accounts {
967            TransactionLoadResult::FeesOnly(fees_only_tx) => {
968                assert_eq!(error_metrics.account_not_found.0, 1);
969                assert_eq!(
970                    fees_only_tx.load_error,
971                    TransactionError::ProgramAccountNotFound,
972                );
973            }
974            result => panic!("unexpected result: {result:?}"),
975        }
976    }
977
978    #[test]
979    fn test_load_accounts_bad_owner() {
980        let mut accounts: Vec<KeyedAccountSharedData> = Vec::new();
981        let mut error_metrics = TransactionErrorMetrics::default();
982
983        let keypair = Keypair::new();
984        let key0 = keypair.pubkey();
985        let key1 = Pubkey::from([5u8; 32]);
986
987        let account = AccountSharedData::new(1, 0, &Pubkey::default());
988        accounts.push((key0, account));
989
990        let mut account = AccountSharedData::new(40, 1, &Pubkey::default());
991        account.set_executable(true);
992        accounts.push((key1, account));
993
994        let instructions = vec![CompiledInstruction::new(1, &(), vec![0])];
995        let tx = Transaction::new_with_compiled_instructions(
996            &[&keypair],
997            &[],
998            Hash::default(),
999            vec![key1],
1000            instructions,
1001        );
1002
1003        let feature_set = SVMFeatureSet::all_enabled();
1004        let load_results = load_accounts_with_features_and_rent(
1005            tx,
1006            &accounts,
1007            &Rent::default(),
1008            &mut error_metrics,
1009            feature_set,
1010        );
1011
1012        assert_eq!(error_metrics.invalid_program_for_execution.0, 1);
1013        assert!(matches!(
1014            load_results,
1015            TransactionLoadResult::FeesOnly(FeesOnlyTransaction {
1016                load_error: TransactionError::InvalidProgramForExecution,
1017                ..
1018            }),
1019        ));
1020    }
1021
1022    #[test]
1023    fn test_load_accounts_not_executable() {
1024        let mut accounts: Vec<KeyedAccountSharedData> = Vec::new();
1025        let mut error_metrics = TransactionErrorMetrics::default();
1026
1027        let keypair = Keypair::new();
1028        let key0 = keypair.pubkey();
1029        let key1 = Pubkey::from([5u8; 32]);
1030
1031        let account = AccountSharedData::new(1, 0, &Pubkey::default());
1032        accounts.push((key0, account));
1033
1034        let account = AccountSharedData::new(40, 0, &native_loader::id());
1035        accounts.push((key1, account));
1036
1037        let instructions = vec![CompiledInstruction::new(1, &(), vec![0])];
1038        let tx = Transaction::new_with_compiled_instructions(
1039            &[&keypair],
1040            &[],
1041            Hash::default(),
1042            vec![key1],
1043            instructions,
1044        );
1045
1046        let feature_set = SVMFeatureSet::all_enabled();
1047        let load_results = load_accounts_with_features_and_rent(
1048            tx,
1049            &accounts,
1050            &Rent::default(),
1051            &mut error_metrics,
1052            feature_set,
1053        );
1054
1055        assert_eq!(error_metrics.invalid_program_for_execution.0, 0);
1056        match &load_results {
1057            TransactionLoadResult::Loaded(loaded_transaction) => {
1058                assert_eq!(loaded_transaction.accounts.len(), 2);
1059                assert_eq!(loaded_transaction.accounts[0].1, accounts[0].1);
1060                assert_eq!(loaded_transaction.accounts[1].1, accounts[1].1);
1061            }
1062            TransactionLoadResult::FeesOnly(fees_only_tx) => panic!("{}", fees_only_tx.load_error),
1063            TransactionLoadResult::NoOp(no_op_tx) => panic!("{}", no_op_tx.validation_error),
1064            TransactionLoadResult::Unprocessable(e) => panic!("{e}"),
1065        }
1066    }
1067
1068    #[test]
1069    fn test_load_accounts_multiple_loaders() {
1070        let mut accounts: Vec<KeyedAccountSharedData> = Vec::new();
1071        let mut error_metrics = TransactionErrorMetrics::default();
1072
1073        let keypair = Keypair::new();
1074        let key0 = keypair.pubkey();
1075        let key1 = bpf_loader_upgradeable::id();
1076        let key2 = Pubkey::from([6u8; 32]);
1077
1078        let mut account = AccountSharedData::new(1, 0, &Pubkey::default());
1079        account.set_rent_epoch(1);
1080        accounts.push((key0, account));
1081
1082        let mut account = AccountSharedData::new(40, 1, &Pubkey::default());
1083        account.set_executable(true);
1084        account.set_rent_epoch(1);
1085        account.set_owner(native_loader::id());
1086        accounts.push((key1, account));
1087
1088        let mut account = AccountSharedData::new(41, 1, &Pubkey::default());
1089        account.set_executable(true);
1090        account.set_rent_epoch(1);
1091        account.set_owner(key1);
1092        accounts.push((key2, account));
1093
1094        let instructions = vec![
1095            CompiledInstruction::new(1, &(), vec![0]),
1096            CompiledInstruction::new(2, &(), vec![0]),
1097        ];
1098        let tx = Transaction::new_with_compiled_instructions(
1099            &[&keypair],
1100            &[],
1101            Hash::default(),
1102            vec![key1, key2],
1103            instructions,
1104        );
1105
1106        let feature_set = SVMFeatureSet::all_enabled();
1107        let loaded_accounts = load_accounts_with_features_and_rent(
1108            tx,
1109            &accounts,
1110            &Rent::default(),
1111            &mut error_metrics,
1112            feature_set,
1113        );
1114
1115        assert_eq!(error_metrics.account_not_found.0, 0);
1116        match &loaded_accounts {
1117            TransactionLoadResult::Loaded(loaded_transaction) => {
1118                assert_eq!(loaded_transaction.accounts.len(), 3);
1119                assert_eq!(loaded_transaction.accounts[0].1, accounts[0].1);
1120            }
1121            TransactionLoadResult::FeesOnly(fees_only_tx) => panic!("{}", fees_only_tx.load_error),
1122            TransactionLoadResult::NoOp(no_op_tx) => panic!("{}", no_op_tx.validation_error),
1123            TransactionLoadResult::Unprocessable(e) => panic!("{e}"),
1124        }
1125    }
1126
1127    fn load_accounts_no_store(
1128        accounts: &[KeyedAccountSharedData],
1129        tx: Transaction,
1130        account_overrides: Option<&AccountOverrides>,
1131    ) -> TransactionLoadResult {
1132        let tx = SanitizedTransaction::from_transaction_for_tests(tx);
1133
1134        let mut error_metrics = TransactionErrorMetrics::default();
1135        let mut accounts_map = HashMap::new();
1136        for (pubkey, account) in accounts {
1137            accounts_map.insert(*pubkey, (account.clone(), 1));
1138        }
1139        let callbacks = TestCallbacks {
1140            accounts_map,
1141            ..Default::default()
1142        };
1143        let feature_set = SVMFeatureSet::all_enabled();
1144        let mut account_loader = AccountLoader::new_with_loaded_accounts_capacity(
1145            account_overrides,
1146            &callbacks,
1147            &feature_set,
1148            0,
1149        );
1150        load_transaction(
1151            &mut account_loader,
1152            &tx,
1153            TransactionValidationResult::Loadable(ValidatedTransactionDetails::default()),
1154            &mut error_metrics,
1155            &Rent::default(),
1156        )
1157    }
1158
1159    #[test]
1160    fn test_instructions() {
1161        setup_test_logger();
1162        let instructions_key = solana_sdk_ids::sysvar::instructions::id();
1163        let keypair = Keypair::new();
1164        let instructions = vec![CompiledInstruction::new(1, &(), vec![0, 1])];
1165        let tx = Transaction::new_with_compiled_instructions(
1166            &[&keypair],
1167            &[solana_pubkey::new_rand(), instructions_key],
1168            Hash::default(),
1169            vec![native_loader::id()],
1170            instructions,
1171        );
1172
1173        let load_results = load_accounts_no_store(&[], tx, None);
1174        assert!(matches!(
1175            load_results,
1176            TransactionLoadResult::FeesOnly(FeesOnlyTransaction {
1177                load_error: TransactionError::ProgramAccountNotFound,
1178                ..
1179            }),
1180        ));
1181    }
1182
1183    #[test]
1184    fn test_overrides() {
1185        setup_test_logger();
1186        let mut account_overrides = AccountOverrides::default();
1187        let slot_history_id = sysvar::slot_history::id();
1188        let account = AccountSharedData::new(42, 0, &Pubkey::default());
1189        account_overrides.set_slot_history(Some(account));
1190
1191        let keypair = Keypair::new();
1192        let account = AccountSharedData::new(1_000_000, 0, &Pubkey::default());
1193
1194        let mut program_account = AccountSharedData::default();
1195        program_account.set_lamports(1);
1196        program_account.set_executable(true);
1197        program_account.set_owner(native_loader::id());
1198
1199        let instructions = vec![CompiledInstruction::new(2, &(), vec![0])];
1200        let tx = Transaction::new_with_compiled_instructions(
1201            &[&keypair],
1202            &[slot_history_id],
1203            Hash::default(),
1204            vec![bpf_loader::id()],
1205            instructions,
1206        );
1207
1208        let loaded_accounts = load_accounts_no_store(
1209            &[
1210                (keypair.pubkey(), account),
1211                (bpf_loader::id(), program_account),
1212            ],
1213            tx,
1214            Some(&account_overrides),
1215        );
1216        match &loaded_accounts {
1217            TransactionLoadResult::Loaded(loaded_transaction) => {
1218                assert_eq!(loaded_transaction.accounts[0].0, keypair.pubkey());
1219                assert_eq!(loaded_transaction.accounts[1].0, slot_history_id);
1220                assert_eq!(loaded_transaction.accounts[1].1.lamports(), 42);
1221            }
1222            TransactionLoadResult::FeesOnly(fees_only_tx) => panic!("{}", fees_only_tx.load_error),
1223            TransactionLoadResult::NoOp(no_op_tx) => panic!("{}", no_op_tx.validation_error),
1224            TransactionLoadResult::Unprocessable(e) => panic!("{e}"),
1225        }
1226    }
1227
1228    #[test]
1229    fn test_increase_calculated_data_size() {
1230        let mut error_metrics = TransactionErrorMetrics::default();
1231        let data_size: usize = 123;
1232        let requested_data_size_limit = data_size as u32 + 1;
1233        let mut acc = LoadedTransactionDataSize::with_max_size(requested_data_size_limit);
1234
1235        // OK - loaded data size is under limit
1236        assert!(
1237            acc.increase_calculated_data_size(data_size, &mut error_metrics)
1238                .is_ok()
1239        );
1240        assert_eq!(data_size as u32, u32::from(acc.clone()));
1241
1242        // OK - loaded data size meets limit
1243        assert!(
1244            acc.increase_calculated_data_size(1, &mut error_metrics)
1245                .is_ok()
1246        );
1247        assert_eq!(requested_data_size_limit, u32::from(acc.clone()));
1248
1249        // fail - loading more data would exceed limit
1250        // data size helper reports the limit only
1251        assert_eq!(
1252            acc.increase_calculated_data_size(1, &mut error_metrics),
1253            Err(TransactionError::MaxLoadedAccountsDataSizeExceeded)
1254        );
1255        assert_eq!(requested_data_size_limit, u32::from(acc));
1256
1257        let mut acc = LoadedTransactionDataSize::with_max_size(requested_data_size_limit);
1258
1259        // fail - adding a huge number exceeds limit
1260        // data size helper correctly reports we hit the limit
1261        assert_eq!(
1262            acc.increase_calculated_data_size(u32::MAX as usize + 1, &mut error_metrics),
1263            Err(TransactionError::MaxLoadedAccountsDataSizeExceeded)
1264        );
1265        assert_eq!(requested_data_size_limit, u32::from(acc));
1266    }
1267
1268    struct ValidateFeePayerTestParameter {
1269        is_nonce: bool,
1270        payer_init_balance: u64,
1271        fee: u64,
1272        relax_post_exec_min_balance_check: bool,
1273        expected_result: Result<()>,
1274        payer_post_balance: u64,
1275    }
1276    fn validate_fee_payer_account(test_parameter: ValidateFeePayerTestParameter, rent: &Rent) {
1277        let mut account = if test_parameter.is_nonce {
1278            AccountSharedData::new_data(
1279                test_parameter.payer_init_balance,
1280                &NonceVersions::new(NonceState::Initialized(nonce::state::Data::default())),
1281                &system_program::id(),
1282            )
1283            .unwrap()
1284        } else {
1285            AccountSharedData::new(test_parameter.payer_init_balance, 0, &system_program::id())
1286        };
1287        let result = validate_fee_payer(
1288            &mut account,
1289            0,
1290            &mut TransactionErrorMetrics::default(),
1291            rent,
1292            test_parameter.fee,
1293            test_parameter.relax_post_exec_min_balance_check,
1294        );
1295
1296        assert_eq!(result, test_parameter.expected_result);
1297        assert_eq!(account.lamports(), test_parameter.payer_post_balance);
1298    }
1299
1300    #[test]
1301    fn test_validate_fee_payer() {
1302        let rent = Rent::default();
1303        let nonce_min_balance = rent.minimum_balance(NonceState::size());
1304        let system_min_balance = rent.minimum_balance(0);
1305        let fee = 5_000;
1306
1307        // If payer account has sufficient balance, expect successful fee deduction,
1308        // regardless of feature gate status.
1309        {
1310            for relax_post_exec_min_balance_check in [false, true] {
1311                for (is_nonce, min_balance) in [
1312                    (true, nonce_min_balance),
1313                    (false, system_min_balance), // rent-exempt case
1314                    (false, 0),                  // sub-exempt case, spend to zero
1315                ] {
1316                    validate_fee_payer_account(
1317                        ValidateFeePayerTestParameter {
1318                            is_nonce,
1319                            payer_init_balance: min_balance + fee,
1320                            fee,
1321                            relax_post_exec_min_balance_check,
1322                            expected_result: Ok(()),
1323                            payer_post_balance: min_balance,
1324                        },
1325                        &rent,
1326                    );
1327                }
1328            }
1329        }
1330
1331        // If payer account has no balance, expected AccountNotFound error
1332        // regardless feature gate status, or if payer is nonce account.
1333        {
1334            for relax_post_exec_min_balance_check in [false, true] {
1335                for is_nonce in [true, false] {
1336                    validate_fee_payer_account(
1337                        ValidateFeePayerTestParameter {
1338                            is_nonce,
1339                            payer_init_balance: 0,
1340                            fee,
1341                            relax_post_exec_min_balance_check,
1342                            expected_result: Err(TransactionError::AccountNotFound),
1343                            payer_post_balance: 0,
1344                        },
1345                        &rent,
1346                    );
1347                }
1348            }
1349        }
1350
1351        // Check InsufficientFunds error cases. Note: balance checks that occur
1352        // before rent state transition checks return InsufficientFundsForFee,
1353        //while those that occur after return InsufficientFundsForRent.
1354        {
1355            for relax_post_exec_min_balance_check in [false, true] {
1356                for (is_nonce, payer_init_balance, expected_result) in [
1357                    // rent-exempt nonce: arithmetic check fails before rent-state validation
1358                    (
1359                        true,
1360                        nonce_min_balance + fee - 1,
1361                        Err(TransactionError::InsufficientFundsForFee),
1362                    ),
1363                    // sub-exempt nonce: arithmetic check fails before rent-state validation
1364                    (
1365                        true,
1366                        nonce_min_balance - 1,
1367                        Err(TransactionError::InsufficientFundsForFee),
1368                    ),
1369                    // sub-exempt system: insufficient lamports to pay the fee at all
1370                    (
1371                        false,
1372                        fee - 1,
1373                        Err(TransactionError::InsufficientFundsForFee),
1374                    ),
1375                    // sub-exempt system: fee debit succeeds but an invalid state
1376                    // transition of RentExempt -> RentPaying is produced if
1377                    // relax_post_exec_min_balance_check is true. Otherwise, the
1378                    // valid RentPaying -> RentPaying transition is produced.
1379                    (
1380                        false,
1381                        system_min_balance - 1,
1382                        if relax_post_exec_min_balance_check {
1383                            Err(TransactionError::InsufficientFundsForRent { account_index: 0 })
1384                        } else {
1385                            Ok(())
1386                        },
1387                    ),
1388                    // rent-exempt system: fee debit succeeds but drops below the rent-exempt minimum
1389                    (
1390                        false,
1391                        system_min_balance + fee - 1,
1392                        Err(TransactionError::InsufficientFundsForRent { account_index: 0 }),
1393                    ),
1394                ] {
1395                    // Fee payer validation debits lamports before rent-state
1396                    // validation, so only InsufficientFundsForFee leaves the
1397                    // original balance unchanged.
1398                    let expected_post_balance = if matches!(
1399                        expected_result,
1400                        Err(TransactionError::InsufficientFundsForFee)
1401                    ) {
1402                        payer_init_balance
1403                    } else {
1404                        payer_init_balance - fee
1405                    };
1406                    validate_fee_payer_account(
1407                        ValidateFeePayerTestParameter {
1408                            is_nonce,
1409                            payer_init_balance,
1410                            fee,
1411                            relax_post_exec_min_balance_check,
1412                            expected_result,
1413                            payer_post_balance: expected_post_balance,
1414                        },
1415                        &rent,
1416                    );
1417                }
1418            }
1419        }
1420
1421        // Regular system fee payer may be spent all the way down to zero but
1422        // a nonce fee payer cannot.
1423        {
1424            // try to spend rent-exempt nonce account down to zero
1425            let fee = nonce_min_balance;
1426            for relax_post_exec_min_balance_check in [false, true] {
1427                validate_fee_payer_account(
1428                    ValidateFeePayerTestParameter {
1429                        is_nonce: true,
1430                        payer_init_balance: nonce_min_balance,
1431                        fee,
1432                        relax_post_exec_min_balance_check,
1433                        expected_result: Err(TransactionError::InsufficientFundsForFee),
1434                        payer_post_balance: nonce_min_balance,
1435                    },
1436                    &rent,
1437                );
1438            }
1439        }
1440
1441        // normal payer account has balance of u64::MAX, so does fee; since it does not require
1442        // min_balance, expect successful fee deduction, regardless of feature gate status
1443        {
1444            for relax_post_exec_min_balance_check in [false, true] {
1445                validate_fee_payer_account(
1446                    ValidateFeePayerTestParameter {
1447                        is_nonce: false,
1448                        payer_init_balance: u64::MAX,
1449                        fee: u64::MAX,
1450                        relax_post_exec_min_balance_check,
1451                        expected_result: Ok(()),
1452                        payer_post_balance: 0,
1453                    },
1454                    &rent,
1455                );
1456            }
1457        }
1458    }
1459
1460    #[test]
1461    fn test_validate_nonce_fee_payer_with_checked_arithmetic() {
1462        let rent = Rent {
1463            lamports_per_byte: 1,
1464            ..Rent::default()
1465        };
1466
1467        // nonce payer account has balance of u64::MAX, so does fee; the additional nonce
1468        // min_balance requirement makes the checked arithmetic fail regardless of feature gate.
1469        for relax_post_exec_min_balance_check in [false, true] {
1470            validate_fee_payer_account(
1471                ValidateFeePayerTestParameter {
1472                    is_nonce: true,
1473                    payer_init_balance: u64::MAX,
1474                    fee: u64::MAX,
1475                    relax_post_exec_min_balance_check,
1476                    expected_result: Err(TransactionError::InsufficientFundsForFee),
1477                    payer_post_balance: u64::MAX,
1478                },
1479                &rent,
1480            );
1481        }
1482    }
1483
1484    #[test]
1485    fn test_construct_instructions_account() {
1486        let loaded_message = LoadedMessage {
1487            message: Cow::Owned(solana_message::v0::Message::default()),
1488            loaded_addresses: Cow::Owned(LoadedAddresses::default()),
1489            is_writable_account_cache: vec![false],
1490        };
1491        let message = SanitizedMessage::V0(loaded_message);
1492        let shared_data = construct_instructions_account(&message).unwrap();
1493        let expected = AccountSharedData::from(Account {
1494            data: construct_instructions_data(&message.decompile_instructions()).unwrap(),
1495            owner: sysvar::id(),
1496            ..Account::default()
1497        });
1498        assert_eq!(shared_data, expected);
1499    }
1500
1501    #[test]
1502    fn test_load_transaction_accounts_fee_payer() {
1503        let fee_payer_address = Pubkey::new_unique();
1504        let message = Message {
1505            account_keys: vec![fee_payer_address],
1506            header: MessageHeader::default(),
1507            instructions: vec![],
1508            recent_blockhash: Hash::default(),
1509        };
1510
1511        let sanitized_message = new_unchecked_sanitized_message(message);
1512        let mut mock_bank = TestCallbacks::default();
1513
1514        let fee_payer_balance = 200;
1515        let mut fee_payer_account = AccountSharedData::default();
1516        fee_payer_account.set_lamports(fee_payer_balance);
1517        mock_bank
1518            .accounts_map
1519            .insert(fee_payer_address, (fee_payer_account.clone(), 1));
1520        let mut account_loader = (&mock_bank).into();
1521
1522        let mut error_metrics = TransactionErrorMetrics::default();
1523
1524        let mut loaded_transaction_data_size =
1525            LoadedTransactionDataSize::with_max_size(MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES.get());
1526
1527        let sanitized_transaction = SanitizedTransaction::new_for_tests(
1528            sanitized_message,
1529            vec![Signature::new_unique()],
1530            false,
1531        );
1532        let result = load_transaction_accounts(
1533            &mut account_loader,
1534            sanitized_transaction.message(),
1535            LoadedTransactionAccount {
1536                loaded_size: fee_payer_account.data().len(),
1537                account: fee_payer_account.clone(),
1538            },
1539            &mut loaded_transaction_data_size,
1540            &mut error_metrics,
1541            &Rent::default(),
1542        );
1543        assert_eq!(
1544            vec![(fee_payer_address, fee_payer_account)],
1545            result.unwrap(),
1546        );
1547        assert_eq!(0, loaded_transaction_data_size.loaded_accounts_data_size);
1548    }
1549
1550    #[test]
1551    fn test_load_transaction_accounts_native_loader() {
1552        let key1 = Keypair::new();
1553        let message = Message {
1554            account_keys: vec![key1.pubkey(), native_loader::id()],
1555            header: MessageHeader::default(),
1556            instructions: vec![CompiledInstruction {
1557                program_id_index: 1,
1558                accounts: vec![0],
1559                data: vec![],
1560            }],
1561            recent_blockhash: Hash::default(),
1562        };
1563
1564        let sanitized_message = new_unchecked_sanitized_message(message);
1565        let mut mock_bank = TestCallbacks::default();
1566        mock_bank
1567            .accounts_map
1568            .insert(native_loader::id(), (AccountSharedData::default(), 0));
1569        let mut fee_payer_account = AccountSharedData::default();
1570        fee_payer_account.set_lamports(200);
1571        mock_bank
1572            .accounts_map
1573            .insert(key1.pubkey(), (fee_payer_account.clone(), 1));
1574        let mut account_loader = (&mock_bank).into();
1575
1576        let mut error_metrics = TransactionErrorMetrics::default();
1577
1578        let mut loaded_transaction_data_size =
1579            LoadedTransactionDataSize::with_max_size(MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES.get());
1580
1581        let sanitized_transaction = SanitizedTransaction::new_for_tests(
1582            sanitized_message,
1583            vec![Signature::new_unique()],
1584            false,
1585        );
1586
1587        let result = load_transaction_accounts(
1588            &mut account_loader,
1589            sanitized_transaction.message(),
1590            LoadedTransactionAccount {
1591                account: fee_payer_account.clone(),
1592                loaded_size: TRANSACTION_ACCOUNT_BASE_SIZE,
1593            },
1594            &mut loaded_transaction_data_size,
1595            &mut error_metrics,
1596            &Rent::default(),
1597        );
1598
1599        assert_eq!(
1600            result.unwrap_err(),
1601            TransactionError::ProgramAccountNotFound
1602        );
1603    }
1604
1605    #[test]
1606    fn test_load_transaction_accounts_program_account_no_data() {
1607        let key1 = Keypair::new();
1608        let key2 = Keypair::new();
1609
1610        let message = Message {
1611            account_keys: vec![key1.pubkey(), key2.pubkey()],
1612            header: MessageHeader::default(),
1613            instructions: vec![CompiledInstruction {
1614                program_id_index: 1,
1615                accounts: vec![0, 1],
1616                data: vec![],
1617            }],
1618            recent_blockhash: Hash::default(),
1619        };
1620
1621        let sanitized_message = new_unchecked_sanitized_message(message);
1622        let mut mock_bank = TestCallbacks::default();
1623        let mut account_data = AccountSharedData::default();
1624        account_data.set_lamports(200);
1625        mock_bank
1626            .accounts_map
1627            .insert(key1.pubkey(), (account_data, 1));
1628        let mut account_loader = (&mock_bank).into();
1629
1630        let mut error_metrics = TransactionErrorMetrics::default();
1631
1632        let mut loaded_transaction_data_size =
1633            LoadedTransactionDataSize::with_max_size(MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES.get());
1634
1635        let sanitized_transaction = SanitizedTransaction::new_for_tests(
1636            sanitized_message,
1637            vec![Signature::new_unique()],
1638            false,
1639        );
1640        let result = load_transaction_accounts(
1641            &mut account_loader,
1642            sanitized_transaction.message(),
1643            LoadedTransactionAccount::default(),
1644            &mut loaded_transaction_data_size,
1645            &mut error_metrics,
1646            &Rent::default(),
1647        );
1648
1649        assert_eq!(result.err(), Some(TransactionError::ProgramAccountNotFound));
1650    }
1651
1652    #[test]
1653    fn test_load_transaction_accounts_invalid_program_for_execution() {
1654        let key1 = Keypair::new();
1655        let key2 = Keypair::new();
1656
1657        let message = Message {
1658            account_keys: vec![key1.pubkey(), key2.pubkey()],
1659            header: MessageHeader::default(),
1660            instructions: vec![CompiledInstruction {
1661                program_id_index: 0,
1662                accounts: vec![0, 1],
1663                data: vec![],
1664            }],
1665            recent_blockhash: Hash::default(),
1666        };
1667
1668        let sanitized_message = new_unchecked_sanitized_message(message);
1669        let mut mock_bank = TestCallbacks::default();
1670        let mut account_data = AccountSharedData::default();
1671        account_data.set_lamports(200);
1672        mock_bank
1673            .accounts_map
1674            .insert(key1.pubkey(), (account_data, 1));
1675        let mut account_loader = (&mock_bank).into();
1676
1677        let mut error_metrics = TransactionErrorMetrics::default();
1678
1679        let mut loaded_transaction_data_size =
1680            LoadedTransactionDataSize::with_max_size(MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES.get());
1681
1682        let sanitized_transaction = SanitizedTransaction::new_for_tests(
1683            sanitized_message,
1684            vec![Signature::new_unique()],
1685            false,
1686        );
1687        let result = load_transaction_accounts(
1688            &mut account_loader,
1689            sanitized_transaction.message(),
1690            LoadedTransactionAccount::default(),
1691            &mut loaded_transaction_data_size,
1692            &mut error_metrics,
1693            &Rent::default(),
1694        );
1695
1696        assert_eq!(
1697            result.err(),
1698            Some(TransactionError::InvalidProgramForExecution)
1699        );
1700    }
1701
1702    #[test]
1703    fn test_load_transaction_accounts_native_loader_owner() {
1704        let key1 = Keypair::new();
1705        let key2 = Keypair::new();
1706
1707        let message = Message {
1708            account_keys: vec![key2.pubkey(), key1.pubkey()],
1709            header: MessageHeader::default(),
1710            instructions: vec![CompiledInstruction {
1711                program_id_index: 1,
1712                accounts: vec![0],
1713                data: vec![],
1714            }],
1715            recent_blockhash: Hash::default(),
1716        };
1717
1718        let sanitized_message = new_unchecked_sanitized_message(message);
1719        let mut mock_bank = TestCallbacks::default();
1720        let mut account_data = AccountSharedData::default();
1721        account_data.set_owner(native_loader::id());
1722        account_data.set_lamports(1);
1723        account_data.set_executable(true);
1724        mock_bank
1725            .accounts_map
1726            .insert(key1.pubkey(), (account_data, 1));
1727
1728        let mut fee_payer_account = AccountSharedData::default();
1729        fee_payer_account.set_lamports(200);
1730        mock_bank
1731            .accounts_map
1732            .insert(key2.pubkey(), (fee_payer_account.clone(), 1));
1733        let mut account_loader = (&mock_bank).into();
1734
1735        let mut error_metrics = TransactionErrorMetrics::default();
1736
1737        let mut loaded_transaction_data_size =
1738            LoadedTransactionDataSize::with_max_size(MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES.get());
1739
1740        let sanitized_transaction = SanitizedTransaction::new_for_tests(
1741            sanitized_message,
1742            vec![Signature::new_unique()],
1743            false,
1744        );
1745
1746        let result = load_transaction_accounts(
1747            &mut account_loader,
1748            sanitized_transaction.message(),
1749            LoadedTransactionAccount {
1750                account: fee_payer_account.clone(),
1751                loaded_size: TRANSACTION_ACCOUNT_BASE_SIZE,
1752            },
1753            &mut loaded_transaction_data_size,
1754            &mut error_metrics,
1755            &Rent::default(),
1756        );
1757
1758        let expected_loaded_accounts_data_size = TRANSACTION_ACCOUNT_BASE_SIZE as u32 * 2;
1759
1760        assert_eq!(
1761            vec![
1762                (key2.pubkey(), fee_payer_account),
1763                (
1764                    key1.pubkey(),
1765                    mock_bank.accounts_map[&key1.pubkey()].0.clone()
1766                ),
1767            ],
1768            result.unwrap(),
1769        );
1770        assert_eq!(
1771            expected_loaded_accounts_data_size,
1772            loaded_transaction_data_size.loaded_accounts_data_size
1773        );
1774    }
1775
1776    #[test]
1777    fn test_load_transaction_accounts_program_account_not_found_after_all_checks() {
1778        let key1 = Keypair::new();
1779        let key2 = Keypair::new();
1780
1781        let message = Message {
1782            account_keys: vec![key2.pubkey(), key1.pubkey()],
1783            header: MessageHeader::default(),
1784            instructions: vec![CompiledInstruction {
1785                program_id_index: 1,
1786                accounts: vec![0],
1787                data: vec![],
1788            }],
1789            recent_blockhash: Hash::default(),
1790        };
1791
1792        let sanitized_message = new_unchecked_sanitized_message(message);
1793        let mut mock_bank = TestCallbacks::default();
1794        let mut account_data = AccountSharedData::default();
1795        account_data.set_executable(true);
1796        mock_bank
1797            .accounts_map
1798            .insert(key1.pubkey(), (account_data, 1));
1799
1800        let mut account_data = AccountSharedData::default();
1801        account_data.set_lamports(200);
1802        mock_bank
1803            .accounts_map
1804            .insert(key2.pubkey(), (account_data, 1));
1805        let mut account_loader = (&mock_bank).into();
1806
1807        let mut error_metrics = TransactionErrorMetrics::default();
1808
1809        let mut loaded_transaction_data_size =
1810            LoadedTransactionDataSize::with_max_size(MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES.get());
1811
1812        let sanitized_transaction = SanitizedTransaction::new_for_tests(
1813            sanitized_message,
1814            vec![Signature::new_unique()],
1815            false,
1816        );
1817        let result = load_transaction_accounts(
1818            &mut account_loader,
1819            sanitized_transaction.message(),
1820            LoadedTransactionAccount::default(),
1821            &mut loaded_transaction_data_size,
1822            &mut error_metrics,
1823            &Rent::default(),
1824        );
1825
1826        assert_eq!(result.err(), Some(TransactionError::ProgramAccountNotFound));
1827    }
1828
1829    #[test]
1830    fn test_load_transaction_accounts_program_account_invalid_program_for_execution_last_check() {
1831        let key1 = Keypair::new();
1832        let key2 = Keypair::new();
1833        let key3 = Keypair::new();
1834
1835        let message = Message {
1836            account_keys: vec![key2.pubkey(), key1.pubkey()],
1837            header: MessageHeader::default(),
1838            instructions: vec![CompiledInstruction {
1839                program_id_index: 1,
1840                accounts: vec![0],
1841                data: vec![],
1842            }],
1843            recent_blockhash: Hash::default(),
1844        };
1845
1846        let sanitized_message = new_unchecked_sanitized_message(message);
1847        let mut mock_bank = TestCallbacks::default();
1848        let mut account_data = AccountSharedData::default();
1849        account_data.set_lamports(1);
1850        account_data.set_executable(true);
1851        account_data.set_owner(key3.pubkey());
1852        mock_bank
1853            .accounts_map
1854            .insert(key1.pubkey(), (account_data, 1));
1855
1856        let mut account_data = AccountSharedData::default();
1857        account_data.set_lamports(200);
1858        mock_bank
1859            .accounts_map
1860            .insert(key2.pubkey(), (account_data, 1));
1861        mock_bank
1862            .accounts_map
1863            .insert(key3.pubkey(), (AccountSharedData::default(), 0));
1864        let mut account_loader = (&mock_bank).into();
1865
1866        let mut error_metrics = TransactionErrorMetrics::default();
1867
1868        let mut loaded_transaction_data_size =
1869            LoadedTransactionDataSize::with_max_size(MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES.get());
1870
1871        let sanitized_transaction = SanitizedTransaction::new_for_tests(
1872            sanitized_message,
1873            vec![Signature::new_unique()],
1874            false,
1875        );
1876
1877        let result = load_transaction_accounts(
1878            &mut account_loader,
1879            sanitized_transaction.message(),
1880            LoadedTransactionAccount::default(),
1881            &mut loaded_transaction_data_size,
1882            &mut error_metrics,
1883            &Rent::default(),
1884        );
1885
1886        assert_eq!(
1887            result.err(),
1888            Some(TransactionError::InvalidProgramForExecution)
1889        );
1890    }
1891
1892    #[test]
1893    fn test_load_transaction_accounts_program_success_complete() {
1894        let key1 = Keypair::new();
1895        let key2 = Keypair::new();
1896
1897        let message = Message {
1898            account_keys: vec![key2.pubkey(), key1.pubkey()],
1899            header: MessageHeader::default(),
1900            instructions: vec![CompiledInstruction {
1901                program_id_index: 1,
1902                accounts: vec![0],
1903                data: vec![],
1904            }],
1905            recent_blockhash: Hash::default(),
1906        };
1907
1908        let sanitized_message = new_unchecked_sanitized_message(message);
1909        let mut mock_bank = TestCallbacks::default();
1910        let mut account_data = AccountSharedData::default();
1911        account_data.set_lamports(1);
1912        account_data.set_executable(true);
1913        account_data.set_owner(bpf_loader::id());
1914        mock_bank
1915            .accounts_map
1916            .insert(key1.pubkey(), (account_data, 1));
1917
1918        let mut fee_payer_account = AccountSharedData::default();
1919        fee_payer_account.set_lamports(200);
1920        mock_bank
1921            .accounts_map
1922            .insert(key2.pubkey(), (fee_payer_account.clone(), 1));
1923
1924        let mut account_data = AccountSharedData::default();
1925        account_data.set_lamports(1);
1926        account_data.set_executable(true);
1927        account_data.set_owner(native_loader::id());
1928        mock_bank
1929            .accounts_map
1930            .insert(bpf_loader::id(), (account_data, 0));
1931        let mut account_loader = (&mock_bank).into();
1932
1933        let mut error_metrics = TransactionErrorMetrics::default();
1934
1935        let sanitized_transaction = SanitizedTransaction::new_for_tests(
1936            sanitized_message,
1937            vec![Signature::new_unique()],
1938            false,
1939        );
1940
1941        let mut loaded_transaction_data_size =
1942            LoadedTransactionDataSize::with_max_size(MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES.get());
1943
1944        let result = load_transaction_accounts(
1945            &mut account_loader,
1946            sanitized_transaction.message(),
1947            LoadedTransactionAccount {
1948                account: fee_payer_account.clone(),
1949                loaded_size: TRANSACTION_ACCOUNT_BASE_SIZE,
1950            },
1951            &mut loaded_transaction_data_size,
1952            &mut error_metrics,
1953            &Rent::default(),
1954        );
1955
1956        let expected_loaded_accounts_data_size = TRANSACTION_ACCOUNT_BASE_SIZE as u32 * 2;
1957
1958        assert_eq!(
1959            vec![
1960                (key2.pubkey(), fee_payer_account),
1961                (
1962                    key1.pubkey(),
1963                    mock_bank.accounts_map[&key1.pubkey()].0.clone()
1964                ),
1965            ],
1966            result.unwrap(),
1967        );
1968        assert_eq!(
1969            expected_loaded_accounts_data_size,
1970            loaded_transaction_data_size.loaded_accounts_data_size
1971        );
1972    }
1973
1974    #[test]
1975    fn test_load_transaction_accounts_program_builtin_saturating_add() {
1976        let key1 = Keypair::new();
1977        let key2 = Keypair::new();
1978        let key3 = Keypair::new();
1979
1980        let message = Message {
1981            account_keys: vec![key2.pubkey(), key1.pubkey(), key3.pubkey()],
1982            header: MessageHeader::default(),
1983            instructions: vec![
1984                CompiledInstruction {
1985                    program_id_index: 1,
1986                    accounts: vec![0],
1987                    data: vec![],
1988                },
1989                CompiledInstruction {
1990                    program_id_index: 1,
1991                    accounts: vec![2],
1992                    data: vec![],
1993                },
1994            ],
1995            recent_blockhash: Hash::default(),
1996        };
1997
1998        let sanitized_message = new_unchecked_sanitized_message(message);
1999        let mut mock_bank = TestCallbacks::default();
2000        let mut account_data = AccountSharedData::default();
2001        account_data.set_lamports(1);
2002        account_data.set_executable(true);
2003        account_data.set_owner(bpf_loader::id());
2004        mock_bank
2005            .accounts_map
2006            .insert(key1.pubkey(), (account_data, 0));
2007
2008        let mut fee_payer_account = AccountSharedData::default();
2009        fee_payer_account.set_lamports(200);
2010        mock_bank
2011            .accounts_map
2012            .insert(key2.pubkey(), (fee_payer_account.clone(), 1));
2013
2014        let mut account_data = AccountSharedData::default();
2015        account_data.set_lamports(1);
2016        account_data.set_executable(true);
2017        account_data.set_owner(native_loader::id());
2018        mock_bank
2019            .accounts_map
2020            .insert(bpf_loader::id(), (account_data, 0));
2021        let mut account_loader = (&mock_bank).into();
2022
2023        let mut error_metrics = TransactionErrorMetrics::default();
2024
2025        let sanitized_transaction = SanitizedTransaction::new_for_tests(
2026            sanitized_message,
2027            vec![Signature::new_unique()],
2028            false,
2029        );
2030
2031        let mut loaded_transaction_data_size =
2032            LoadedTransactionDataSize::with_max_size(MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES.get());
2033
2034        let result = load_transaction_accounts(
2035            &mut account_loader,
2036            sanitized_transaction.message(),
2037            LoadedTransactionAccount {
2038                account: fee_payer_account.clone(),
2039                loaded_size: TRANSACTION_ACCOUNT_BASE_SIZE,
2040            },
2041            &mut loaded_transaction_data_size,
2042            &mut error_metrics,
2043            &Rent::default(),
2044        );
2045
2046        let expected_loaded_accounts_data_size = TRANSACTION_ACCOUNT_BASE_SIZE as u32 * 2;
2047
2048        let mut account_data = AccountSharedData::default();
2049        account_data.set_rent_epoch(RENT_EXEMPT_RENT_EPOCH);
2050        assert_eq!(
2051            vec![
2052                (key2.pubkey(), fee_payer_account),
2053                (
2054                    key1.pubkey(),
2055                    mock_bank.accounts_map[&key1.pubkey()].0.clone()
2056                ),
2057                (key3.pubkey(), account_data),
2058            ],
2059            result.unwrap(),
2060        );
2061        assert_eq!(
2062            expected_loaded_accounts_data_size,
2063            loaded_transaction_data_size.loaded_accounts_data_size
2064        );
2065    }
2066
2067    #[test]
2068    fn test_rent_state_list_len() {
2069        let mint_keypair = Keypair::new();
2070        let mut bank = TestCallbacks::default();
2071        let recipient = Pubkey::new_unique();
2072        let last_block_hash = Hash::new_unique();
2073
2074        let mut system_data = AccountSharedData::default();
2075        system_data.set_lamports(1);
2076        system_data.set_executable(true);
2077        system_data.set_owner(native_loader::id());
2078        bank.accounts_map
2079            .insert(Pubkey::new_from_array([0u8; 32]), (system_data, 0));
2080
2081        let mut mint_data = AccountSharedData::default();
2082        mint_data.set_lamports(2);
2083        bank.accounts_map
2084            .insert(mint_keypair.pubkey(), (mint_data, 0));
2085        bank.accounts_map
2086            .insert(recipient, (AccountSharedData::default(), 1));
2087        let mut account_loader = (&bank).into();
2088
2089        let tx = transfer(&mint_keypair, &recipient, LAMPORTS_PER_SOL, last_block_hash);
2090        let num_accounts = tx.message().account_keys.len();
2091        let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(tx);
2092        let mut error_metrics = TransactionErrorMetrics::default();
2093        let load_result = load_transaction(
2094            &mut account_loader,
2095            &sanitized_tx,
2096            TransactionValidationResult::Loadable(ValidatedTransactionDetails::default()),
2097            &mut error_metrics,
2098            &Rent::default(),
2099        );
2100
2101        let TransactionLoadResult::Loaded(loaded_transaction) = load_result else {
2102            panic!("transaction loading failed");
2103        };
2104
2105        let compute_budget = SVMTransactionExecutionBudget {
2106            compute_unit_limit: u64::from(DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT),
2107            ..SVMTransactionExecutionBudget::default()
2108        };
2109        let rent = Rent::default();
2110        let transaction_context = TransactionContext::new(
2111            loaded_transaction.accounts,
2112            rent.clone(),
2113            compute_budget.max_instruction_stack_depth,
2114            compute_budget.max_instruction_trace_length,
2115            1,
2116        );
2117
2118        let pre_account_state_info = TransactionAccountStateInfo::new_pre_exec(
2119            &transaction_context,
2120            sanitized_tx.message(),
2121            &rent,
2122            true,
2123        );
2124        assert_eq!(pre_account_state_info.len(), num_accounts);
2125
2126        assert_eq!(
2127            TransactionAccountStateInfo::new_post_exec(
2128                &transaction_context,
2129                sanitized_tx.message(),
2130                &pre_account_state_info,
2131                &rent,
2132                true,
2133            )
2134            .len(),
2135            num_accounts,
2136        );
2137    }
2138
2139    #[test]
2140    fn test_load_accounts_success() {
2141        let key1 = Keypair::new();
2142        let key2 = Keypair::new();
2143        let key3 = Keypair::new();
2144
2145        let message = Message {
2146            account_keys: vec![key2.pubkey(), key1.pubkey(), key3.pubkey()],
2147            header: MessageHeader::default(),
2148            instructions: vec![
2149                CompiledInstruction {
2150                    program_id_index: 1,
2151                    accounts: vec![0],
2152                    data: vec![],
2153                },
2154                CompiledInstruction {
2155                    program_id_index: 1,
2156                    accounts: vec![2],
2157                    data: vec![],
2158                },
2159            ],
2160            recent_blockhash: Hash::default(),
2161        };
2162
2163        let sanitized_message = new_unchecked_sanitized_message(message);
2164        let mut mock_bank = TestCallbacks::default();
2165        let mut account_data = AccountSharedData::default();
2166        account_data.set_lamports(1);
2167        account_data.set_executable(true);
2168        account_data.set_owner(bpf_loader::id());
2169        mock_bank
2170            .accounts_map
2171            .insert(key1.pubkey(), (account_data, 0));
2172
2173        let mut fee_payer_account = AccountSharedData::default();
2174        fee_payer_account.set_lamports(200);
2175        mock_bank
2176            .accounts_map
2177            .insert(key2.pubkey(), (fee_payer_account.clone(), 1));
2178
2179        let mut account_data = AccountSharedData::default();
2180        account_data.set_lamports(1);
2181        account_data.set_executable(true);
2182        account_data.set_owner(native_loader::id());
2183        mock_bank
2184            .accounts_map
2185            .insert(bpf_loader::id(), (account_data, 0));
2186        let mut account_loader = (&mock_bank).into();
2187
2188        let mut error_metrics = TransactionErrorMetrics::default();
2189
2190        let sanitized_transaction = SanitizedTransaction::new_for_tests(
2191            sanitized_message,
2192            vec![Signature::new_unique()],
2193            false,
2194        );
2195
2196        let validation_result =
2197            TransactionValidationResult::Loadable(ValidatedTransactionDetails {
2198                loaded_fee_payer_account: LoadedTransactionAccount {
2199                    account: fee_payer_account,
2200                    loaded_size: TRANSACTION_ACCOUNT_BASE_SIZE,
2201                },
2202                ..ValidatedTransactionDetails::default()
2203            });
2204
2205        let load_result = load_transaction(
2206            &mut account_loader,
2207            &sanitized_transaction,
2208            validation_result,
2209            &mut error_metrics,
2210            &Rent::default(),
2211        );
2212
2213        let loaded_accounts_data_size = TRANSACTION_ACCOUNT_BASE_SIZE as u32 * 2;
2214
2215        let mut account_data = AccountSharedData::default();
2216        account_data.set_rent_epoch(RENT_EXEMPT_RENT_EPOCH);
2217
2218        let TransactionLoadResult::Loaded(loaded_transaction) = load_result else {
2219            panic!("transaction loading failed");
2220        };
2221        assert_eq!(
2222            loaded_transaction,
2223            LoadedTransaction {
2224                accounts: vec![
2225                    (
2226                        key2.pubkey(),
2227                        mock_bank.accounts_map[&key2.pubkey()].0.clone()
2228                    ),
2229                    (
2230                        key1.pubkey(),
2231                        mock_bank.accounts_map[&key1.pubkey()].0.clone()
2232                    ),
2233                    (key3.pubkey(), account_data),
2234                ],
2235                touched_flags: Box::default(),
2236                fee_details: FeeDetails::default(),
2237                rollback_accounts: RollbackAccounts::default(),
2238                compute_budget: SVMTransactionExecutionBudget::default(),
2239                loaded_accounts_data_size,
2240            }
2241        );
2242    }
2243
2244    #[test]
2245    fn test_load_accounts_error() {
2246        let mock_bank = TestCallbacks::default();
2247        let mut account_loader = (&mock_bank).into();
2248        let rent = Rent::default();
2249
2250        let message = Message {
2251            account_keys: vec![Pubkey::new_from_array([0; 32])],
2252            header: MessageHeader::default(),
2253            instructions: vec![CompiledInstruction {
2254                program_id_index: 0,
2255                accounts: vec![],
2256                data: vec![],
2257            }],
2258            recent_blockhash: Hash::default(),
2259        };
2260
2261        let sanitized_message = new_unchecked_sanitized_message(message);
2262        let sanitized_transaction = SanitizedTransaction::new_for_tests(
2263            sanitized_message,
2264            vec![Signature::new_unique()],
2265            false,
2266        );
2267
2268        let validation_result =
2269            TransactionValidationResult::Loadable(ValidatedTransactionDetails::default());
2270
2271        let load_result = load_transaction(
2272            &mut account_loader,
2273            &sanitized_transaction,
2274            validation_result,
2275            &mut TransactionErrorMetrics::default(),
2276            &rent,
2277        );
2278
2279        assert!(matches!(
2280            load_result,
2281            TransactionLoadResult::FeesOnly(FeesOnlyTransaction {
2282                load_error: TransactionError::ProgramAccountNotFound,
2283                ..
2284            }),
2285        ));
2286
2287        let validation_result =
2288            TransactionValidationResult::Unprocessable(TransactionError::InvalidWritableAccount);
2289
2290        let load_result = load_transaction(
2291            &mut account_loader,
2292            &sanitized_transaction,
2293            validation_result,
2294            &mut TransactionErrorMetrics::default(),
2295            &rent,
2296        );
2297
2298        assert!(matches!(
2299            load_result,
2300            TransactionLoadResult::Unprocessable(TransactionError::InvalidWritableAccount),
2301        ));
2302    }
2303
2304    #[test]
2305    fn test_load_accounts_v1_instructions_sysvar_overflow() {
2306        const NUM_INSTRUCTIONS: usize = 64;
2307        const ACCOUNTS_PER_INSTRUCTION: usize = 31;
2308
2309        let fee_payer = Pubkey::new_unique();
2310        let instructions_sysvar = sysvar::instructions::id();
2311        let program = native_loader::id();
2312        let message = v1::Message::new(
2313            MessageHeader {
2314                num_required_signatures: 1,
2315                num_readonly_signed_accounts: 0,
2316                num_readonly_unsigned_accounts: 2,
2317            },
2318            v1::TransactionConfig::empty(),
2319            Hash::default(),
2320            vec![fee_payer, instructions_sysvar, program],
2321            vec![
2322                CompiledInstruction {
2323                    program_id_index: 2,
2324                    accounts: vec![1; ACCOUNTS_PER_INSTRUCTION],
2325                    data: vec![],
2326                };
2327                NUM_INSTRUCTIONS
2328            ],
2329        );
2330        message.validate().unwrap();
2331        assert!(
2332            1 + message.size() + Signature::default().as_ref().len() <= v1::MAX_TRANSACTION_SIZE
2333        );
2334
2335        let sanitized_message =
2336            SanitizedMessage::V1(v1::CachedMessage::new(message, &HashSet::new()));
2337        let mock_bank = TestCallbacks::default();
2338        let mut account_loader = (&mock_bank).into();
2339
2340        let fee_payer_account = AccountSharedData::new(200, 0, &Pubkey::default());
2341        let load_result = load_transaction(
2342            &mut account_loader,
2343            &sanitized_message,
2344            TransactionValidationResult::Loadable(ValidatedTransactionDetails {
2345                loaded_fee_payer_account: LoadedTransactionAccount {
2346                    account: fee_payer_account,
2347                    loaded_size: TRANSACTION_ACCOUNT_BASE_SIZE,
2348                },
2349                ..ValidatedTransactionDetails::default()
2350            }),
2351            &mut TransactionErrorMetrics::default(),
2352            &Rent::default(),
2353        );
2354
2355        assert!(matches!(
2356            load_result,
2357            TransactionLoadResult::FeesOnly(FeesOnlyTransaction {
2358                load_error: TransactionError::MaxLoadedAccountsDataSizeExceeded,
2359                ..
2360            }),
2361        ));
2362    }
2363
2364    #[test]
2365    fn test_update_rent_exempt_status_for_account() {
2366        let rent = Rent::default();
2367
2368        let min_exempt_balance = rent.minimum_balance(0);
2369        let mut account = AccountSharedData::from(Account {
2370            lamports: min_exempt_balance,
2371            ..Account::default()
2372        });
2373
2374        update_rent_exempt_status_for_account(&rent, &mut account);
2375        assert_eq!(account.rent_epoch(), RENT_EXEMPT_RENT_EPOCH);
2376    }
2377
2378    #[test]
2379    fn test_update_rent_exempt_status_for_rent_paying_account() {
2380        let rent = Rent::default();
2381
2382        let mut account = AccountSharedData::from(Account {
2383            lamports: 1,
2384            ..Account::default()
2385        });
2386
2387        update_rent_exempt_status_for_account(&rent, &mut account);
2388        assert_eq!(account.rent_epoch(), 0);
2389        assert_eq!(account.lamports(), 1);
2390    }
2391
2392    // Ensure `TransactionProcessingCallback::inspect_account()` is called when
2393    // loading accounts for transaction processing.
2394    #[test]
2395    fn test_inspect_account_non_fee_payer() {
2396        let mut mock_bank = TestCallbacks::default();
2397
2398        let address0 = Pubkey::new_unique(); // <-- fee payer
2399        let address1 = Pubkey::new_unique(); // <-- initially alive
2400        let address2 = Pubkey::new_unique(); // <-- initially dead
2401        let address3 = Pubkey::new_unique(); // <-- program
2402
2403        let mut account0 = AccountSharedData::default();
2404        account0.set_lamports(1_000_000_000);
2405        mock_bank
2406            .accounts_map
2407            .insert(address0, (account0.clone(), 1));
2408
2409        let mut account1 = AccountSharedData::default();
2410        account1.set_lamports(2_000_000_000);
2411        mock_bank
2412            .accounts_map
2413            .insert(address1, (account1.clone(), 1));
2414
2415        // account2 *not* added to the bank's accounts_map
2416
2417        let mut account3 = AccountSharedData::default();
2418        account3.set_lamports(4_000_000_000);
2419        account3.set_executable(true);
2420        account3.set_owner(bpf_loader::id());
2421        mock_bank
2422            .accounts_map
2423            .insert(address3, (account3.clone(), 0));
2424        let mut account_loader = (&mock_bank).into();
2425
2426        let message = Message {
2427            account_keys: vec![address0, address1, address2, address3],
2428            header: MessageHeader::default(),
2429            instructions: vec![
2430                CompiledInstruction {
2431                    program_id_index: 3,
2432                    accounts: vec![0],
2433                    data: vec![],
2434                },
2435                CompiledInstruction {
2436                    program_id_index: 3,
2437                    accounts: vec![1, 2],
2438                    data: vec![],
2439                },
2440                CompiledInstruction {
2441                    program_id_index: 3,
2442                    accounts: vec![1],
2443                    data: vec![],
2444                },
2445            ],
2446            recent_blockhash: Hash::new_unique(),
2447        };
2448        let sanitized_message = new_unchecked_sanitized_message(message);
2449        let sanitized_transaction = SanitizedTransaction::new_for_tests(
2450            sanitized_message,
2451            vec![Signature::new_unique()],
2452            false,
2453        );
2454        let validation_result =
2455            TransactionValidationResult::Loadable(ValidatedTransactionDetails {
2456                loaded_fee_payer_account: LoadedTransactionAccount {
2457                    account: account0.clone(),
2458                    ..LoadedTransactionAccount::default()
2459                },
2460                ..ValidatedTransactionDetails::default()
2461            });
2462        let _load_results = load_transaction(
2463            &mut account_loader,
2464            &sanitized_transaction,
2465            validation_result,
2466            &mut TransactionErrorMetrics::default(),
2467            &Rent::default(),
2468        );
2469
2470        // ensure the loaded accounts are inspected
2471        let mut actual_inspected_accounts: Vec<_> = mock_bank
2472            .inspected_accounts
2473            .borrow()
2474            .iter()
2475            .map(|(k, v)| (*k, v.clone()))
2476            .collect();
2477        actual_inspected_accounts.sort_unstable_by_key(|a| a.0);
2478
2479        let mut expected_inspected_accounts = vec![
2480            // *not* key0, since it is loaded during fee payer validation
2481            (address1, vec![(Some(account1), true)]),
2482            (address2, vec![(None, true)]),
2483            (address3, vec![(Some(account3), false)]),
2484        ];
2485        expected_inspected_accounts.sort_unstable_by_key(|a| a.0);
2486
2487        assert_eq!(actual_inspected_accounts, expected_inspected_accounts,);
2488    }
2489
2490    #[test]
2491    fn test_account_loader_wrappers() {
2492        let fee_payer = Pubkey::new_unique();
2493        let mut fee_payer_account = AccountSharedData::default();
2494        fee_payer_account.set_rent_epoch(u64::MAX);
2495        fee_payer_account.set_lamports(5000);
2496
2497        let mut mock_bank = TestCallbacks::default();
2498        mock_bank
2499            .accounts_map
2500            .insert(fee_payer, (fee_payer_account.clone(), 1));
2501
2502        // test without stored account
2503        let mut account_loader: AccountLoader<_> = (&mock_bank).into();
2504        assert_eq!(
2505            account_loader
2506                .load_transaction_account(&fee_payer, false)
2507                .unwrap()
2508                .account,
2509            fee_payer_account
2510        );
2511
2512        let mut account_loader: AccountLoader<_> = (&mock_bank).into();
2513        assert_eq!(
2514            account_loader
2515                .load_transaction_account(&fee_payer, true)
2516                .unwrap()
2517                .account,
2518            fee_payer_account
2519        );
2520
2521        let mut account_loader: AccountLoader<_> = (&mock_bank).into();
2522        assert_eq!(
2523            account_loader.load_account(&fee_payer).unwrap(),
2524            fee_payer_account
2525        );
2526
2527        let account_loader: AccountLoader<_> = (&mock_bank).into();
2528        assert_eq!(
2529            account_loader
2530                .get_account_shared_data(&fee_payer)
2531                .unwrap()
2532                .0,
2533            fee_payer_account
2534        );
2535
2536        // test with stored account
2537        let mut account_loader: AccountLoader<_> = (&mock_bank).into();
2538        account_loader.load_account(&fee_payer).unwrap();
2539
2540        assert_eq!(
2541            account_loader
2542                .load_transaction_account(&fee_payer, false)
2543                .unwrap()
2544                .account,
2545            fee_payer_account
2546        );
2547        assert_eq!(
2548            account_loader
2549                .load_transaction_account(&fee_payer, true)
2550                .unwrap()
2551                .account,
2552            fee_payer_account
2553        );
2554        assert_eq!(
2555            account_loader.load_account(&fee_payer).unwrap(),
2556            fee_payer_account
2557        );
2558        assert_eq!(
2559            account_loader
2560                .get_account_shared_data(&fee_payer)
2561                .unwrap()
2562                .0,
2563            fee_payer_account
2564        );
2565
2566        // drop the account and ensure all deliver the updated state
2567        fee_payer_account.set_lamports(0);
2568        account_loader.update_accounts_for_failed_tx(
2569            &RollbackAccounts::FeePayerOnly {
2570                fee_payer: (fee_payer, fee_payer_account),
2571            },
2572            0,
2573        );
2574
2575        assert_eq!(
2576            account_loader.load_transaction_account(&fee_payer, false),
2577            None
2578        );
2579        assert_eq!(
2580            account_loader.load_transaction_account(&fee_payer, true),
2581            None
2582        );
2583        assert_eq!(account_loader.load_account(&fee_payer), None);
2584        assert_eq!(account_loader.get_account_shared_data(&fee_payer), None);
2585    }
2586
2587    // note all magic numbers (how many accounts, how many instructions, how big to size buffers) are arbitrary
2588    // other than trying not to swamp programs with blank accounts and keep transaction size below the 64mb limit
2589    #[test]
2590    fn test_load_transaction_accounts_data_sizes() {
2591        let mut rng = rand::rng();
2592        let mut mock_bank = TestCallbacks::default();
2593
2594        // arbitrary accounts
2595        for _ in 0..128 {
2596            let account = AccountSharedData::create_from_existing_shared_data(
2597                1,
2598                Arc::new(vec![0; rng.random_range(0..128)]),
2599                Pubkey::new_unique(),
2600                rng.random(),
2601                u64::MAX,
2602            );
2603            mock_bank
2604                .accounts_map
2605                .insert(Pubkey::new_unique(), (account, 1));
2606        }
2607
2608        // fee-payers
2609        let mut fee_payers = vec![];
2610        for _ in 0..8 {
2611            let fee_payer = Pubkey::new_unique();
2612            let account = AccountSharedData::create_from_existing_shared_data(
2613                LAMPORTS_PER_SOL,
2614                Arc::new(vec![0; rng.random_range(0..32)]),
2615                system_program::id(),
2616                rng.random(),
2617                u64::MAX,
2618            );
2619            mock_bank.accounts_map.insert(fee_payer, (account, 1));
2620            fee_payers.push(fee_payer);
2621        }
2622
2623        // programs
2624        let mut loader_owned_accounts = vec![];
2625        let mut programdata_tracker = AHashMap::new();
2626        for loader in PROGRAM_OWNERS {
2627            for _ in 0..16 {
2628                let program_id = Pubkey::new_unique();
2629                let mut account = AccountSharedData::create_from_existing_shared_data(
2630                    1,
2631                    Arc::new(vec![0; rng.random_range(0..512)]),
2632                    *loader,
2633                    rng.random(),
2634                    u64::MAX,
2635                );
2636
2637                // give half loaderv3 accounts (if they're long enough) a valid programdata
2638                // a quarter a dead pointer and a quarter nothing
2639                // we set executable like a program because after the flag is disabled...
2640                // ...programdata and buffer accounts can be used as program ids without aborting loading
2641                // this will always fail at execution but we are merely testing the data size accounting here
2642                if *loader == bpf_loader_upgradeable::id() && account.data().len() >= 64 {
2643                    let programdata_address = Pubkey::new_unique();
2644                    let has_programdata = rng.random();
2645
2646                    if has_programdata {
2647                        let programdata_account =
2648                            AccountSharedData::create_from_existing_shared_data(
2649                                1,
2650                                Arc::new(vec![0; rng.random_range(0..512)]),
2651                                *loader,
2652                                rng.random(),
2653                                u64::MAX,
2654                            );
2655                        programdata_tracker.insert(
2656                            program_id,
2657                            (programdata_address, programdata_account.data().len()),
2658                        );
2659                        mock_bank
2660                            .accounts_map
2661                            .insert(programdata_address, (programdata_account, 1));
2662                        loader_owned_accounts.push(programdata_address);
2663                    }
2664
2665                    if has_programdata || rng.random() {
2666                        bincode::serialize_into(
2667                            account.data_as_mut_slice(),
2668                            &UpgradeableLoaderState::Program {
2669                                programdata_address,
2670                            },
2671                        )
2672                        .unwrap();
2673                    }
2674                }
2675
2676                mock_bank.accounts_map.insert(program_id, (account, 1));
2677                loader_owned_accounts.push(program_id);
2678            }
2679        }
2680
2681        let mut all_accounts = mock_bank.accounts_map.keys().copied().collect::<Vec<_>>();
2682
2683        // append some to-be-created accounts
2684        // this is to test that their size is 0 rather than 64
2685        for _ in 0..32 {
2686            all_accounts.push(Pubkey::new_unique());
2687        }
2688
2689        let mut account_loader = (&mock_bank).into();
2690
2691        // now generate arbitrary transactions using this accounts
2692        // we ensure valid fee-payers and that all program ids are loader-owned
2693        // otherwise any account can appear anywhere
2694        // some edge cases we hope to hit (not necessarily all in every run):
2695        // * programs used multiple times as program ids and/or normal accounts are counted once
2696        // * loaderv3 programdata used explicitly zero one or multiple times is counted once
2697        // * loaderv3 programs with missing programdata are allowed through
2698        // * loaderv3 programdata used as program id does nothing weird
2699        // * loaderv3 programdata used as a regular account does nothing weird
2700        // * the programdata conditions hold regardless of ordering
2701        for _ in 0..1024 {
2702            let mut instructions = vec![];
2703            for _ in 0..rng.random_range(1..8) {
2704                let mut accounts = vec![];
2705                for _ in 0..rng.random_range(1..16) {
2706                    all_accounts.shuffle(&mut rng);
2707                    let pubkey = all_accounts[0];
2708
2709                    accounts.push(AccountMeta {
2710                        pubkey,
2711                        is_writable: rng.random(),
2712                        is_signer: rng.random() && rng.random(),
2713                    });
2714                }
2715
2716                loader_owned_accounts.shuffle(&mut rng);
2717                let program_id = loader_owned_accounts[0];
2718                instructions.push(Instruction {
2719                    accounts,
2720                    program_id,
2721                    data: vec![],
2722                });
2723            }
2724
2725            fee_payers.shuffle(&mut rng);
2726            let fee_payer = fee_payers[0];
2727            let fee_payer_account = mock_bank.accounts_map.get(&fee_payer).cloned().unwrap().0;
2728
2729            let transaction = SanitizedTransaction::from_transaction_for_tests(
2730                Transaction::new_with_payer(&instructions, Some(&fee_payer)),
2731            );
2732
2733            let mut expected_size = 0;
2734            let mut counted_programdatas = transaction
2735                .account_keys()
2736                .iter()
2737                .copied()
2738                .collect::<AHashSet<_>>();
2739
2740            for pubkey in transaction.account_keys().iter() {
2741                if let Some((account, _last_modification_slot)) = mock_bank.accounts_map.get(pubkey)
2742                {
2743                    expected_size += TRANSACTION_ACCOUNT_BASE_SIZE + account.data().len();
2744                };
2745
2746                if let Some((programdata_address, programdata_size)) =
2747                    programdata_tracker.get(pubkey)
2748                    && counted_programdatas.get(programdata_address).is_none()
2749                {
2750                    expected_size += TRANSACTION_ACCOUNT_BASE_SIZE + programdata_size;
2751                    counted_programdatas.insert(*programdata_address);
2752                }
2753            }
2754
2755            assert!(expected_size <= MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES.get() as usize);
2756
2757            let mut loaded_transaction_data_size =
2758                LoadedTransactionDataSize::with_max_size(MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES.get());
2759
2760            load_transaction_accounts(
2761                &mut account_loader,
2762                &transaction,
2763                LoadedTransactionAccount {
2764                    loaded_size: TRANSACTION_ACCOUNT_BASE_SIZE + fee_payer_account.data().len(),
2765                    account: fee_payer_account,
2766                },
2767                &mut loaded_transaction_data_size,
2768                &mut TransactionErrorMetrics::default(),
2769                &Rent::default(),
2770            )
2771            .unwrap();
2772
2773            assert_eq!(
2774                loaded_transaction_data_size.loaded_accounts_data_size,
2775                expected_size as u32,
2776            );
2777        }
2778    }
2779
2780    #[test]
2781    fn test_loader_aliasing() {
2782        let mut mock_bank = TestCallbacks::default();
2783
2784        let hit_address = Pubkey::new_unique();
2785        let miss_address = Pubkey::new_unique();
2786
2787        let expected_hit_account = AccountSharedData::default();
2788        mock_bank
2789            .accounts_map
2790            .insert(hit_address, (expected_hit_account.clone(), 1));
2791
2792        let mut account_loader: AccountLoader<_> = (&mock_bank).into();
2793
2794        // load hits accounts-db, same account is stored
2795        account_loader.load_account(&hit_address);
2796        let actual_hit_account = account_loader.loaded_accounts.get(&hit_address);
2797
2798        assert_eq!(actual_hit_account.as_ref().unwrap().0, expected_hit_account);
2799        assert_eq!(actual_hit_account.as_ref().unwrap().1, 1);
2800        assert!(Arc::ptr_eq(
2801            &actual_hit_account.unwrap().0.data_clone(),
2802            &expected_hit_account.data_clone()
2803        ));
2804
2805        // reload doesn't affect this
2806        account_loader.load_account(&hit_address);
2807        let actual_hit_account = account_loader.loaded_accounts.get(&hit_address);
2808
2809        assert_eq!(actual_hit_account.as_ref().unwrap().0, expected_hit_account);
2810        assert_eq!(actual_hit_account.as_ref().unwrap().1, 1);
2811        assert!(Arc::ptr_eq(
2812            &actual_hit_account.unwrap().0.data_clone(),
2813            &expected_hit_account.data_clone()
2814        ));
2815
2816        // load misses accounts-db, placeholder is inserted
2817        account_loader.load_account(&miss_address);
2818        let expected_miss_account = account_loader
2819            .loaded_accounts
2820            .get(&miss_address)
2821            .unwrap()
2822            .clone();
2823
2824        assert!(!Arc::ptr_eq(
2825            &expected_miss_account.0.data_clone(),
2826            &expected_hit_account.data_clone()
2827        ));
2828
2829        // reload keeps the same placeholder
2830        account_loader.load_account(&miss_address);
2831        let actual_miss_account = account_loader.loaded_accounts.get(&miss_address);
2832
2833        assert_eq!(actual_miss_account, Some(&expected_miss_account));
2834        assert!(Arc::ptr_eq(
2835            &actual_miss_account.unwrap().0.data_clone(),
2836            &expected_miss_account.0.data_clone()
2837        ));
2838    }
2839}