solana_svm/
account_loader.rs

1use {
2    crate::{
3        account_overrides::AccountOverrides,
4        nonce_info::NonceInfo,
5        rollback_accounts::RollbackAccounts,
6        transaction_error_metrics::TransactionErrorMetrics,
7        transaction_processing_callback::{AccountState, TransactionProcessingCallback},
8    },
9    itertools::Itertools,
10    solana_compute_budget::compute_budget_limits::ComputeBudgetLimits,
11    solana_feature_set::{self as feature_set, FeatureSet},
12    solana_program_runtime::loaded_programs::ProgramCacheForTxBatch,
13    solana_sdk::{
14        account::{Account, AccountSharedData, ReadableAccount, WritableAccount},
15        fee::FeeDetails,
16        native_loader,
17        nonce::State as NonceState,
18        pubkey::Pubkey,
19        rent::RentDue,
20        rent_collector::{CollectedInfo, RENT_EXEMPT_RENT_EPOCH},
21        rent_debits::RentDebits,
22        saturating_add_assign,
23        sysvar::{
24            self,
25            instructions::{construct_instructions_data, BorrowedAccountMeta, BorrowedInstruction},
26        },
27        transaction::{Result, TransactionError},
28        transaction_context::{IndexOfAccount, TransactionAccount},
29    },
30    solana_svm_rent_collector::svm_rent_collector::SVMRentCollector,
31    solana_svm_transaction::svm_message::SVMMessage,
32    solana_system_program::{get_system_account_kind, SystemAccountKind},
33    std::{collections::HashMap, num::NonZeroU32},
34};
35
36// for the load instructions
37pub(crate) type TransactionRent = u64;
38pub(crate) type TransactionProgramIndices = Vec<Vec<IndexOfAccount>>;
39pub type TransactionCheckResult = Result<CheckedTransactionDetails>;
40pub type TransactionValidationResult = Result<ValidatedTransactionDetails>;
41
42#[derive(PartialEq, Eq, Debug)]
43pub enum TransactionLoadResult {
44    /// All transaction accounts were loaded successfully
45    Loaded(LoadedTransaction),
46    /// Some transaction accounts needed for execution were unable to be loaded
47    /// but the fee payer and any nonce account needed for fee collection were
48    /// loaded successfully
49    FeesOnly(FeesOnlyTransaction),
50    /// Some transaction accounts needed for fee collection were unable to be
51    /// loaded
52    NotLoaded(TransactionError),
53}
54
55#[derive(PartialEq, Eq, Debug, Clone)]
56pub struct CheckedTransactionDetails {
57    pub nonce: Option<NonceInfo>,
58    pub lamports_per_signature: u64,
59}
60
61#[derive(PartialEq, Eq, Debug, Clone)]
62#[cfg_attr(feature = "dev-context-only-utils", derive(Default))]
63pub struct ValidatedTransactionDetails {
64    pub rollback_accounts: RollbackAccounts,
65    pub compute_budget_limits: ComputeBudgetLimits,
66    pub fee_details: FeeDetails,
67    pub loaded_fee_payer_account: LoadedTransactionAccount,
68}
69
70#[derive(PartialEq, Eq, Debug, Clone)]
71#[cfg_attr(feature = "dev-context-only-utils", derive(Default))]
72pub struct LoadedTransactionAccount {
73    pub(crate) account: AccountSharedData,
74    pub(crate) loaded_size: usize,
75    pub(crate) rent_collected: u64,
76}
77
78#[derive(PartialEq, Eq, Debug, Clone)]
79#[cfg_attr(feature = "dev-context-only-utils", derive(Default))]
80pub struct LoadedTransaction {
81    pub accounts: Vec<TransactionAccount>,
82    pub program_indices: TransactionProgramIndices,
83    pub fee_details: FeeDetails,
84    pub rollback_accounts: RollbackAccounts,
85    pub compute_budget_limits: ComputeBudgetLimits,
86    pub rent: TransactionRent,
87    pub rent_debits: RentDebits,
88    pub loaded_accounts_data_size: u32,
89}
90
91#[derive(PartialEq, Eq, Debug, Clone)]
92pub struct FeesOnlyTransaction {
93    pub load_error: TransactionError,
94    pub rollback_accounts: RollbackAccounts,
95    pub fee_details: FeeDetails,
96}
97
98/// Collect rent from an account if rent is still enabled and regardless of
99/// whether rent is enabled, set the rent epoch to u64::MAX if the account is
100/// rent exempt.
101pub fn collect_rent_from_account(
102    feature_set: &FeatureSet,
103    rent_collector: &dyn SVMRentCollector,
104    address: &Pubkey,
105    account: &mut AccountSharedData,
106) -> CollectedInfo {
107    if !feature_set.is_active(&feature_set::disable_rent_fees_collection::id()) {
108        rent_collector.collect_rent(address, account)
109    } else {
110        // When rent fee collection is disabled, we won't collect rent for any account. If there
111        // are any rent paying accounts, their `rent_epoch` won't change either. However, if the
112        // account itself is rent-exempted but its `rent_epoch` is not u64::MAX, we will set its
113        // `rent_epoch` to u64::MAX. In such case, the behavior stays the same as before.
114        if account.rent_epoch() != RENT_EXEMPT_RENT_EPOCH
115            && rent_collector.get_rent_due(
116                account.lamports(),
117                account.data().len(),
118                account.rent_epoch(),
119            ) == RentDue::Exempt
120        {
121            account.set_rent_epoch(RENT_EXEMPT_RENT_EPOCH);
122        }
123
124        CollectedInfo::default()
125    }
126}
127
128/// Check whether the payer_account is capable of paying the fee. The
129/// side effect is to subtract the fee amount from the payer_account
130/// balance of lamports. If the payer_acount is not able to pay the
131/// fee, the error_metrics is incremented, and a specific error is
132/// returned.
133pub fn validate_fee_payer(
134    payer_address: &Pubkey,
135    payer_account: &mut AccountSharedData,
136    payer_index: IndexOfAccount,
137    error_metrics: &mut TransactionErrorMetrics,
138    rent_collector: &dyn SVMRentCollector,
139    fee: u64,
140) -> Result<()> {
141    if payer_account.lamports() == 0 {
142        error_metrics.account_not_found += 1;
143        return Err(TransactionError::AccountNotFound);
144    }
145    let system_account_kind = get_system_account_kind(payer_account).ok_or_else(|| {
146        error_metrics.invalid_account_for_fee += 1;
147        TransactionError::InvalidAccountForFee
148    })?;
149    let min_balance = match system_account_kind {
150        SystemAccountKind::System => 0,
151        SystemAccountKind::Nonce => {
152            // Should we ever allow a fees charge to zero a nonce account's
153            // balance. The state MUST be set to uninitialized in that case
154            rent_collector
155                .get_rent()
156                .minimum_balance(NonceState::size())
157        }
158    };
159
160    payer_account
161        .lamports()
162        .checked_sub(min_balance)
163        .and_then(|v| v.checked_sub(fee))
164        .ok_or_else(|| {
165            error_metrics.insufficient_funds += 1;
166            TransactionError::InsufficientFundsForFee
167        })?;
168
169    let payer_pre_rent_state = rent_collector.get_account_rent_state(payer_account);
170    payer_account
171        .checked_sub_lamports(fee)
172        .map_err(|_| TransactionError::InsufficientFundsForFee)?;
173
174    let payer_post_rent_state = rent_collector.get_account_rent_state(payer_account);
175    rent_collector.check_rent_state_with_account(
176        &payer_pre_rent_state,
177        &payer_post_rent_state,
178        payer_address,
179        payer_account,
180        payer_index,
181    )
182}
183
184/// Collect information about accounts used in txs transactions and
185/// return vector of tuples, one for each transaction in the
186/// batch. Each tuple contains struct of information about accounts as
187/// its first element and an optional transaction nonce info as its
188/// second element.
189pub(crate) fn load_accounts<CB: TransactionProcessingCallback>(
190    callbacks: &CB,
191    txs: &[impl SVMMessage],
192    validation_results: Vec<TransactionValidationResult>,
193    error_metrics: &mut TransactionErrorMetrics,
194    account_overrides: Option<&AccountOverrides>,
195    feature_set: &FeatureSet,
196    rent_collector: &dyn SVMRentCollector,
197    program_accounts: &HashMap<Pubkey, (&Pubkey, u64)>,
198    loaded_programs: &ProgramCacheForTxBatch,
199) -> Vec<TransactionLoadResult> {
200    txs.iter()
201        .zip(validation_results)
202        .map(|(transaction, validation_result)| {
203            load_transaction(
204                callbacks,
205                transaction,
206                validation_result,
207                error_metrics,
208                account_overrides,
209                feature_set,
210                rent_collector,
211                program_accounts,
212                loaded_programs,
213            )
214        })
215        .collect()
216}
217
218fn load_transaction<CB: TransactionProcessingCallback>(
219    callbacks: &CB,
220    message: &impl SVMMessage,
221    validation_result: TransactionValidationResult,
222    error_metrics: &mut TransactionErrorMetrics,
223    account_overrides: Option<&AccountOverrides>,
224    feature_set: &FeatureSet,
225    rent_collector: &dyn SVMRentCollector,
226    program_accounts: &HashMap<Pubkey, (&Pubkey, u64)>,
227    loaded_programs: &ProgramCacheForTxBatch,
228) -> TransactionLoadResult {
229    match validation_result {
230        Err(e) => TransactionLoadResult::NotLoaded(e),
231        Ok(tx_details) => {
232            let load_result = load_transaction_accounts(
233                callbacks,
234                message,
235                tx_details.loaded_fee_payer_account,
236                &tx_details.compute_budget_limits,
237                error_metrics,
238                account_overrides,
239                feature_set,
240                rent_collector,
241                program_accounts,
242                loaded_programs,
243            );
244
245            match load_result {
246                Ok(loaded_tx_accounts) => TransactionLoadResult::Loaded(LoadedTransaction {
247                    accounts: loaded_tx_accounts.accounts,
248                    program_indices: loaded_tx_accounts.program_indices,
249                    fee_details: tx_details.fee_details,
250                    rent: loaded_tx_accounts.rent,
251                    rent_debits: loaded_tx_accounts.rent_debits,
252                    rollback_accounts: tx_details.rollback_accounts,
253                    compute_budget_limits: tx_details.compute_budget_limits,
254                    loaded_accounts_data_size: loaded_tx_accounts.loaded_accounts_data_size,
255                }),
256                Err(err) => TransactionLoadResult::FeesOnly(FeesOnlyTransaction {
257                    load_error: err,
258                    fee_details: tx_details.fee_details,
259                    rollback_accounts: tx_details.rollback_accounts,
260                }),
261            }
262        }
263    }
264}
265
266#[derive(PartialEq, Eq, Debug, Clone)]
267struct LoadedTransactionAccounts {
268    pub accounts: Vec<TransactionAccount>,
269    pub program_indices: TransactionProgramIndices,
270    pub rent: TransactionRent,
271    pub rent_debits: RentDebits,
272    pub loaded_accounts_data_size: u32,
273}
274
275#[allow(clippy::too_many_arguments)]
276fn load_transaction_accounts<CB: TransactionProcessingCallback>(
277    callbacks: &CB,
278    message: &impl SVMMessage,
279    loaded_fee_payer_account: LoadedTransactionAccount,
280    compute_budget_limits: &ComputeBudgetLimits,
281    error_metrics: &mut TransactionErrorMetrics,
282    account_overrides: Option<&AccountOverrides>,
283    feature_set: &FeatureSet,
284    rent_collector: &dyn SVMRentCollector,
285    program_accounts: &HashMap<Pubkey, (&Pubkey, u64)>,
286    loaded_programs: &ProgramCacheForTxBatch,
287) -> Result<LoadedTransactionAccounts> {
288    let mut tx_rent: TransactionRent = 0;
289    let account_keys = message.account_keys();
290    let mut accounts = Vec::with_capacity(account_keys.len());
291    let mut accounts_found = Vec::with_capacity(account_keys.len());
292    let mut rent_debits = RentDebits::default();
293    let mut accumulated_accounts_data_size: u32 = 0;
294
295    let instruction_accounts = message
296        .instructions_iter()
297        .flat_map(|instruction| instruction.accounts)
298        .unique()
299        .collect::<Vec<&u8>>();
300
301    let mut collect_loaded_account = |key, (loaded_account, found)| -> Result<()> {
302        let LoadedTransactionAccount {
303            account,
304            loaded_size,
305            rent_collected,
306        } = loaded_account;
307
308        accumulate_and_check_loaded_account_data_size(
309            &mut accumulated_accounts_data_size,
310            loaded_size,
311            compute_budget_limits.loaded_accounts_bytes,
312            error_metrics,
313        )?;
314
315        tx_rent += rent_collected;
316        rent_debits.insert(key, rent_collected, account.lamports());
317
318        accounts.push((*key, account));
319        accounts_found.push(found);
320        Ok(())
321    };
322
323    // Since the fee payer is always the first account, collect it first. Note
324    // that account overrides are already applied during fee payer validation so
325    // it's fine to use the fee payer directly here rather than checking account
326    // overrides again.
327    collect_loaded_account(message.fee_payer(), (loaded_fee_payer_account, true))?;
328
329    // Attempt to load and collect remaining non-fee payer accounts
330    for (account_index, account_key) in account_keys.iter().enumerate().skip(1) {
331        let (loaded_account, account_found) = load_transaction_account(
332            callbacks,
333            message,
334            account_key,
335            account_index,
336            &instruction_accounts[..],
337            account_overrides,
338            feature_set,
339            rent_collector,
340            program_accounts,
341            loaded_programs,
342        )?;
343        collect_loaded_account(account_key, (loaded_account, account_found))?;
344    }
345
346    let builtins_start_index = accounts.len();
347    let program_indices = message
348        .instructions_iter()
349        .map(|instruction| {
350            let mut account_indices = Vec::with_capacity(2);
351            let program_index = instruction.program_id_index as usize;
352            // This command may never return error, because the transaction is sanitized
353            let (program_id, program_account) = accounts
354                .get(program_index)
355                .ok_or(TransactionError::ProgramAccountNotFound)?;
356            if native_loader::check_id(program_id) {
357                return Ok(account_indices);
358            }
359
360            let account_found = accounts_found.get(program_index).unwrap_or(&true);
361            if !account_found {
362                error_metrics.account_not_found += 1;
363                return Err(TransactionError::ProgramAccountNotFound);
364            }
365
366            if !program_account.executable() {
367                error_metrics.invalid_program_for_execution += 1;
368                return Err(TransactionError::InvalidProgramForExecution);
369            }
370            account_indices.insert(0, program_index as IndexOfAccount);
371            let owner_id = program_account.owner();
372            if native_loader::check_id(owner_id) {
373                return Ok(account_indices);
374            }
375            if !accounts
376                .get(builtins_start_index..)
377                .ok_or(TransactionError::ProgramAccountNotFound)?
378                .iter()
379                .any(|(key, _)| key == owner_id)
380            {
381                if let Some(owner_account) = callbacks.get_account_shared_data(owner_id) {
382                    if !native_loader::check_id(owner_account.owner())
383                        || !owner_account.executable()
384                    {
385                        error_metrics.invalid_program_for_execution += 1;
386                        return Err(TransactionError::InvalidProgramForExecution);
387                    }
388                    accumulate_and_check_loaded_account_data_size(
389                        &mut accumulated_accounts_data_size,
390                        owner_account.data().len(),
391                        compute_budget_limits.loaded_accounts_bytes,
392                        error_metrics,
393                    )?;
394                    accounts.push((*owner_id, owner_account));
395                } else {
396                    error_metrics.account_not_found += 1;
397                    return Err(TransactionError::ProgramAccountNotFound);
398                }
399            }
400            Ok(account_indices)
401        })
402        .collect::<Result<Vec<Vec<IndexOfAccount>>>>()?;
403
404    Ok(LoadedTransactionAccounts {
405        accounts,
406        program_indices,
407        rent: tx_rent,
408        rent_debits,
409        loaded_accounts_data_size: accumulated_accounts_data_size,
410    })
411}
412
413#[allow(clippy::too_many_arguments)]
414fn load_transaction_account<CB: TransactionProcessingCallback>(
415    callbacks: &CB,
416    message: &impl SVMMessage,
417    account_key: &Pubkey,
418    account_index: usize,
419    instruction_accounts: &[&u8],
420    account_overrides: Option<&AccountOverrides>,
421    feature_set: &FeatureSet,
422    rent_collector: &dyn SVMRentCollector,
423    program_accounts: &HashMap<Pubkey, (&Pubkey, u64)>,
424    loaded_programs: &ProgramCacheForTxBatch,
425) -> Result<(LoadedTransactionAccount, bool)> {
426    let mut account_found = true;
427    let disable_account_loader_special_case =
428        feature_set.is_active(&feature_set::disable_account_loader_special_case::id());
429    let is_instruction_account = u8::try_from(account_index)
430        .map(|i| instruction_accounts.contains(&&i))
431        .unwrap_or(false);
432    let is_writable = message.is_writable(account_index);
433    let loaded_account = if solana_sdk::sysvar::instructions::check_id(account_key) {
434        // Since the instructions sysvar is constructed by the SVM and modified
435        // for each transaction instruction, it cannot be overridden.
436        LoadedTransactionAccount {
437            loaded_size: 0,
438            account: construct_instructions_account(message),
439            rent_collected: 0,
440        }
441    } else if let Some(account_override) =
442        account_overrides.and_then(|overrides| overrides.get(account_key))
443    {
444        LoadedTransactionAccount {
445            loaded_size: account_override.data().len(),
446            account: account_override.clone(),
447            rent_collected: 0,
448        }
449    } else if let Some(program) =
450        (!disable_account_loader_special_case && !is_instruction_account && !is_writable)
451            .then_some(())
452            .and_then(|_| loaded_programs.find(account_key))
453    {
454        // Optimization to skip loading of accounts which are only used as
455        // programs in top-level instructions and not passed as instruction accounts.
456        LoadedTransactionAccount {
457            loaded_size: program.account_size,
458            account: account_shared_data_from_program(account_key, program_accounts)?,
459            rent_collected: 0,
460        }
461    } else {
462        callbacks
463            .get_account_shared_data(account_key)
464            .map(|mut account| {
465                // Inspect the account prior to collecting rent, since
466                // rent collection can modify the account.
467                callbacks.inspect_account(account_key, AccountState::Alive(&account), is_writable);
468
469                let rent_collected = if is_writable {
470                    collect_rent_from_account(
471                        feature_set,
472                        rent_collector,
473                        account_key,
474                        &mut account,
475                    )
476                    .rent_amount
477                } else {
478                    0
479                };
480
481                LoadedTransactionAccount {
482                    loaded_size: account.data().len(),
483                    account,
484                    rent_collected,
485                }
486            })
487            .unwrap_or_else(|| {
488                callbacks.inspect_account(account_key, AccountState::Dead, is_writable);
489
490                account_found = false;
491                let mut default_account = AccountSharedData::default();
492
493                // All new accounts must be rent-exempt (enforced in Bank::execute_loaded_transaction).
494                // Currently, rent collection sets rent_epoch to u64::MAX, but initializing the account
495                // with this field already set would allow us to skip rent collection for these accounts.
496                default_account.set_rent_epoch(RENT_EXEMPT_RENT_EPOCH);
497                LoadedTransactionAccount {
498                    loaded_size: default_account.data().len(),
499                    account: default_account,
500                    rent_collected: 0,
501                }
502            })
503    };
504
505    Ok((loaded_account, account_found))
506}
507
508fn account_shared_data_from_program(
509    key: &Pubkey,
510    program_accounts: &HashMap<Pubkey, (&Pubkey, u64)>,
511) -> Result<AccountSharedData> {
512    // It's an executable program account. The program is already loaded in the cache.
513    // So the account data is not needed. Return a dummy AccountSharedData with meta
514    // information.
515    let mut program_account = AccountSharedData::default();
516    let (program_owner, _count) = program_accounts
517        .get(key)
518        .ok_or(TransactionError::AccountNotFound)?;
519    program_account.set_owner(**program_owner);
520    program_account.set_executable(true);
521    Ok(program_account)
522}
523
524/// Accumulate loaded account data size into `accumulated_accounts_data_size`.
525/// Returns TransactionErr::MaxLoadedAccountsDataSizeExceeded if
526/// `accumulated_accounts_data_size` exceeds
527/// `requested_loaded_accounts_data_size_limit`.
528fn accumulate_and_check_loaded_account_data_size(
529    accumulated_loaded_accounts_data_size: &mut u32,
530    account_data_size: usize,
531    requested_loaded_accounts_data_size_limit: NonZeroU32,
532    error_metrics: &mut TransactionErrorMetrics,
533) -> Result<()> {
534    let Ok(account_data_size) = u32::try_from(account_data_size) else {
535        error_metrics.max_loaded_accounts_data_size_exceeded += 1;
536        return Err(TransactionError::MaxLoadedAccountsDataSizeExceeded);
537    };
538    saturating_add_assign!(*accumulated_loaded_accounts_data_size, account_data_size);
539    if *accumulated_loaded_accounts_data_size > requested_loaded_accounts_data_size_limit.get() {
540        error_metrics.max_loaded_accounts_data_size_exceeded += 1;
541        Err(TransactionError::MaxLoadedAccountsDataSizeExceeded)
542    } else {
543        Ok(())
544    }
545}
546
547fn construct_instructions_account(message: &impl SVMMessage) -> AccountSharedData {
548    let account_keys = message.account_keys();
549    let mut decompiled_instructions = Vec::with_capacity(message.num_instructions());
550    for (program_id, instruction) in message.program_instructions_iter() {
551        let accounts = instruction
552            .accounts
553            .iter()
554            .map(|account_index| {
555                let account_index = usize::from(*account_index);
556                BorrowedAccountMeta {
557                    is_signer: message.is_signer(account_index),
558                    is_writable: message.is_writable(account_index),
559                    pubkey: account_keys.get(account_index).unwrap(),
560                }
561            })
562            .collect();
563
564        decompiled_instructions.push(BorrowedInstruction {
565            accounts,
566            data: instruction.data,
567            program_id,
568        });
569    }
570
571    AccountSharedData::from(Account {
572        data: construct_instructions_data(&decompiled_instructions),
573        owner: sysvar::id(),
574        ..Account::default()
575    })
576}
577
578#[cfg(test)]
579mod tests {
580    use {
581        super::*,
582        crate::{
583            transaction_account_state_info::TransactionAccountStateInfo,
584            transaction_processing_callback::TransactionProcessingCallback,
585        },
586        nonce::state::Versions as NonceVersions,
587        solana_compute_budget::{compute_budget::ComputeBudget, compute_budget_limits},
588        solana_feature_set::FeatureSet,
589        solana_program_runtime::loaded_programs::{
590            ProgramCacheEntry, ProgramCacheEntryOwner, ProgramCacheEntryType,
591            ProgramCacheForTxBatch,
592        },
593        solana_rbpf::program::BuiltinProgram,
594        solana_sdk::{
595            account::{Account, AccountSharedData, ReadableAccount, WritableAccount},
596            bpf_loader,
597            bpf_loader_upgradeable::{self, UpgradeableLoaderState},
598            epoch_schedule::EpochSchedule,
599            hash::Hash,
600            instruction::{AccountMeta, CompiledInstruction, Instruction},
601            message::{
602                v0::{LoadedAddresses, LoadedMessage},
603                LegacyMessage, Message, MessageHeader, SanitizedMessage,
604            },
605            native_loader,
606            native_token::{sol_to_lamports, LAMPORTS_PER_SOL},
607            nonce,
608            pubkey::Pubkey,
609            rent::Rent,
610            rent_collector::{RentCollector, RENT_EXEMPT_RENT_EPOCH},
611            rent_debits::RentDebits,
612            reserved_account_keys::ReservedAccountKeys,
613            signature::{Keypair, Signature, Signer},
614            system_program, system_transaction, sysvar,
615            transaction::{Result, SanitizedTransaction, Transaction, TransactionError},
616            transaction_context::{TransactionAccount, TransactionContext},
617        },
618        std::{borrow::Cow, cell::RefCell, collections::HashMap, fs::File, io::Read, sync::Arc},
619    };
620
621    #[derive(Default)]
622    struct TestCallbacks {
623        accounts_map: HashMap<Pubkey, AccountSharedData>,
624        #[allow(clippy::type_complexity)]
625        inspected_accounts:
626            RefCell<HashMap<Pubkey, Vec<(Option<AccountSharedData>, /* is_writable */ bool)>>>,
627    }
628
629    impl TransactionProcessingCallback for TestCallbacks {
630        fn account_matches_owners(&self, _account: &Pubkey, _owners: &[Pubkey]) -> Option<usize> {
631            None
632        }
633
634        fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option<AccountSharedData> {
635            self.accounts_map.get(pubkey).cloned()
636        }
637
638        fn inspect_account(
639            &self,
640            address: &Pubkey,
641            account_state: AccountState,
642            is_writable: bool,
643        ) {
644            let account = match account_state {
645                AccountState::Dead => None,
646                AccountState::Alive(account) => Some(account.clone()),
647            };
648            self.inspected_accounts
649                .borrow_mut()
650                .entry(*address)
651                .or_default()
652                .push((account, is_writable));
653        }
654    }
655
656    fn load_accounts_with_features_and_rent(
657        tx: Transaction,
658        accounts: &[TransactionAccount],
659        rent_collector: &RentCollector,
660        error_metrics: &mut TransactionErrorMetrics,
661        feature_set: &mut FeatureSet,
662    ) -> Vec<TransactionLoadResult> {
663        feature_set.deactivate(&feature_set::disable_rent_fees_collection::id());
664        let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(tx);
665        let fee_payer_account = accounts[0].1.clone();
666        let mut accounts_map = HashMap::new();
667        for (pubkey, account) in accounts {
668            accounts_map.insert(*pubkey, account.clone());
669        }
670        let callbacks = TestCallbacks {
671            accounts_map,
672            ..Default::default()
673        };
674        load_accounts(
675            &callbacks,
676            &[sanitized_tx],
677            vec![Ok(ValidatedTransactionDetails {
678                loaded_fee_payer_account: LoadedTransactionAccount {
679                    account: fee_payer_account,
680                    ..LoadedTransactionAccount::default()
681                },
682                ..ValidatedTransactionDetails::default()
683            })],
684            error_metrics,
685            None,
686            feature_set,
687            rent_collector,
688            &HashMap::new(),
689            &ProgramCacheForTxBatch::default(),
690        )
691    }
692
693    /// get a feature set with all features activated
694    /// with the optional except of 'exclude'
695    fn all_features_except(exclude: Option<&[Pubkey]>) -> FeatureSet {
696        let mut features = FeatureSet::all_enabled();
697        if let Some(exclude) = exclude {
698            features.active.retain(|k, _v| !exclude.contains(k));
699        }
700        features
701    }
702
703    fn new_unchecked_sanitized_message(message: Message) -> SanitizedMessage {
704        SanitizedMessage::Legacy(LegacyMessage::new(
705            message,
706            &ReservedAccountKeys::empty_key_set(),
707        ))
708    }
709
710    fn new_unchecked_sanitized_transaction_with_writable_program(
711        program_id: Pubkey,
712        fee_payer: Pubkey,
713    ) -> SanitizedTransaction {
714        let mut message = Message::new(
715            &[Instruction::new_with_bytes(program_id, &[], vec![])],
716            Some(&fee_payer),
717        );
718        message.header.num_readonly_unsigned_accounts = 0;
719
720        let legacy_message = LegacyMessage {
721            message: Cow::Owned(message),
722            is_writable_account_cache: vec![true, true],
723        };
724
725        SanitizedTransaction::new_for_tests(
726            SanitizedMessage::Legacy(legacy_message),
727            vec![Signature::default()],
728            false,
729        )
730    }
731
732    fn load_accounts_aux_test(
733        tx: Transaction,
734        accounts: &[TransactionAccount],
735        error_metrics: &mut TransactionErrorMetrics,
736    ) -> Vec<TransactionLoadResult> {
737        load_accounts_with_features_and_rent(
738            tx,
739            accounts,
740            &RentCollector::default(),
741            error_metrics,
742            &mut FeatureSet::all_enabled(),
743        )
744    }
745
746    fn load_accounts_with_excluded_features(
747        tx: Transaction,
748        accounts: &[TransactionAccount],
749        error_metrics: &mut TransactionErrorMetrics,
750        exclude_features: Option<&[Pubkey]>,
751    ) -> Vec<TransactionLoadResult> {
752        load_accounts_with_features_and_rent(
753            tx,
754            accounts,
755            &RentCollector::default(),
756            error_metrics,
757            &mut all_features_except(exclude_features),
758        )
759    }
760
761    #[test]
762    fn test_load_accounts_unknown_program_id() {
763        let mut accounts: Vec<TransactionAccount> = Vec::new();
764        let mut error_metrics = TransactionErrorMetrics::default();
765
766        let keypair = Keypair::new();
767        let key0 = keypair.pubkey();
768        let key1 = Pubkey::from([5u8; 32]);
769
770        let account = AccountSharedData::new(1, 0, &Pubkey::default());
771        accounts.push((key0, account));
772
773        let account = AccountSharedData::new(2, 1, &Pubkey::default());
774        accounts.push((key1, account));
775
776        let instructions = vec![CompiledInstruction::new(1, &(), vec![0])];
777        let tx = Transaction::new_with_compiled_instructions(
778            &[&keypair],
779            &[],
780            Hash::default(),
781            vec![Pubkey::default()],
782            instructions,
783        );
784
785        let load_results = load_accounts_aux_test(tx, &accounts, &mut error_metrics);
786
787        assert_eq!(error_metrics.account_not_found, 1);
788        assert_eq!(load_results.len(), 1);
789        assert!(matches!(
790            load_results[0],
791            TransactionLoadResult::FeesOnly(FeesOnlyTransaction {
792                load_error: TransactionError::ProgramAccountNotFound,
793                ..
794            }),
795        ));
796    }
797
798    #[test]
799    fn test_load_accounts_no_loaders() {
800        let mut accounts: Vec<TransactionAccount> = Vec::new();
801        let mut error_metrics = TransactionErrorMetrics::default();
802
803        let keypair = Keypair::new();
804        let key0 = keypair.pubkey();
805        let key1 = Pubkey::from([5u8; 32]);
806
807        let mut account = AccountSharedData::new(1, 0, &Pubkey::default());
808        account.set_rent_epoch(1);
809        accounts.push((key0, account));
810
811        let mut account = AccountSharedData::new(2, 1, &Pubkey::default());
812        account.set_rent_epoch(1);
813        accounts.push((key1, account));
814
815        let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
816        let tx = Transaction::new_with_compiled_instructions(
817            &[&keypair],
818            &[key1],
819            Hash::default(),
820            vec![native_loader::id()],
821            instructions,
822        );
823
824        let loaded_accounts =
825            load_accounts_with_excluded_features(tx, &accounts, &mut error_metrics, None);
826
827        assert_eq!(error_metrics.account_not_found, 0);
828        assert_eq!(loaded_accounts.len(), 1);
829        match &loaded_accounts[0] {
830            TransactionLoadResult::Loaded(loaded_transaction) => {
831                assert_eq!(loaded_transaction.accounts.len(), 3);
832                assert_eq!(loaded_transaction.accounts[0].1, accounts[0].1);
833                assert_eq!(loaded_transaction.program_indices.len(), 1);
834                assert_eq!(loaded_transaction.program_indices[0].len(), 0);
835            }
836            TransactionLoadResult::FeesOnly(fees_only_tx) => panic!("{}", fees_only_tx.load_error),
837            TransactionLoadResult::NotLoaded(e) => panic!("{e}"),
838        }
839    }
840
841    #[test]
842    fn test_load_accounts_bad_owner() {
843        let mut accounts: Vec<TransactionAccount> = Vec::new();
844        let mut error_metrics = TransactionErrorMetrics::default();
845
846        let keypair = Keypair::new();
847        let key0 = keypair.pubkey();
848        let key1 = Pubkey::from([5u8; 32]);
849
850        let account = AccountSharedData::new(1, 0, &Pubkey::default());
851        accounts.push((key0, account));
852
853        let mut account = AccountSharedData::new(40, 1, &Pubkey::default());
854        account.set_owner(bpf_loader_upgradeable::id());
855        account.set_executable(true);
856        accounts.push((key1, account));
857
858        let instructions = vec![CompiledInstruction::new(1, &(), vec![0])];
859        let tx = Transaction::new_with_compiled_instructions(
860            &[&keypair],
861            &[],
862            Hash::default(),
863            vec![key1],
864            instructions,
865        );
866
867        let load_results = load_accounts_aux_test(tx, &accounts, &mut error_metrics);
868
869        assert_eq!(error_metrics.account_not_found, 1);
870        assert_eq!(load_results.len(), 1);
871        assert!(matches!(
872            load_results[0],
873            TransactionLoadResult::FeesOnly(FeesOnlyTransaction {
874                load_error: TransactionError::ProgramAccountNotFound,
875                ..
876            }),
877        ));
878    }
879
880    #[test]
881    fn test_load_accounts_not_executable() {
882        let mut accounts: Vec<TransactionAccount> = Vec::new();
883        let mut error_metrics = TransactionErrorMetrics::default();
884
885        let keypair = Keypair::new();
886        let key0 = keypair.pubkey();
887        let key1 = Pubkey::from([5u8; 32]);
888
889        let account = AccountSharedData::new(1, 0, &Pubkey::default());
890        accounts.push((key0, account));
891
892        let account = AccountSharedData::new(40, 0, &native_loader::id());
893        accounts.push((key1, account));
894
895        let instructions = vec![CompiledInstruction::new(1, &(), vec![0])];
896        let tx = Transaction::new_with_compiled_instructions(
897            &[&keypair],
898            &[],
899            Hash::default(),
900            vec![key1],
901            instructions,
902        );
903
904        let load_results = load_accounts_aux_test(tx, &accounts, &mut error_metrics);
905
906        assert_eq!(error_metrics.invalid_program_for_execution, 1);
907        assert_eq!(load_results.len(), 1);
908        assert!(matches!(
909            load_results[0],
910            TransactionLoadResult::FeesOnly(FeesOnlyTransaction {
911                load_error: TransactionError::InvalidProgramForExecution,
912                ..
913            }),
914        ));
915    }
916
917    #[test]
918    fn test_load_accounts_multiple_loaders() {
919        let mut accounts: Vec<TransactionAccount> = Vec::new();
920        let mut error_metrics = TransactionErrorMetrics::default();
921
922        let keypair = Keypair::new();
923        let key0 = keypair.pubkey();
924        let key1 = bpf_loader_upgradeable::id();
925        let key2 = Pubkey::from([6u8; 32]);
926
927        let mut account = AccountSharedData::new(1, 0, &Pubkey::default());
928        account.set_rent_epoch(1);
929        accounts.push((key0, account));
930
931        let mut account = AccountSharedData::new(40, 1, &Pubkey::default());
932        account.set_executable(true);
933        account.set_rent_epoch(1);
934        account.set_owner(native_loader::id());
935        accounts.push((key1, account));
936
937        let mut account = AccountSharedData::new(41, 1, &Pubkey::default());
938        account.set_executable(true);
939        account.set_rent_epoch(1);
940        account.set_owner(key1);
941        accounts.push((key2, account));
942
943        let instructions = vec![
944            CompiledInstruction::new(1, &(), vec![0]),
945            CompiledInstruction::new(2, &(), vec![0]),
946        ];
947        let tx = Transaction::new_with_compiled_instructions(
948            &[&keypair],
949            &[],
950            Hash::default(),
951            vec![key1, key2],
952            instructions,
953        );
954
955        let loaded_accounts =
956            load_accounts_with_excluded_features(tx, &accounts, &mut error_metrics, None);
957
958        assert_eq!(error_metrics.account_not_found, 0);
959        assert_eq!(loaded_accounts.len(), 1);
960        match &loaded_accounts[0] {
961            TransactionLoadResult::Loaded(loaded_transaction) => {
962                assert_eq!(loaded_transaction.accounts.len(), 4);
963                assert_eq!(loaded_transaction.accounts[0].1, accounts[0].1);
964                assert_eq!(loaded_transaction.program_indices.len(), 2);
965                assert_eq!(loaded_transaction.program_indices[0], &[1]);
966                assert_eq!(loaded_transaction.program_indices[1], &[2]);
967            }
968            TransactionLoadResult::FeesOnly(fees_only_tx) => panic!("{}", fees_only_tx.load_error),
969            TransactionLoadResult::NotLoaded(e) => panic!("{e}"),
970        }
971    }
972
973    fn load_accounts_no_store(
974        accounts: &[TransactionAccount],
975        tx: Transaction,
976        account_overrides: Option<&AccountOverrides>,
977    ) -> Vec<TransactionLoadResult> {
978        let tx = SanitizedTransaction::from_transaction_for_tests(tx);
979
980        let mut error_metrics = TransactionErrorMetrics::default();
981        let mut accounts_map = HashMap::new();
982        for (pubkey, account) in accounts {
983            accounts_map.insert(*pubkey, account.clone());
984        }
985        let callbacks = TestCallbacks {
986            accounts_map,
987            ..Default::default()
988        };
989        load_accounts(
990            &callbacks,
991            &[tx],
992            vec![Ok(ValidatedTransactionDetails::default())],
993            &mut error_metrics,
994            account_overrides,
995            &FeatureSet::all_enabled(),
996            &RentCollector::default(),
997            &HashMap::new(),
998            &ProgramCacheForTxBatch::default(),
999        )
1000    }
1001
1002    #[test]
1003    fn test_instructions() {
1004        solana_logger::setup();
1005        let instructions_key = solana_sdk::sysvar::instructions::id();
1006        let keypair = Keypair::new();
1007        let instructions = vec![CompiledInstruction::new(1, &(), vec![0, 1])];
1008        let tx = Transaction::new_with_compiled_instructions(
1009            &[&keypair],
1010            &[solana_sdk::pubkey::new_rand(), instructions_key],
1011            Hash::default(),
1012            vec![native_loader::id()],
1013            instructions,
1014        );
1015
1016        let load_results = load_accounts_no_store(&[], tx, None);
1017        assert_eq!(load_results.len(), 1);
1018        assert!(matches!(
1019            load_results[0],
1020            TransactionLoadResult::FeesOnly(FeesOnlyTransaction {
1021                load_error: TransactionError::ProgramAccountNotFound,
1022                ..
1023            }),
1024        ));
1025    }
1026
1027    #[test]
1028    fn test_overrides() {
1029        solana_logger::setup();
1030        let mut account_overrides = AccountOverrides::default();
1031        let slot_history_id = sysvar::slot_history::id();
1032        let account = AccountSharedData::new(42, 0, &Pubkey::default());
1033        account_overrides.set_slot_history(Some(account));
1034
1035        let keypair = Keypair::new();
1036        let account = AccountSharedData::new(1_000_000, 0, &Pubkey::default());
1037
1038        let instructions = vec![CompiledInstruction::new(2, &(), vec![0])];
1039        let tx = Transaction::new_with_compiled_instructions(
1040            &[&keypair],
1041            &[slot_history_id],
1042            Hash::default(),
1043            vec![native_loader::id()],
1044            instructions,
1045        );
1046
1047        let loaded_accounts =
1048            load_accounts_no_store(&[(keypair.pubkey(), account)], tx, Some(&account_overrides));
1049        assert_eq!(loaded_accounts.len(), 1);
1050        match &loaded_accounts[0] {
1051            TransactionLoadResult::Loaded(loaded_transaction) => {
1052                assert_eq!(loaded_transaction.accounts[0].0, keypair.pubkey());
1053                assert_eq!(loaded_transaction.accounts[1].0, slot_history_id);
1054                assert_eq!(loaded_transaction.accounts[1].1.lamports(), 42);
1055            }
1056            TransactionLoadResult::FeesOnly(fees_only_tx) => panic!("{}", fees_only_tx.load_error),
1057            TransactionLoadResult::NotLoaded(e) => panic!("{e}"),
1058        }
1059    }
1060
1061    #[test]
1062    fn test_accumulate_and_check_loaded_account_data_size() {
1063        let mut error_metrics = TransactionErrorMetrics::default();
1064        let mut accumulated_data_size: u32 = 0;
1065        let data_size: usize = 123;
1066        let requested_data_size_limit = NonZeroU32::new(data_size as u32).unwrap();
1067
1068        // OK - loaded data size is up to limit
1069        assert!(accumulate_and_check_loaded_account_data_size(
1070            &mut accumulated_data_size,
1071            data_size,
1072            requested_data_size_limit,
1073            &mut error_metrics
1074        )
1075        .is_ok());
1076        assert_eq!(data_size as u32, accumulated_data_size);
1077
1078        // fail - loading more data that would exceed limit
1079        let another_byte: usize = 1;
1080        assert_eq!(
1081            accumulate_and_check_loaded_account_data_size(
1082                &mut accumulated_data_size,
1083                another_byte,
1084                requested_data_size_limit,
1085                &mut error_metrics
1086            ),
1087            Err(TransactionError::MaxLoadedAccountsDataSizeExceeded)
1088        );
1089    }
1090
1091    struct ValidateFeePayerTestParameter {
1092        is_nonce: bool,
1093        payer_init_balance: u64,
1094        fee: u64,
1095        expected_result: Result<()>,
1096        payer_post_balance: u64,
1097    }
1098    fn validate_fee_payer_account(
1099        test_parameter: ValidateFeePayerTestParameter,
1100        rent_collector: &RentCollector,
1101    ) {
1102        let payer_account_keys = Keypair::new();
1103        let mut account = if test_parameter.is_nonce {
1104            AccountSharedData::new_data(
1105                test_parameter.payer_init_balance,
1106                &NonceVersions::new(NonceState::Initialized(nonce::state::Data::default())),
1107                &system_program::id(),
1108            )
1109            .unwrap()
1110        } else {
1111            AccountSharedData::new(test_parameter.payer_init_balance, 0, &system_program::id())
1112        };
1113        let result = validate_fee_payer(
1114            &payer_account_keys.pubkey(),
1115            &mut account,
1116            0,
1117            &mut TransactionErrorMetrics::default(),
1118            rent_collector,
1119            test_parameter.fee,
1120        );
1121
1122        assert_eq!(result, test_parameter.expected_result);
1123        assert_eq!(account.lamports(), test_parameter.payer_post_balance);
1124    }
1125
1126    #[test]
1127    fn test_validate_fee_payer() {
1128        let rent_collector = RentCollector::new(
1129            0,
1130            EpochSchedule::default(),
1131            500_000.0,
1132            Rent {
1133                lamports_per_byte_year: 1,
1134                ..Rent::default()
1135            },
1136        );
1137        let min_balance = rent_collector.rent.minimum_balance(NonceState::size());
1138        let fee = 5_000;
1139
1140        // If payer account has sufficient balance, expect successful fee deduction,
1141        // regardless feature gate status, or if payer is nonce account.
1142        {
1143            for (is_nonce, min_balance) in [(true, min_balance), (false, 0)] {
1144                validate_fee_payer_account(
1145                    ValidateFeePayerTestParameter {
1146                        is_nonce,
1147                        payer_init_balance: min_balance + fee,
1148                        fee,
1149                        expected_result: Ok(()),
1150                        payer_post_balance: min_balance,
1151                    },
1152                    &rent_collector,
1153                );
1154            }
1155        }
1156
1157        // If payer account has no balance, expected AccountNotFound Error
1158        // regardless feature gate status, or if payer is nonce account.
1159        {
1160            for is_nonce in [true, false] {
1161                validate_fee_payer_account(
1162                    ValidateFeePayerTestParameter {
1163                        is_nonce,
1164                        payer_init_balance: 0,
1165                        fee,
1166                        expected_result: Err(TransactionError::AccountNotFound),
1167                        payer_post_balance: 0,
1168                    },
1169                    &rent_collector,
1170                );
1171            }
1172        }
1173
1174        // If payer account has insufficient balance, expect InsufficientFundsForFee error
1175        // regardless feature gate status, or if payer is nonce account.
1176        {
1177            for (is_nonce, min_balance) in [(true, min_balance), (false, 0)] {
1178                validate_fee_payer_account(
1179                    ValidateFeePayerTestParameter {
1180                        is_nonce,
1181                        payer_init_balance: min_balance + fee - 1,
1182                        fee,
1183                        expected_result: Err(TransactionError::InsufficientFundsForFee),
1184                        payer_post_balance: min_balance + fee - 1,
1185                    },
1186                    &rent_collector,
1187                );
1188            }
1189        }
1190
1191        // normal payer account has balance of u64::MAX, so does fee; since it does not  require
1192        // min_balance, expect successful fee deduction, regardless of feature gate status
1193        {
1194            validate_fee_payer_account(
1195                ValidateFeePayerTestParameter {
1196                    is_nonce: false,
1197                    payer_init_balance: u64::MAX,
1198                    fee: u64::MAX,
1199                    expected_result: Ok(()),
1200                    payer_post_balance: 0,
1201                },
1202                &rent_collector,
1203            );
1204        }
1205    }
1206
1207    #[test]
1208    fn test_validate_nonce_fee_payer_with_checked_arithmetic() {
1209        let rent_collector = RentCollector::new(
1210            0,
1211            EpochSchedule::default(),
1212            500_000.0,
1213            Rent {
1214                lamports_per_byte_year: 1,
1215                ..Rent::default()
1216            },
1217        );
1218
1219        // nonce payer account has balance of u64::MAX, so does fee; due to nonce account
1220        // requires additional min_balance, expect InsufficientFundsForFee error if feature gate is
1221        // enabled
1222        validate_fee_payer_account(
1223            ValidateFeePayerTestParameter {
1224                is_nonce: true,
1225                payer_init_balance: u64::MAX,
1226                fee: u64::MAX,
1227                expected_result: Err(TransactionError::InsufficientFundsForFee),
1228                payer_post_balance: u64::MAX,
1229            },
1230            &rent_collector,
1231        );
1232    }
1233
1234    #[test]
1235    fn test_construct_instructions_account() {
1236        let loaded_message = LoadedMessage {
1237            message: Cow::Owned(solana_sdk::message::v0::Message::default()),
1238            loaded_addresses: Cow::Owned(LoadedAddresses::default()),
1239            is_writable_account_cache: vec![false],
1240        };
1241        let message = SanitizedMessage::V0(loaded_message);
1242        let shared_data = construct_instructions_account(&message);
1243        let expected = AccountSharedData::from(Account {
1244            data: construct_instructions_data(&message.decompile_instructions()),
1245            owner: sysvar::id(),
1246            ..Account::default()
1247        });
1248        assert_eq!(shared_data, expected);
1249    }
1250
1251    #[test]
1252    fn test_load_transaction_accounts_fee_payer() {
1253        let fee_payer_address = Pubkey::new_unique();
1254        let message = Message {
1255            account_keys: vec![fee_payer_address],
1256            header: MessageHeader::default(),
1257            instructions: vec![],
1258            recent_blockhash: Hash::default(),
1259        };
1260
1261        let sanitized_message = new_unchecked_sanitized_message(message);
1262        let mut mock_bank = TestCallbacks::default();
1263
1264        let fee_payer_balance = 200;
1265        let mut fee_payer_account = AccountSharedData::default();
1266        fee_payer_account.set_lamports(fee_payer_balance);
1267        mock_bank
1268            .accounts_map
1269            .insert(fee_payer_address, fee_payer_account.clone());
1270        let fee_payer_rent_debit = 42;
1271
1272        let mut error_metrics = TransactionErrorMetrics::default();
1273        let loaded_programs = ProgramCacheForTxBatch::default();
1274
1275        let sanitized_transaction = SanitizedTransaction::new_for_tests(
1276            sanitized_message,
1277            vec![Signature::new_unique()],
1278            false,
1279        );
1280        let result = load_transaction_accounts(
1281            &mock_bank,
1282            sanitized_transaction.message(),
1283            LoadedTransactionAccount {
1284                loaded_size: fee_payer_account.data().len(),
1285                account: fee_payer_account.clone(),
1286                rent_collected: fee_payer_rent_debit,
1287            },
1288            &ComputeBudgetLimits::default(),
1289            &mut error_metrics,
1290            None,
1291            &FeatureSet::default(),
1292            &RentCollector::default(),
1293            &HashMap::new(),
1294            &loaded_programs,
1295        );
1296
1297        let expected_rent_debits = {
1298            let mut rent_debits = RentDebits::default();
1299            rent_debits.insert(&fee_payer_address, fee_payer_rent_debit, fee_payer_balance);
1300            rent_debits
1301        };
1302        assert_eq!(
1303            result.unwrap(),
1304            LoadedTransactionAccounts {
1305                accounts: vec![(fee_payer_address, fee_payer_account)],
1306                program_indices: vec![],
1307                rent: fee_payer_rent_debit,
1308                rent_debits: expected_rent_debits,
1309                loaded_accounts_data_size: 0,
1310            }
1311        );
1312    }
1313
1314    #[test]
1315    fn test_load_transaction_accounts_native_loader() {
1316        let key1 = Keypair::new();
1317        let message = Message {
1318            account_keys: vec![key1.pubkey(), native_loader::id()],
1319            header: MessageHeader::default(),
1320            instructions: vec![CompiledInstruction {
1321                program_id_index: 1,
1322                accounts: vec![0],
1323                data: vec![],
1324            }],
1325            recent_blockhash: Hash::default(),
1326        };
1327
1328        let sanitized_message = new_unchecked_sanitized_message(message);
1329        let mut mock_bank = TestCallbacks::default();
1330        mock_bank
1331            .accounts_map
1332            .insert(native_loader::id(), AccountSharedData::default());
1333        let mut fee_payer_account = AccountSharedData::default();
1334        fee_payer_account.set_lamports(200);
1335        mock_bank
1336            .accounts_map
1337            .insert(key1.pubkey(), fee_payer_account.clone());
1338
1339        let mut error_metrics = TransactionErrorMetrics::default();
1340        let loaded_programs = ProgramCacheForTxBatch::default();
1341
1342        let sanitized_transaction = SanitizedTransaction::new_for_tests(
1343            sanitized_message,
1344            vec![Signature::new_unique()],
1345            false,
1346        );
1347        let result = load_transaction_accounts(
1348            &mock_bank,
1349            sanitized_transaction.message(),
1350            LoadedTransactionAccount {
1351                account: fee_payer_account.clone(),
1352                ..LoadedTransactionAccount::default()
1353            },
1354            &ComputeBudgetLimits::default(),
1355            &mut error_metrics,
1356            None,
1357            &FeatureSet::default(),
1358            &RentCollector::default(),
1359            &HashMap::new(),
1360            &loaded_programs,
1361        );
1362
1363        assert_eq!(
1364            result.unwrap(),
1365            LoadedTransactionAccounts {
1366                accounts: vec![
1367                    (key1.pubkey(), fee_payer_account),
1368                    (
1369                        native_loader::id(),
1370                        mock_bank.accounts_map[&native_loader::id()].clone()
1371                    )
1372                ],
1373                program_indices: vec![vec![]],
1374                rent: 0,
1375                rent_debits: RentDebits::default(),
1376                loaded_accounts_data_size: 0,
1377            }
1378        );
1379    }
1380
1381    #[test]
1382    fn test_load_transaction_accounts_program_account_executable_bypass() {
1383        // currently, the account loader retrieves read-only non-instruction accounts from the program cache
1384        // it creates a mock AccountSharedData with the executable flag set to true
1385        // however, it does not check whether these accounts are actually executable before doing so
1386        // this affects consensus: a transaction that uses a cached non-executable program executes and fails
1387        // but if the transaction gets the program from accounts-db, it will be dropped during account loading
1388        // this test enforces the current behavior, so that future account loader changes do not break consensus
1389
1390        let mut mock_bank = TestCallbacks::default();
1391        let account_keypair = Keypair::new();
1392        let program_keypair = Keypair::new();
1393        let loader_v2 = bpf_loader::id();
1394
1395        let mut account_data = AccountSharedData::default();
1396        account_data.set_lamports(200);
1397        mock_bank
1398            .accounts_map
1399            .insert(account_keypair.pubkey(), account_data.clone());
1400
1401        let mut program_data = AccountSharedData::default();
1402        program_data.set_lamports(200);
1403        program_data.set_owner(loader_v2);
1404        mock_bank
1405            .accounts_map
1406            .insert(program_keypair.pubkey(), program_data);
1407
1408        let mut loader_data = AccountSharedData::default();
1409        loader_data.set_lamports(200);
1410        loader_data.set_executable(true);
1411        loader_data.set_owner(native_loader::id());
1412        mock_bank
1413            .accounts_map
1414            .insert(loader_v2, loader_data.clone());
1415        mock_bank
1416            .accounts_map
1417            .insert(native_loader::id(), loader_data.clone());
1418
1419        let transaction =
1420            SanitizedTransaction::from_transaction_for_tests(Transaction::new_signed_with_payer(
1421                &[Instruction::new_with_bytes(
1422                    program_keypair.pubkey(),
1423                    &[],
1424                    vec![],
1425                )],
1426                Some(&account_keypair.pubkey()),
1427                &[&account_keypair],
1428                Hash::default(),
1429            ));
1430
1431        let mut program_accounts = HashMap::new();
1432        program_accounts.insert(program_keypair.pubkey(), (&loader_v2, 0));
1433        let mut loaded_programs = ProgramCacheForTxBatch::default();
1434        loaded_programs.replenish(
1435            program_keypair.pubkey(),
1436            Arc::new(ProgramCacheEntry::new_tombstone(
1437                0,
1438                ProgramCacheEntryOwner::LoaderV2,
1439                ProgramCacheEntryType::Closed,
1440            )),
1441        );
1442
1443        let mut error_metrics = TransactionErrorMetrics::default();
1444        let result = load_transaction_accounts(
1445            &mock_bank,
1446            transaction.message(),
1447            LoadedTransactionAccount {
1448                account: account_data.clone(),
1449                ..LoadedTransactionAccount::default()
1450            },
1451            &ComputeBudgetLimits::default(),
1452            &mut error_metrics,
1453            None,
1454            &FeatureSet::default(),
1455            &RentCollector::default(),
1456            &program_accounts,
1457            &loaded_programs,
1458        );
1459
1460        // Executable flag is bypassed
1461        let mut cached_program = AccountSharedData::default();
1462        cached_program.set_owner(loader_v2);
1463        cached_program.set_executable(true);
1464
1465        assert_eq!(
1466            result.unwrap(),
1467            LoadedTransactionAccounts {
1468                accounts: vec![
1469                    (account_keypair.pubkey(), account_data.clone()),
1470                    (program_keypair.pubkey(), cached_program),
1471                    (loader_v2, loader_data),
1472                ],
1473                program_indices: vec![vec![1]],
1474                rent: 0,
1475                rent_debits: RentDebits::default(),
1476                loaded_accounts_data_size: 0,
1477            }
1478        );
1479
1480        let transaction =
1481            SanitizedTransaction::from_transaction_for_tests(Transaction::new_signed_with_payer(
1482                &[Instruction::new_with_bytes(
1483                    program_keypair.pubkey(),
1484                    &[],
1485                    vec![AccountMeta::new_readonly(program_keypair.pubkey(), false)],
1486                )],
1487                Some(&account_keypair.pubkey()),
1488                &[&account_keypair],
1489                Hash::default(),
1490            ));
1491
1492        let result = load_transaction_accounts(
1493            &mock_bank,
1494            transaction.message(),
1495            LoadedTransactionAccount {
1496                account: account_data.clone(),
1497                ..LoadedTransactionAccount::default()
1498            },
1499            &ComputeBudgetLimits::default(),
1500            &mut error_metrics,
1501            None,
1502            &FeatureSet::default(),
1503            &RentCollector::default(),
1504            &program_accounts,
1505            &loaded_programs,
1506        );
1507
1508        // including program as instruction account bypasses executable bypass
1509        assert_eq!(
1510            result.err(),
1511            Some(TransactionError::InvalidProgramForExecution)
1512        );
1513
1514        let transaction = new_unchecked_sanitized_transaction_with_writable_program(
1515            program_keypair.pubkey(),
1516            account_keypair.pubkey(),
1517        );
1518
1519        let result = load_transaction_accounts(
1520            &mock_bank,
1521            transaction.message(),
1522            LoadedTransactionAccount {
1523                account: account_data.clone(),
1524                ..LoadedTransactionAccount::default()
1525            },
1526            &ComputeBudgetLimits::default(),
1527            &mut error_metrics,
1528            None,
1529            &FeatureSet::default(),
1530            &RentCollector::default(),
1531            &program_accounts,
1532            &loaded_programs,
1533        );
1534
1535        // including program as writable bypasses executable bypass
1536        assert_eq!(
1537            result.err(),
1538            Some(TransactionError::InvalidProgramForExecution)
1539        );
1540    }
1541
1542    #[test]
1543    fn test_load_transaction_accounts_program_account_no_data() {
1544        let key1 = Keypair::new();
1545        let key2 = Keypair::new();
1546
1547        let message = Message {
1548            account_keys: vec![key1.pubkey(), key2.pubkey()],
1549            header: MessageHeader::default(),
1550            instructions: vec![CompiledInstruction {
1551                program_id_index: 1,
1552                accounts: vec![0, 1],
1553                data: vec![],
1554            }],
1555            recent_blockhash: Hash::default(),
1556        };
1557
1558        let sanitized_message = new_unchecked_sanitized_message(message);
1559        let mut mock_bank = TestCallbacks::default();
1560        let mut account_data = AccountSharedData::default();
1561        account_data.set_lamports(200);
1562        mock_bank.accounts_map.insert(key1.pubkey(), account_data);
1563
1564        let mut error_metrics = TransactionErrorMetrics::default();
1565        let loaded_programs = ProgramCacheForTxBatch::default();
1566
1567        let sanitized_transaction = SanitizedTransaction::new_for_tests(
1568            sanitized_message,
1569            vec![Signature::new_unique()],
1570            false,
1571        );
1572        let result = load_transaction_accounts(
1573            &mock_bank,
1574            sanitized_transaction.message(),
1575            LoadedTransactionAccount::default(),
1576            &ComputeBudgetLimits::default(),
1577            &mut error_metrics,
1578            None,
1579            &FeatureSet::default(),
1580            &RentCollector::default(),
1581            &HashMap::new(),
1582            &loaded_programs,
1583        );
1584
1585        assert_eq!(result.err(), Some(TransactionError::ProgramAccountNotFound));
1586    }
1587
1588    #[test]
1589    fn test_load_transaction_accounts_invalid_program_for_execution() {
1590        let key1 = Keypair::new();
1591        let key2 = Keypair::new();
1592
1593        let message = Message {
1594            account_keys: vec![key1.pubkey(), key2.pubkey()],
1595            header: MessageHeader::default(),
1596            instructions: vec![CompiledInstruction {
1597                program_id_index: 0,
1598                accounts: vec![0, 1],
1599                data: vec![],
1600            }],
1601            recent_blockhash: Hash::default(),
1602        };
1603
1604        let sanitized_message = new_unchecked_sanitized_message(message);
1605        let mut mock_bank = TestCallbacks::default();
1606        let mut account_data = AccountSharedData::default();
1607        account_data.set_lamports(200);
1608        mock_bank.accounts_map.insert(key1.pubkey(), account_data);
1609
1610        let mut error_metrics = TransactionErrorMetrics::default();
1611        let loaded_programs = ProgramCacheForTxBatch::default();
1612
1613        let sanitized_transaction = SanitizedTransaction::new_for_tests(
1614            sanitized_message,
1615            vec![Signature::new_unique()],
1616            false,
1617        );
1618        let result = load_transaction_accounts(
1619            &mock_bank,
1620            sanitized_transaction.message(),
1621            LoadedTransactionAccount::default(),
1622            &ComputeBudgetLimits::default(),
1623            &mut error_metrics,
1624            None,
1625            &FeatureSet::default(),
1626            &RentCollector::default(),
1627            &HashMap::new(),
1628            &loaded_programs,
1629        );
1630
1631        assert_eq!(
1632            result.err(),
1633            Some(TransactionError::InvalidProgramForExecution)
1634        );
1635    }
1636
1637    #[test]
1638    fn test_load_transaction_accounts_native_loader_owner() {
1639        let key1 = Keypair::new();
1640        let key2 = Keypair::new();
1641
1642        let message = Message {
1643            account_keys: vec![key2.pubkey(), key1.pubkey()],
1644            header: MessageHeader::default(),
1645            instructions: vec![CompiledInstruction {
1646                program_id_index: 1,
1647                accounts: vec![0],
1648                data: vec![],
1649            }],
1650            recent_blockhash: Hash::default(),
1651        };
1652
1653        let sanitized_message = new_unchecked_sanitized_message(message);
1654        let mut mock_bank = TestCallbacks::default();
1655        let mut account_data = AccountSharedData::default();
1656        account_data.set_owner(native_loader::id());
1657        account_data.set_executable(true);
1658        mock_bank.accounts_map.insert(key1.pubkey(), account_data);
1659
1660        let mut fee_payer_account = AccountSharedData::default();
1661        fee_payer_account.set_lamports(200);
1662        mock_bank
1663            .accounts_map
1664            .insert(key2.pubkey(), fee_payer_account.clone());
1665        let mut error_metrics = TransactionErrorMetrics::default();
1666        let loaded_programs = ProgramCacheForTxBatch::default();
1667
1668        let sanitized_transaction = SanitizedTransaction::new_for_tests(
1669            sanitized_message,
1670            vec![Signature::new_unique()],
1671            false,
1672        );
1673        let result = load_transaction_accounts(
1674            &mock_bank,
1675            sanitized_transaction.message(),
1676            LoadedTransactionAccount {
1677                account: fee_payer_account.clone(),
1678                ..LoadedTransactionAccount::default()
1679            },
1680            &ComputeBudgetLimits::default(),
1681            &mut error_metrics,
1682            None,
1683            &FeatureSet::default(),
1684            &RentCollector::default(),
1685            &HashMap::new(),
1686            &loaded_programs,
1687        );
1688
1689        assert_eq!(
1690            result.unwrap(),
1691            LoadedTransactionAccounts {
1692                accounts: vec![
1693                    (key2.pubkey(), fee_payer_account),
1694                    (
1695                        key1.pubkey(),
1696                        mock_bank.accounts_map[&key1.pubkey()].clone()
1697                    ),
1698                ],
1699                program_indices: vec![vec![1]],
1700                rent: 0,
1701                rent_debits: RentDebits::default(),
1702                loaded_accounts_data_size: 0,
1703            }
1704        );
1705    }
1706
1707    #[test]
1708    fn test_load_transaction_accounts_program_account_not_found_after_all_checks() {
1709        let key1 = Keypair::new();
1710        let key2 = Keypair::new();
1711
1712        let message = Message {
1713            account_keys: vec![key2.pubkey(), key1.pubkey()],
1714            header: MessageHeader::default(),
1715            instructions: vec![CompiledInstruction {
1716                program_id_index: 1,
1717                accounts: vec![0],
1718                data: vec![],
1719            }],
1720            recent_blockhash: Hash::default(),
1721        };
1722
1723        let sanitized_message = new_unchecked_sanitized_message(message);
1724        let mut mock_bank = TestCallbacks::default();
1725        let mut account_data = AccountSharedData::default();
1726        account_data.set_executable(true);
1727        mock_bank.accounts_map.insert(key1.pubkey(), account_data);
1728
1729        let mut account_data = AccountSharedData::default();
1730        account_data.set_lamports(200);
1731        mock_bank.accounts_map.insert(key2.pubkey(), account_data);
1732        let mut error_metrics = TransactionErrorMetrics::default();
1733        let loaded_programs = ProgramCacheForTxBatch::default();
1734
1735        let sanitized_transaction = SanitizedTransaction::new_for_tests(
1736            sanitized_message,
1737            vec![Signature::new_unique()],
1738            false,
1739        );
1740        let result = load_transaction_accounts(
1741            &mock_bank,
1742            sanitized_transaction.message(),
1743            LoadedTransactionAccount::default(),
1744            &ComputeBudgetLimits::default(),
1745            &mut error_metrics,
1746            None,
1747            &FeatureSet::default(),
1748            &RentCollector::default(),
1749            &HashMap::new(),
1750            &loaded_programs,
1751        );
1752
1753        assert_eq!(result.err(), Some(TransactionError::ProgramAccountNotFound));
1754    }
1755
1756    #[test]
1757    fn test_load_transaction_accounts_program_account_invalid_program_for_execution_last_check() {
1758        let key1 = Keypair::new();
1759        let key2 = Keypair::new();
1760        let key3 = Keypair::new();
1761
1762        let message = Message {
1763            account_keys: vec![key2.pubkey(), key1.pubkey()],
1764            header: MessageHeader::default(),
1765            instructions: vec![CompiledInstruction {
1766                program_id_index: 1,
1767                accounts: vec![0],
1768                data: vec![],
1769            }],
1770            recent_blockhash: Hash::default(),
1771        };
1772
1773        let sanitized_message = new_unchecked_sanitized_message(message);
1774        let mut mock_bank = TestCallbacks::default();
1775        let mut account_data = AccountSharedData::default();
1776        account_data.set_executable(true);
1777        account_data.set_owner(key3.pubkey());
1778        mock_bank.accounts_map.insert(key1.pubkey(), account_data);
1779
1780        let mut account_data = AccountSharedData::default();
1781        account_data.set_lamports(200);
1782        mock_bank.accounts_map.insert(key2.pubkey(), account_data);
1783
1784        mock_bank
1785            .accounts_map
1786            .insert(key3.pubkey(), AccountSharedData::default());
1787        let mut error_metrics = TransactionErrorMetrics::default();
1788        let loaded_programs = ProgramCacheForTxBatch::default();
1789
1790        let sanitized_transaction = SanitizedTransaction::new_for_tests(
1791            sanitized_message,
1792            vec![Signature::new_unique()],
1793            false,
1794        );
1795        let result = load_transaction_accounts(
1796            &mock_bank,
1797            sanitized_transaction.message(),
1798            LoadedTransactionAccount::default(),
1799            &ComputeBudgetLimits::default(),
1800            &mut error_metrics,
1801            None,
1802            &FeatureSet::default(),
1803            &RentCollector::default(),
1804            &HashMap::new(),
1805            &loaded_programs,
1806        );
1807
1808        assert_eq!(
1809            result.err(),
1810            Some(TransactionError::InvalidProgramForExecution)
1811        );
1812    }
1813
1814    #[test]
1815    fn test_load_transaction_accounts_program_success_complete() {
1816        let key1 = Keypair::new();
1817        let key2 = Keypair::new();
1818        let key3 = Keypair::new();
1819
1820        let message = Message {
1821            account_keys: vec![key2.pubkey(), key1.pubkey()],
1822            header: MessageHeader::default(),
1823            instructions: vec![CompiledInstruction {
1824                program_id_index: 1,
1825                accounts: vec![0],
1826                data: vec![],
1827            }],
1828            recent_blockhash: Hash::default(),
1829        };
1830
1831        let sanitized_message = new_unchecked_sanitized_message(message);
1832        let mut mock_bank = TestCallbacks::default();
1833        let mut account_data = AccountSharedData::default();
1834        account_data.set_executable(true);
1835        account_data.set_owner(key3.pubkey());
1836        mock_bank.accounts_map.insert(key1.pubkey(), account_data);
1837
1838        let mut fee_payer_account = AccountSharedData::default();
1839        fee_payer_account.set_lamports(200);
1840        mock_bank
1841            .accounts_map
1842            .insert(key2.pubkey(), fee_payer_account.clone());
1843
1844        let mut account_data = AccountSharedData::default();
1845        account_data.set_executable(true);
1846        account_data.set_owner(native_loader::id());
1847        mock_bank.accounts_map.insert(key3.pubkey(), account_data);
1848
1849        let mut error_metrics = TransactionErrorMetrics::default();
1850        let loaded_programs = ProgramCacheForTxBatch::default();
1851
1852        let sanitized_transaction = SanitizedTransaction::new_for_tests(
1853            sanitized_message,
1854            vec![Signature::new_unique()],
1855            false,
1856        );
1857        let result = load_transaction_accounts(
1858            &mock_bank,
1859            sanitized_transaction.message(),
1860            LoadedTransactionAccount {
1861                account: fee_payer_account.clone(),
1862                ..LoadedTransactionAccount::default()
1863            },
1864            &ComputeBudgetLimits::default(),
1865            &mut error_metrics,
1866            None,
1867            &FeatureSet::default(),
1868            &RentCollector::default(),
1869            &HashMap::new(),
1870            &loaded_programs,
1871        );
1872
1873        assert_eq!(
1874            result.unwrap(),
1875            LoadedTransactionAccounts {
1876                accounts: vec![
1877                    (key2.pubkey(), fee_payer_account),
1878                    (
1879                        key1.pubkey(),
1880                        mock_bank.accounts_map[&key1.pubkey()].clone()
1881                    ),
1882                    (
1883                        key3.pubkey(),
1884                        mock_bank.accounts_map[&key3.pubkey()].clone()
1885                    ),
1886                ],
1887                program_indices: vec![vec![1]],
1888                rent: 0,
1889                rent_debits: RentDebits::default(),
1890                loaded_accounts_data_size: 0,
1891            }
1892        );
1893    }
1894
1895    #[test]
1896    fn test_load_transaction_accounts_program_builtin_saturating_add() {
1897        let key1 = Keypair::new();
1898        let key2 = Keypair::new();
1899        let key3 = Keypair::new();
1900        let key4 = Keypair::new();
1901
1902        let message = Message {
1903            account_keys: vec![key2.pubkey(), key1.pubkey(), key4.pubkey()],
1904            header: MessageHeader::default(),
1905            instructions: vec![
1906                CompiledInstruction {
1907                    program_id_index: 1,
1908                    accounts: vec![0],
1909                    data: vec![],
1910                },
1911                CompiledInstruction {
1912                    program_id_index: 1,
1913                    accounts: vec![2],
1914                    data: vec![],
1915                },
1916            ],
1917            recent_blockhash: Hash::default(),
1918        };
1919
1920        let sanitized_message = new_unchecked_sanitized_message(message);
1921        let mut mock_bank = TestCallbacks::default();
1922        let mut account_data = AccountSharedData::default();
1923        account_data.set_executable(true);
1924        account_data.set_owner(key3.pubkey());
1925        mock_bank.accounts_map.insert(key1.pubkey(), account_data);
1926
1927        let mut fee_payer_account = AccountSharedData::default();
1928        fee_payer_account.set_lamports(200);
1929        mock_bank
1930            .accounts_map
1931            .insert(key2.pubkey(), fee_payer_account.clone());
1932
1933        let mut account_data = AccountSharedData::default();
1934        account_data.set_executable(true);
1935        account_data.set_owner(native_loader::id());
1936        mock_bank.accounts_map.insert(key3.pubkey(), account_data);
1937
1938        let mut error_metrics = TransactionErrorMetrics::default();
1939        let loaded_programs = ProgramCacheForTxBatch::default();
1940
1941        let sanitized_transaction = SanitizedTransaction::new_for_tests(
1942            sanitized_message,
1943            vec![Signature::new_unique()],
1944            false,
1945        );
1946        let result = load_transaction_accounts(
1947            &mock_bank,
1948            sanitized_transaction.message(),
1949            LoadedTransactionAccount {
1950                account: fee_payer_account.clone(),
1951                ..LoadedTransactionAccount::default()
1952            },
1953            &ComputeBudgetLimits::default(),
1954            &mut error_metrics,
1955            None,
1956            &FeatureSet::default(),
1957            &RentCollector::default(),
1958            &HashMap::new(),
1959            &loaded_programs,
1960        );
1961
1962        let mut account_data = AccountSharedData::default();
1963        account_data.set_rent_epoch(RENT_EXEMPT_RENT_EPOCH);
1964        assert_eq!(
1965            result.unwrap(),
1966            LoadedTransactionAccounts {
1967                accounts: vec![
1968                    (key2.pubkey(), fee_payer_account),
1969                    (
1970                        key1.pubkey(),
1971                        mock_bank.accounts_map[&key1.pubkey()].clone()
1972                    ),
1973                    (key4.pubkey(), account_data),
1974                    (
1975                        key3.pubkey(),
1976                        mock_bank.accounts_map[&key3.pubkey()].clone()
1977                    ),
1978                ],
1979                program_indices: vec![vec![1], vec![1]],
1980                rent: 0,
1981                rent_debits: RentDebits::default(),
1982                loaded_accounts_data_size: 0,
1983            }
1984        );
1985    }
1986
1987    #[test]
1988    fn test_rent_state_list_len() {
1989        let mint_keypair = Keypair::new();
1990        let mut bank = TestCallbacks::default();
1991        let recipient = Pubkey::new_unique();
1992        let last_block_hash = Hash::new_unique();
1993
1994        let mut system_data = AccountSharedData::default();
1995        system_data.set_executable(true);
1996        system_data.set_owner(native_loader::id());
1997        bank.accounts_map
1998            .insert(Pubkey::new_from_array([0u8; 32]), system_data);
1999
2000        let mut mint_data = AccountSharedData::default();
2001        mint_data.set_lamports(2);
2002        bank.accounts_map.insert(mint_keypair.pubkey(), mint_data);
2003
2004        bank.accounts_map
2005            .insert(recipient, AccountSharedData::default());
2006
2007        let tx = system_transaction::transfer(
2008            &mint_keypair,
2009            &recipient,
2010            sol_to_lamports(1.),
2011            last_block_hash,
2012        );
2013        let num_accounts = tx.message().account_keys.len();
2014        let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(tx);
2015        let mut error_metrics = TransactionErrorMetrics::default();
2016        let mut load_results = load_accounts(
2017            &bank,
2018            &[sanitized_tx.clone()],
2019            vec![Ok(ValidatedTransactionDetails::default())],
2020            &mut error_metrics,
2021            None,
2022            &FeatureSet::default(),
2023            &RentCollector::default(),
2024            &HashMap::new(),
2025            &ProgramCacheForTxBatch::default(),
2026        );
2027
2028        let TransactionLoadResult::Loaded(loaded_transaction) = load_results.swap_remove(0) else {
2029            panic!("transaction loading failed");
2030        };
2031
2032        let compute_budget = ComputeBudget::new(u64::from(
2033            compute_budget_limits::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT,
2034        ));
2035        let rent_collector = RentCollector::default();
2036        let transaction_context = TransactionContext::new(
2037            loaded_transaction.accounts,
2038            rent_collector.get_rent().clone(),
2039            compute_budget.max_instruction_stack_depth,
2040            compute_budget.max_instruction_trace_length,
2041        );
2042
2043        assert_eq!(
2044            TransactionAccountStateInfo::new(
2045                &transaction_context,
2046                sanitized_tx.message(),
2047                &rent_collector,
2048            )
2049            .len(),
2050            num_accounts,
2051        );
2052    }
2053
2054    #[test]
2055    fn test_load_accounts_success() {
2056        let key1 = Keypair::new();
2057        let key2 = Keypair::new();
2058        let key3 = Keypair::new();
2059        let key4 = Keypair::new();
2060
2061        let message = Message {
2062            account_keys: vec![key2.pubkey(), key1.pubkey(), key4.pubkey()],
2063            header: MessageHeader::default(),
2064            instructions: vec![
2065                CompiledInstruction {
2066                    program_id_index: 1,
2067                    accounts: vec![0],
2068                    data: vec![],
2069                },
2070                CompiledInstruction {
2071                    program_id_index: 1,
2072                    accounts: vec![2],
2073                    data: vec![],
2074                },
2075            ],
2076            recent_blockhash: Hash::default(),
2077        };
2078
2079        let sanitized_message = new_unchecked_sanitized_message(message);
2080        let mut mock_bank = TestCallbacks::default();
2081        let mut account_data = AccountSharedData::default();
2082        account_data.set_executable(true);
2083        account_data.set_owner(key3.pubkey());
2084        mock_bank.accounts_map.insert(key1.pubkey(), account_data);
2085
2086        let mut fee_payer_account = AccountSharedData::default();
2087        fee_payer_account.set_lamports(200);
2088        mock_bank
2089            .accounts_map
2090            .insert(key2.pubkey(), fee_payer_account.clone());
2091
2092        let mut account_data = AccountSharedData::default();
2093        account_data.set_executable(true);
2094        account_data.set_owner(native_loader::id());
2095        mock_bank.accounts_map.insert(key3.pubkey(), account_data);
2096
2097        let mut error_metrics = TransactionErrorMetrics::default();
2098        let loaded_programs = ProgramCacheForTxBatch::default();
2099
2100        let sanitized_transaction = SanitizedTransaction::new_for_tests(
2101            sanitized_message,
2102            vec![Signature::new_unique()],
2103            false,
2104        );
2105        let validation_result = Ok(ValidatedTransactionDetails {
2106            loaded_fee_payer_account: LoadedTransactionAccount {
2107                account: fee_payer_account,
2108                ..LoadedTransactionAccount::default()
2109            },
2110            ..ValidatedTransactionDetails::default()
2111        });
2112
2113        let mut load_results = load_accounts(
2114            &mock_bank,
2115            &[sanitized_transaction],
2116            vec![validation_result],
2117            &mut error_metrics,
2118            None,
2119            &FeatureSet::default(),
2120            &RentCollector::default(),
2121            &HashMap::new(),
2122            &loaded_programs,
2123        );
2124
2125        let mut account_data = AccountSharedData::default();
2126        account_data.set_rent_epoch(RENT_EXEMPT_RENT_EPOCH);
2127
2128        assert_eq!(load_results.len(), 1);
2129        let TransactionLoadResult::Loaded(loaded_transaction) = load_results.swap_remove(0) else {
2130            panic!("transaction loading failed");
2131        };
2132        assert_eq!(
2133            loaded_transaction,
2134            LoadedTransaction {
2135                accounts: vec![
2136                    (
2137                        key2.pubkey(),
2138                        mock_bank.accounts_map[&key2.pubkey()].clone()
2139                    ),
2140                    (
2141                        key1.pubkey(),
2142                        mock_bank.accounts_map[&key1.pubkey()].clone()
2143                    ),
2144                    (key4.pubkey(), account_data),
2145                    (
2146                        key3.pubkey(),
2147                        mock_bank.accounts_map[&key3.pubkey()].clone()
2148                    ),
2149                ],
2150                program_indices: vec![vec![1], vec![1]],
2151                fee_details: FeeDetails::default(),
2152                rollback_accounts: RollbackAccounts::default(),
2153                compute_budget_limits: ComputeBudgetLimits::default(),
2154                rent: 0,
2155                rent_debits: RentDebits::default(),
2156                loaded_accounts_data_size: 0,
2157            }
2158        );
2159    }
2160
2161    #[test]
2162    fn test_load_accounts_error() {
2163        let mock_bank = TestCallbacks::default();
2164        let feature_set = FeatureSet::default();
2165        let rent_collector = RentCollector::default();
2166
2167        let message = Message {
2168            account_keys: vec![Pubkey::new_from_array([0; 32])],
2169            header: MessageHeader::default(),
2170            instructions: vec![CompiledInstruction {
2171                program_id_index: 0,
2172                accounts: vec![],
2173                data: vec![],
2174            }],
2175            recent_blockhash: Hash::default(),
2176        };
2177
2178        let sanitized_message = new_unchecked_sanitized_message(message);
2179        let sanitized_transaction = SanitizedTransaction::new_for_tests(
2180            sanitized_message,
2181            vec![Signature::new_unique()],
2182            false,
2183        );
2184
2185        let validation_result = Ok(ValidatedTransactionDetails::default());
2186        let load_results = load_accounts(
2187            &mock_bank,
2188            &[sanitized_transaction.clone()],
2189            vec![validation_result.clone()],
2190            &mut TransactionErrorMetrics::default(),
2191            None,
2192            &feature_set,
2193            &rent_collector,
2194            &HashMap::new(),
2195            &ProgramCacheForTxBatch::default(),
2196        );
2197
2198        assert!(matches!(
2199            load_results[0],
2200            TransactionLoadResult::FeesOnly(FeesOnlyTransaction {
2201                load_error: TransactionError::InvalidProgramForExecution,
2202                ..
2203            }),
2204        ));
2205
2206        let validation_result = Err(TransactionError::InvalidWritableAccount);
2207
2208        let load_results = load_accounts(
2209            &mock_bank,
2210            &[sanitized_transaction.clone()],
2211            vec![validation_result],
2212            &mut TransactionErrorMetrics::default(),
2213            None,
2214            &feature_set,
2215            &rent_collector,
2216            &HashMap::new(),
2217            &ProgramCacheForTxBatch::default(),
2218        );
2219
2220        assert!(matches!(
2221            load_results[0],
2222            TransactionLoadResult::NotLoaded(TransactionError::InvalidWritableAccount),
2223        ));
2224    }
2225
2226    #[test]
2227    fn test_collect_rent_from_account() {
2228        let feature_set = FeatureSet::all_enabled();
2229        let rent_collector = RentCollector {
2230            epoch: 1,
2231            ..RentCollector::default()
2232        };
2233
2234        let address = Pubkey::new_unique();
2235        let min_exempt_balance = rent_collector.rent.minimum_balance(0);
2236        let mut account = AccountSharedData::from(Account {
2237            lamports: min_exempt_balance,
2238            ..Account::default()
2239        });
2240
2241        assert_eq!(
2242            collect_rent_from_account(&feature_set, &rent_collector, &address, &mut account),
2243            CollectedInfo::default()
2244        );
2245        assert_eq!(account.rent_epoch(), RENT_EXEMPT_RENT_EPOCH);
2246    }
2247
2248    #[test]
2249    fn test_collect_rent_from_account_rent_paying() {
2250        let feature_set = FeatureSet::all_enabled();
2251        let rent_collector = RentCollector {
2252            epoch: 1,
2253            ..RentCollector::default()
2254        };
2255
2256        let address = Pubkey::new_unique();
2257        let mut account = AccountSharedData::from(Account {
2258            lamports: 1,
2259            ..Account::default()
2260        });
2261
2262        assert_eq!(
2263            collect_rent_from_account(&feature_set, &rent_collector, &address, &mut account),
2264            CollectedInfo::default()
2265        );
2266        assert_eq!(account.rent_epoch(), 0);
2267        assert_eq!(account.lamports(), 1);
2268    }
2269
2270    #[test]
2271    fn test_collect_rent_from_account_rent_enabled() {
2272        let feature_set =
2273            all_features_except(Some(&[feature_set::disable_rent_fees_collection::id()]));
2274        let rent_collector = RentCollector {
2275            epoch: 1,
2276            ..RentCollector::default()
2277        };
2278
2279        let address = Pubkey::new_unique();
2280        let mut account = AccountSharedData::from(Account {
2281            lamports: 1,
2282            data: vec![0],
2283            ..Account::default()
2284        });
2285
2286        assert_eq!(
2287            collect_rent_from_account(&feature_set, &rent_collector, &address, &mut account),
2288            CollectedInfo {
2289                rent_amount: 1,
2290                account_data_len_reclaimed: 1
2291            }
2292        );
2293        assert_eq!(account.rent_epoch(), 0);
2294        assert_eq!(account.lamports(), 0);
2295    }
2296
2297    // Ensure `TransactionProcessingCallback::inspect_account()` is called when
2298    // loading accounts for transaction processing.
2299    #[test]
2300    fn test_inspect_account_non_fee_payer() {
2301        let mut mock_bank = TestCallbacks::default();
2302
2303        let address0 = Pubkey::new_unique(); // <-- fee payer
2304        let address1 = Pubkey::new_unique(); // <-- initially alive
2305        let address2 = Pubkey::new_unique(); // <-- initially dead
2306        let address3 = Pubkey::new_unique(); // <-- program
2307
2308        let mut account0 = AccountSharedData::default();
2309        account0.set_lamports(1_000_000_000);
2310        mock_bank.accounts_map.insert(address0, account0.clone());
2311
2312        let mut account1 = AccountSharedData::default();
2313        account1.set_lamports(2_000_000_000);
2314        mock_bank.accounts_map.insert(address1, account1.clone());
2315
2316        // account2 *not* added to the bank's accounts_map
2317
2318        let mut account3 = AccountSharedData::default();
2319        account3.set_lamports(4_000_000_000);
2320        account3.set_executable(true);
2321        account3.set_owner(native_loader::id());
2322        mock_bank.accounts_map.insert(address3, account3.clone());
2323
2324        let message = Message {
2325            account_keys: vec![address0, address1, address2, address3],
2326            header: MessageHeader::default(),
2327            instructions: vec![
2328                CompiledInstruction {
2329                    program_id_index: 3,
2330                    accounts: vec![0],
2331                    data: vec![],
2332                },
2333                CompiledInstruction {
2334                    program_id_index: 3,
2335                    accounts: vec![1, 2],
2336                    data: vec![],
2337                },
2338                CompiledInstruction {
2339                    program_id_index: 3,
2340                    accounts: vec![1],
2341                    data: vec![],
2342                },
2343            ],
2344            recent_blockhash: Hash::new_unique(),
2345        };
2346        let sanitized_message = new_unchecked_sanitized_message(message);
2347        let sanitized_transaction = SanitizedTransaction::new_for_tests(
2348            sanitized_message,
2349            vec![Signature::new_unique()],
2350            false,
2351        );
2352        let validation_result = Ok(ValidatedTransactionDetails {
2353            loaded_fee_payer_account: LoadedTransactionAccount {
2354                account: account0.clone(),
2355                ..LoadedTransactionAccount::default()
2356            },
2357            ..ValidatedTransactionDetails::default()
2358        });
2359        let _load_results = load_accounts(
2360            &mock_bank,
2361            &[sanitized_transaction],
2362            vec![validation_result],
2363            &mut TransactionErrorMetrics::default(),
2364            None,
2365            &FeatureSet::default(),
2366            &RentCollector::default(),
2367            &HashMap::new(),
2368            &ProgramCacheForTxBatch::default(),
2369        );
2370
2371        // ensure the loaded accounts are inspected
2372        let mut actual_inspected_accounts: Vec<_> = mock_bank
2373            .inspected_accounts
2374            .borrow()
2375            .iter()
2376            .map(|(k, v)| (*k, v.clone()))
2377            .collect();
2378        actual_inspected_accounts.sort_unstable_by(|a, b| a.0.cmp(&b.0));
2379
2380        let mut expected_inspected_accounts = vec![
2381            // *not* key0, since it is loaded during fee payer validation
2382            (address1, vec![(Some(account1), true)]),
2383            (address2, vec![(None, true)]),
2384            (address3, vec![(Some(account3), false)]),
2385        ];
2386        expected_inspected_accounts.sort_unstable_by(|a, b| a.0.cmp(&b.0));
2387
2388        assert_eq!(actual_inspected_accounts, expected_inspected_accounts,);
2389    }
2390
2391    #[test]
2392    fn test_load_transaction_accounts_data_sizes() {
2393        let mut mock_bank = TestCallbacks::default();
2394
2395        let loader_v2 = bpf_loader::id();
2396        let loader_v3 = bpf_loader_upgradeable::id();
2397        let program1_keypair = Keypair::new();
2398        let program1 = program1_keypair.pubkey();
2399        let program2 = Pubkey::new_unique();
2400        let programdata2 = Pubkey::new_unique();
2401        use solana_sdk::account_utils::StateMut;
2402
2403        let program2_size = std::mem::size_of::<UpgradeableLoaderState>() as u32;
2404        let mut program2_account = AccountSharedData::default();
2405        program2_account.set_owner(loader_v3);
2406        program2_account.set_lamports(LAMPORTS_PER_SOL);
2407        program2_account.set_executable(true);
2408        program2_account.set_data(vec![0; program2_size as usize]);
2409        program2_account
2410            .set_state(&UpgradeableLoaderState::Program {
2411                programdata_address: programdata2,
2412            })
2413            .unwrap();
2414        mock_bank.accounts_map.insert(program2, program2_account);
2415        let mut programdata2_account = AccountSharedData::default();
2416        programdata2_account.set_owner(loader_v3);
2417        programdata2_account.set_lamports(LAMPORTS_PER_SOL);
2418        programdata2_account.set_data(vec![0; program2_size as usize]);
2419        programdata2_account
2420            .set_state(&UpgradeableLoaderState::ProgramData {
2421                slot: 0,
2422                upgrade_authority_address: None,
2423            })
2424            .unwrap();
2425        let mut programdata = programdata2_account.data().to_vec();
2426        let mut file =
2427            File::open("tests/example-programs/hello-solana/hello_solana_program.so").unwrap();
2428        file.read_to_end(&mut programdata).unwrap();
2429        let programdata2_size = programdata.len() as u32;
2430        programdata2_account.set_data(programdata);
2431        mock_bank
2432            .accounts_map
2433            .insert(programdata2, programdata2_account);
2434
2435        let mut next_size = 1;
2436        let mut make_account = |pubkey, owner, executable| {
2437            let size = next_size;
2438            let account = AccountSharedData::create(
2439                LAMPORTS_PER_SOL,
2440                vec![0; size],
2441                owner,
2442                executable,
2443                u64::MAX,
2444            );
2445
2446            mock_bank.accounts_map.insert(pubkey, account.clone());
2447
2448            // accounts are counted at most twice
2449            // by multiplying account size by 4, we ensure all totals are unique
2450            next_size *= 4;
2451
2452            (size as u32, account)
2453        };
2454
2455        let fee_payer_keypair = Keypair::new();
2456        let fee_payer = fee_payer_keypair.pubkey();
2457        let (fee_payer_size, fee_payer_account) =
2458            make_account(fee_payer, system_program::id(), false);
2459
2460        let account1 = Pubkey::new_unique();
2461        let (account1_size, _) = make_account(account1, program1, false);
2462
2463        let account2 = Pubkey::new_unique();
2464        let (account2_size, _) = make_account(account2, program2, false);
2465
2466        let (native_loader_size, _) = make_account(native_loader::id(), native_loader::id(), true);
2467        let (bpf_loader_size, _) = make_account(loader_v2, native_loader::id(), true);
2468        let (upgradeable_loader_size, _) = make_account(loader_v3, native_loader::id(), true);
2469
2470        let (_program1_size, _) = make_account(program1, loader_v2, true);
2471
2472        let mut program_accounts = HashMap::new();
2473        program_accounts.insert(program1, (&loader_v2, 0));
2474        program_accounts.insert(program2, (&loader_v3, 0));
2475        let mut program_cache = ProgramCacheForTxBatch::default();
2476        let program1_entry = ProgramCacheEntry {
2477            account_size: 0,
2478            account_owner: ProgramCacheEntryOwner::LoaderV2,
2479            program: ProgramCacheEntryType::Closed,
2480            ..ProgramCacheEntry::default()
2481        };
2482        program_cache.replenish(program1, Arc::new(program1_entry));
2483        let program2_entry = ProgramCacheEntry {
2484            account_size: (program2_size + programdata2_size) as usize,
2485            account_owner: ProgramCacheEntryOwner::LoaderV3,
2486            program: ProgramCacheEntryType::Unloaded(Arc::new(BuiltinProgram::new_mock())),
2487            ..ProgramCacheEntry::default()
2488        };
2489        program_cache.replenish(program2, Arc::new(program2_entry));
2490
2491        let test_transaction_data_size = |transaction, expected_size| {
2492            let loaded_transaction_accounts = load_transaction_accounts(
2493                &mock_bank,
2494                &transaction,
2495                LoadedTransactionAccount {
2496                    account: fee_payer_account.clone(),
2497                    loaded_size: fee_payer_size as usize,
2498                    rent_collected: 0,
2499                },
2500                &ComputeBudgetLimits::default(),
2501                &mut TransactionErrorMetrics::default(),
2502                None,
2503                &FeatureSet::default(),
2504                &RentCollector::default(),
2505                &program_accounts,
2506                &program_cache,
2507            )
2508            .unwrap();
2509
2510            assert_eq!(
2511                loaded_transaction_accounts.loaded_accounts_data_size,
2512                expected_size
2513            );
2514        };
2515
2516        let test_data_size = |instructions: Vec<_>, expected_size| {
2517            let transaction = SanitizedTransaction::from_transaction_for_tests(
2518                Transaction::new_signed_with_payer(
2519                    &instructions,
2520                    Some(&fee_payer),
2521                    &[&fee_payer_keypair],
2522                    Hash::default(),
2523                ),
2524            );
2525
2526            test_transaction_data_size(transaction, expected_size)
2527        };
2528
2529        for account_meta in [AccountMeta::new, AccountMeta::new_readonly] {
2530            // one program plus loader
2531            let ixns = vec![Instruction::new_with_bytes(program1, &[], vec![])];
2532            test_data_size(ixns, bpf_loader_size + fee_payer_size);
2533
2534            // two programs, two loaders, two accounts
2535            let ixns = vec![
2536                Instruction::new_with_bytes(program1, &[], vec![account_meta(account1, false)]),
2537                Instruction::new_with_bytes(program2, &[], vec![account_meta(account2, false)]),
2538            ];
2539            test_data_size(
2540                ixns,
2541                account1_size
2542                    + account2_size
2543                    + program2_size
2544                    + programdata2_size
2545                    + bpf_loader_size
2546                    + upgradeable_loader_size
2547                    + fee_payer_size,
2548            );
2549
2550            // ordinary owners not counted
2551            let ixns = vec![Instruction::new_with_bytes(
2552                program1,
2553                &[],
2554                vec![account_meta(account2, false)],
2555            )];
2556            test_data_size(ixns, account2_size + bpf_loader_size + fee_payer_size);
2557
2558            // program and loader counted once
2559            let ixns = vec![
2560                Instruction::new_with_bytes(program2, &[], vec![]),
2561                Instruction::new_with_bytes(program2, &[], vec![]),
2562            ];
2563            test_data_size(
2564                ixns,
2565                upgradeable_loader_size + program2_size + programdata2_size + fee_payer_size,
2566            );
2567
2568            // native loader not counted if loader
2569            let ixns = vec![Instruction::new_with_bytes(bpf_loader::id(), &[], vec![])];
2570            test_data_size(ixns, bpf_loader_size + fee_payer_size);
2571
2572            // native loader counted if instruction
2573            let ixns = vec![Instruction::new_with_bytes(
2574                bpf_loader::id(),
2575                &[],
2576                vec![account_meta(native_loader::id(), false)],
2577            )];
2578            test_data_size(ixns, bpf_loader_size + native_loader_size + fee_payer_size);
2579
2580            // native loader counted if invoked
2581            let ixns = vec![Instruction::new_with_bytes(
2582                native_loader::id(),
2583                &[],
2584                vec![],
2585            )];
2586            test_data_size(ixns, native_loader_size + fee_payer_size);
2587
2588            // native loader counted once if invoked and instruction
2589            let ixns = vec![Instruction::new_with_bytes(
2590                native_loader::id(),
2591                &[],
2592                vec![account_meta(native_loader::id(), false)],
2593            )];
2594            test_data_size(ixns, native_loader_size + fee_payer_size);
2595
2596            // loader counted twice if included in instruction
2597            let ixns = vec![Instruction::new_with_bytes(
2598                program2,
2599                &[],
2600                vec![account_meta(bpf_loader_upgradeable::id(), false)],
2601            )];
2602            test_data_size(
2603                ixns,
2604                upgradeable_loader_size * 2 + program2_size + programdata2_size + fee_payer_size,
2605            );
2606
2607            // loader counted twice even if included first
2608            let ixns = vec![
2609                Instruction::new_with_bytes(bpf_loader_upgradeable::id(), &[], vec![]),
2610                Instruction::new_with_bytes(program2, &[], vec![]),
2611            ];
2612            test_data_size(
2613                ixns,
2614                upgradeable_loader_size * 2 + program2_size + programdata2_size + fee_payer_size,
2615            );
2616
2617            // cover that case with multiple loaders to be sure
2618            let ixns = vec![
2619                Instruction::new_with_bytes(
2620                    program1,
2621                    &[],
2622                    vec![
2623                        account_meta(bpf_loader::id(), false),
2624                        account_meta(bpf_loader_upgradeable::id(), false),
2625                    ],
2626                ),
2627                Instruction::new_with_bytes(program2, &[], vec![account_meta(account1, false)]),
2628                Instruction::new_with_bytes(
2629                    bpf_loader_upgradeable::id(),
2630                    &[],
2631                    vec![account_meta(account1, false)],
2632                ),
2633            ];
2634            test_data_size(
2635                ixns,
2636                account1_size
2637                    + program2_size
2638                    + programdata2_size
2639                    + bpf_loader_size * 2
2640                    + upgradeable_loader_size * 2
2641                    + fee_payer_size,
2642            );
2643
2644            // fee-payer counted once
2645            let ixns = vec![Instruction::new_with_bytes(
2646                program1,
2647                &[],
2648                vec![account_meta(fee_payer, false)],
2649            )];
2650            test_data_size(ixns, bpf_loader_size + fee_payer_size);
2651
2652            // normal function call uses the combined cache size
2653            let ixns = vec![Instruction::new_with_bytes(program2, &[], vec![])];
2654            test_data_size(
2655                ixns,
2656                program2_size + programdata2_size + upgradeable_loader_size + fee_payer_size,
2657            );
2658
2659            // program as instruction account bypasses the cache
2660            let ixns = vec![Instruction::new_with_bytes(
2661                program2,
2662                &[],
2663                vec![account_meta(program2, false)],
2664            )];
2665            test_data_size(
2666                ixns,
2667                program2_size + upgradeable_loader_size + fee_payer_size,
2668            );
2669
2670            // programdata as instruction account double-counts it
2671            let ixns = vec![Instruction::new_with_bytes(
2672                program2,
2673                &[],
2674                vec![account_meta(programdata2, false)],
2675            )];
2676
2677            test_data_size(
2678                ixns,
2679                program2_size + programdata2_size * 2 + upgradeable_loader_size + fee_payer_size,
2680            );
2681
2682            // both as instruction accounts, for completeness
2683            let ixns = vec![Instruction::new_with_bytes(
2684                program2,
2685                &[],
2686                vec![
2687                    account_meta(program2, false),
2688                    account_meta(programdata2, false),
2689                ],
2690            )];
2691            test_data_size(
2692                ixns,
2693                program2_size + programdata2_size + upgradeable_loader_size + fee_payer_size,
2694            );
2695
2696            // writable program bypasses the cache
2697            let tx = new_unchecked_sanitized_transaction_with_writable_program(program2, fee_payer);
2698            test_transaction_data_size(
2699                tx,
2700                program2_size + upgradeable_loader_size + fee_payer_size,
2701            );
2702
2703            // NOTE for the new loader we *must* also test arbitrary permutations of the cache transactions
2704            // to ensure that the batched loading is overridden on a tx-per-tx basis
2705        }
2706    }
2707}