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