solana_svm/
transaction_processor.rs

1#[cfg(feature = "dev-context-only-utils")]
2use qualifier_attr::{field_qualifiers, qualifiers};
3use {
4    crate::{
5        account_loader::{
6            collect_rent_from_account, load_transaction, validate_fee_payer, AccountLoader,
7            CheckedTransactionDetails, LoadedTransaction, TransactionCheckResult,
8            TransactionLoadResult, ValidatedTransactionDetails,
9        },
10        account_overrides::AccountOverrides,
11        message_processor::process_message,
12        nonce_info::NonceInfo,
13        program_loader::{get_program_modification_slot, load_program_with_pubkey},
14        rollback_accounts::RollbackAccounts,
15        transaction_account_state_info::TransactionAccountStateInfo,
16        transaction_error_metrics::TransactionErrorMetrics,
17        transaction_execution_result::{ExecutedTransaction, TransactionExecutionDetails},
18        transaction_processing_callback::TransactionProcessingCallback,
19        transaction_processing_result::{ProcessedTransaction, TransactionProcessingResult},
20    },
21    log::debug,
22    percentage::Percentage,
23    solana_account::{state_traits::StateMut, AccountSharedData, ReadableAccount, PROGRAM_OWNERS},
24    solana_bpf_loader_program::syscalls::{
25        create_program_runtime_environment_v1, create_program_runtime_environment_v2,
26    },
27    solana_clock::{Epoch, Slot},
28    solana_compute_budget::compute_budget::ComputeBudget,
29    solana_compute_budget_instruction::instructions_processor::process_compute_budget_instructions,
30    solana_feature_set::{
31        enable_transaction_loading_failure_fees, remove_accounts_executable_flag_checks, FeatureSet,
32    },
33    solana_fee_structure::{FeeBudgetLimits, FeeDetails, FeeStructure},
34    solana_hash::Hash,
35    solana_instruction::TRANSACTION_LEVEL_STACK_HEIGHT,
36    solana_log_collector::LogCollector,
37    solana_measure::{measure::Measure, measure_us},
38    solana_message::compiled_instruction::CompiledInstruction,
39    solana_nonce::{
40        state::{DurableNonce, State as NonceState},
41        versions::Versions as NonceVersions,
42    },
43    solana_program_runtime::{
44        invoke_context::{EnvironmentConfig, InvokeContext},
45        loaded_programs::{
46            ForkGraph, ProgramCache, ProgramCacheEntry, ProgramCacheForTxBatch,
47            ProgramCacheMatchCriteria, ProgramRuntimeEnvironment,
48        },
49        solana_sbpf::{program::BuiltinProgram, vm::Config as VmConfig},
50        sysvar_cache::SysvarCache,
51    },
52    solana_pubkey::Pubkey,
53    solana_sdk::{
54        inner_instruction::{InnerInstruction, InnerInstructionsList},
55        rent_collector::RentCollector,
56    },
57    solana_sdk_ids::{native_loader, system_program},
58    solana_svm_rent_collector::svm_rent_collector::SVMRentCollector,
59    solana_svm_transaction::{svm_message::SVMMessage, svm_transaction::SVMTransaction},
60    solana_timings::{ExecuteTimingType, ExecuteTimings},
61    solana_transaction_context::{ExecutionRecord, TransactionContext},
62    solana_transaction_error::{TransactionError, TransactionResult},
63    solana_type_overrides::sync::{atomic::Ordering, Arc, RwLock, RwLockReadGuard},
64    std::{
65        collections::{hash_map::Entry, HashMap, HashSet},
66        fmt::{Debug, Formatter},
67        rc::Rc,
68        sync::Weak,
69    },
70};
71
72/// A list of log messages emitted during a transaction
73pub type TransactionLogMessages = Vec<String>;
74
75/// The output of the transaction batch processor's
76/// `load_and_execute_sanitized_transactions` method.
77pub struct LoadAndExecuteSanitizedTransactionsOutput {
78    /// Error metrics for transactions that were processed.
79    pub error_metrics: TransactionErrorMetrics,
80    /// Timings for transaction batch execution.
81    pub execute_timings: ExecuteTimings,
82    /// Vector of results indicating whether a transaction was processed or
83    /// could not be processed. Note processed transactions can still have a
84    /// failure result meaning that the transaction will be rolled back.
85    pub processing_results: Vec<TransactionProcessingResult>,
86}
87
88/// Configuration of the recording capabilities for transaction execution
89#[derive(Copy, Clone, Default)]
90pub struct ExecutionRecordingConfig {
91    pub enable_cpi_recording: bool,
92    pub enable_log_recording: bool,
93    pub enable_return_data_recording: bool,
94}
95
96impl ExecutionRecordingConfig {
97    pub fn new_single_setting(option: bool) -> Self {
98        ExecutionRecordingConfig {
99            enable_return_data_recording: option,
100            enable_log_recording: option,
101            enable_cpi_recording: option,
102        }
103    }
104}
105
106/// Configurations for processing transactions.
107#[derive(Default)]
108pub struct TransactionProcessingConfig<'a> {
109    /// Encapsulates overridden accounts, typically used for transaction
110    /// simulation.
111    pub account_overrides: Option<&'a AccountOverrides>,
112    /// Whether or not to check a program's modification slot when replenishing
113    /// a program cache instance.
114    pub check_program_modification_slot: bool,
115    /// The compute budget to use for transaction execution.
116    pub compute_budget: Option<ComputeBudget>,
117    /// The maximum number of bytes that log messages can consume.
118    pub log_messages_bytes_limit: Option<usize>,
119    /// Whether to limit the number of programs loaded for the transaction
120    /// batch.
121    pub limit_to_load_programs: bool,
122    /// Recording capabilities for transaction execution.
123    pub recording_config: ExecutionRecordingConfig,
124    /// The max number of accounts that a transaction may lock.
125    pub transaction_account_lock_limit: Option<usize>,
126}
127
128/// Runtime environment for transaction batch processing.
129pub struct TransactionProcessingEnvironment<'a> {
130    /// The blockhash to use for the transaction batch.
131    pub blockhash: Hash,
132    /// Lamports per signature that corresponds to this blockhash.
133    ///
134    /// Note: This value is primarily used for nonce accounts. If set to zero,
135    /// it will disable transaction fees. However, any non-zero value will not
136    /// change transaction fees. For this reason, it is recommended to use the
137    /// `fee_per_signature` field to adjust transaction fees.
138    pub blockhash_lamports_per_signature: u64,
139    /// The total stake for the current epoch.
140    pub epoch_total_stake: u64,
141    /// Runtime feature set to use for the transaction batch.
142    pub feature_set: Arc<FeatureSet>,
143    /// Transaction fee to charge per signature, in lamports.
144    pub fee_lamports_per_signature: u64,
145    /// Rent collector to use for the transaction batch.
146    pub rent_collector: Option<&'a dyn SVMRentCollector>,
147}
148
149impl Default for TransactionProcessingEnvironment<'_> {
150    fn default() -> Self {
151        Self {
152            blockhash: Hash::default(),
153            blockhash_lamports_per_signature: 0,
154            epoch_total_stake: 0,
155            feature_set: Arc::<FeatureSet>::default(),
156            fee_lamports_per_signature: FeeStructure::default().lamports_per_signature, // <-- Default fee.
157            rent_collector: None,
158        }
159    }
160}
161
162#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
163#[cfg_attr(
164    feature = "dev-context-only-utils",
165    field_qualifiers(slot(pub), epoch(pub))
166)]
167pub struct TransactionBatchProcessor<FG: ForkGraph> {
168    /// Bank slot (i.e. block)
169    slot: Slot,
170
171    /// Bank epoch
172    epoch: Epoch,
173
174    /// SysvarCache is a collection of system variables that are
175    /// accessible from on chain programs. It is passed to SVM from
176    /// client code (e.g. Bank) and forwarded to process_message.
177    sysvar_cache: RwLock<SysvarCache>,
178
179    /// Programs required for transaction batch processing
180    pub program_cache: Arc<RwLock<ProgramCache<FG>>>,
181
182    /// Builtin program ids
183    pub builtin_program_ids: RwLock<HashSet<Pubkey>>,
184}
185
186impl<FG: ForkGraph> Debug for TransactionBatchProcessor<FG> {
187    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
188        f.debug_struct("TransactionBatchProcessor")
189            .field("slot", &self.slot)
190            .field("epoch", &self.epoch)
191            .field("sysvar_cache", &self.sysvar_cache)
192            .field("program_cache", &self.program_cache)
193            .finish()
194    }
195}
196
197impl<FG: ForkGraph> Default for TransactionBatchProcessor<FG> {
198    fn default() -> Self {
199        Self {
200            slot: Slot::default(),
201            epoch: Epoch::default(),
202            sysvar_cache: RwLock::<SysvarCache>::default(),
203            program_cache: Arc::new(RwLock::new(ProgramCache::new(
204                Slot::default(),
205                Epoch::default(),
206            ))),
207            builtin_program_ids: RwLock::new(HashSet::new()),
208        }
209    }
210}
211
212impl<FG: ForkGraph> TransactionBatchProcessor<FG> {
213    /// Create a new, uninitialized `TransactionBatchProcessor`.
214    ///
215    /// In this context, uninitialized means that the `TransactionBatchProcessor`
216    /// has been initialized with an empty program cache. The cache contains no
217    /// programs (including builtins) and has not been configured with a valid
218    /// fork graph.
219    ///
220    /// When using this method, it's advisable to call `set_fork_graph_in_program_cache`
221    /// as well as `add_builtin` to configure the cache before using the processor.
222    pub fn new_uninitialized(slot: Slot, epoch: Epoch) -> Self {
223        Self {
224            slot,
225            epoch,
226            program_cache: Arc::new(RwLock::new(ProgramCache::new(slot, epoch))),
227            ..Self::default()
228        }
229    }
230
231    /// Create a new `TransactionBatchProcessor`.
232    ///
233    /// The created processor's program cache is initialized with the provided
234    /// fork graph and loaders. If any loaders are omitted, a default "empty"
235    /// loader (no syscalls) will be used.
236    ///
237    /// The cache will still not contain any builtin programs. It's advisable to
238    /// call `add_builtin` to add the required builtins before using the processor.
239    pub fn new(
240        slot: Slot,
241        epoch: Epoch,
242        fork_graph: Weak<RwLock<FG>>,
243        program_runtime_environment_v1: Option<ProgramRuntimeEnvironment>,
244        program_runtime_environment_v2: Option<ProgramRuntimeEnvironment>,
245    ) -> Self {
246        let processor = Self::new_uninitialized(slot, epoch);
247        {
248            let mut program_cache = processor.program_cache.write().unwrap();
249            program_cache.set_fork_graph(fork_graph);
250            processor.configure_program_runtime_environments_inner(
251                &mut program_cache,
252                program_runtime_environment_v1,
253                program_runtime_environment_v2,
254            );
255        }
256        processor
257    }
258
259    /// Create a new `TransactionBatchProcessor` from the current instance, but
260    /// with the provided slot and epoch.
261    ///
262    /// * Inherits the program cache and builtin program ids from the current
263    ///   instance.
264    /// * Resets the sysvar cache.
265    pub fn new_from(&self, slot: Slot, epoch: Epoch) -> Self {
266        Self {
267            slot,
268            epoch,
269            sysvar_cache: RwLock::<SysvarCache>::default(),
270            program_cache: self.program_cache.clone(),
271            builtin_program_ids: RwLock::new(self.builtin_program_ids.read().unwrap().clone()),
272        }
273    }
274
275    fn configure_program_runtime_environments_inner(
276        &self,
277        program_cache: &mut ProgramCache<FG>,
278        program_runtime_environment_v1: Option<ProgramRuntimeEnvironment>,
279        program_runtime_environment_v2: Option<ProgramRuntimeEnvironment>,
280    ) {
281        let empty_loader = || Arc::new(BuiltinProgram::new_loader(VmConfig::default()));
282
283        program_cache.latest_root_slot = self.slot;
284        program_cache.latest_root_epoch = self.epoch;
285        program_cache.environments.program_runtime_v1 =
286            program_runtime_environment_v1.unwrap_or(empty_loader());
287        program_cache.environments.program_runtime_v2 =
288            program_runtime_environment_v2.unwrap_or(empty_loader());
289    }
290
291    /// Configures the program runtime environments (loaders) in the
292    /// transaction processor's program cache.
293    pub fn configure_program_runtime_environments(
294        &self,
295        program_runtime_environment_v1: Option<ProgramRuntimeEnvironment>,
296        program_runtime_environment_v2: Option<ProgramRuntimeEnvironment>,
297    ) {
298        self.configure_program_runtime_environments_inner(
299            &mut self.program_cache.write().unwrap(),
300            program_runtime_environment_v1,
301            program_runtime_environment_v2,
302        );
303    }
304
305    /// Returns the current environments depending on the given epoch
306    /// Returns None if the call could result in a deadlock
307    #[cfg(feature = "dev-context-only-utils")]
308    pub fn get_environments_for_epoch(
309        &self,
310        epoch: Epoch,
311    ) -> Option<solana_program_runtime::loaded_programs::ProgramRuntimeEnvironments> {
312        self.program_cache
313            .try_read()
314            .ok()
315            .map(|cache| cache.get_environments_for_epoch(epoch))
316    }
317
318    pub fn sysvar_cache(&self) -> RwLockReadGuard<SysvarCache> {
319        self.sysvar_cache.read().unwrap()
320    }
321
322    /// Main entrypoint to the SVM.
323    pub fn load_and_execute_sanitized_transactions<CB: TransactionProcessingCallback>(
324        &self,
325        callbacks: &CB,
326        sanitized_txs: &[impl SVMTransaction],
327        check_results: Vec<TransactionCheckResult>,
328        environment: &TransactionProcessingEnvironment,
329        config: &TransactionProcessingConfig,
330    ) -> LoadAndExecuteSanitizedTransactionsOutput {
331        // If `check_results` does not have the same length as `sanitized_txs`,
332        // transactions could be truncated as a result of `.iter().zip()` in
333        // many of the below methods.
334        // See <https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.zip>.
335        debug_assert_eq!(
336            sanitized_txs.len(),
337            check_results.len(),
338            "Length of check_results does not match length of sanitized_txs"
339        );
340
341        // Initialize metrics.
342        let mut error_metrics = TransactionErrorMetrics::default();
343        let mut execute_timings = ExecuteTimings::default();
344        let mut processing_results = Vec::with_capacity(sanitized_txs.len());
345
346        let native_loader = native_loader::id();
347        let (program_accounts_map, filter_executable_us) = measure_us!({
348            let mut program_accounts_map = Self::filter_executable_program_accounts(
349                callbacks,
350                sanitized_txs,
351                &check_results,
352                PROGRAM_OWNERS,
353            );
354            for builtin_program in self.builtin_program_ids.read().unwrap().iter() {
355                program_accounts_map.insert(*builtin_program, (&native_loader, 0));
356            }
357            program_accounts_map
358        });
359
360        let (mut program_cache_for_tx_batch, program_cache_us) = measure_us!({
361            let program_cache_for_tx_batch = self.replenish_program_cache(
362                callbacks,
363                &program_accounts_map,
364                &mut execute_timings,
365                config.check_program_modification_slot,
366                config.limit_to_load_programs,
367            );
368
369            if program_cache_for_tx_batch.hit_max_limit {
370                return LoadAndExecuteSanitizedTransactionsOutput {
371                    error_metrics,
372                    execute_timings,
373                    processing_results: (0..sanitized_txs.len())
374                        .map(|_| Err(TransactionError::ProgramCacheHitMaxLimit))
375                        .collect(),
376                };
377            }
378
379            program_cache_for_tx_batch
380        });
381
382        // Determine a capacity for the internal account cache. This
383        // over-allocates but avoids ever reallocating, and spares us from
384        // deduplicating the account keys lists.
385        let account_keys_in_batch = sanitized_txs.iter().map(|tx| tx.account_keys().len()).sum();
386
387        // Create the account loader, which wraps all external account fetching.
388        let mut account_loader = AccountLoader::new_with_account_cache_capacity(
389            config.account_overrides,
390            callbacks,
391            environment.feature_set.clone(),
392            account_keys_in_batch,
393        );
394
395        let enable_transaction_loading_failure_fees = environment
396            .feature_set
397            .is_active(&enable_transaction_loading_failure_fees::id());
398
399        let (mut validate_fees_us, mut load_us, mut execution_us): (u64, u64, u64) = (0, 0, 0);
400
401        // Validate, execute, and collect results from each transaction in order.
402        // With SIMD83, transactions must be executed in order, because transactions
403        // in the same batch may modify the same accounts. Transaction order is
404        // preserved within entries written to the ledger.
405        for (tx, check_result) in sanitized_txs.iter().zip(check_results) {
406            let (validate_result, single_validate_fees_us) =
407                measure_us!(check_result.and_then(|tx_details| {
408                    Self::validate_transaction_nonce_and_fee_payer(
409                        &mut account_loader,
410                        tx,
411                        tx_details,
412                        &environment.blockhash,
413                        environment.fee_lamports_per_signature,
414                        environment
415                            .rent_collector
416                            .unwrap_or(&RentCollector::default()),
417                        &mut error_metrics,
418                        callbacks,
419                    )
420                }));
421            validate_fees_us = validate_fees_us.saturating_add(single_validate_fees_us);
422
423            let (load_result, single_load_us) = measure_us!(load_transaction(
424                &mut account_loader,
425                tx,
426                validate_result,
427                &mut error_metrics,
428                environment
429                    .rent_collector
430                    .unwrap_or(&RentCollector::default()),
431            ));
432            load_us = load_us.saturating_add(single_load_us);
433
434            let (processing_result, single_execution_us) = measure_us!(match load_result {
435                TransactionLoadResult::NotLoaded(err) => Err(err),
436                TransactionLoadResult::FeesOnly(fees_only_tx) => {
437                    if enable_transaction_loading_failure_fees {
438                        // Update loaded accounts cache with nonce and fee-payer
439                        account_loader
440                            .update_accounts_for_failed_tx(tx, &fees_only_tx.rollback_accounts);
441
442                        Ok(ProcessedTransaction::FeesOnly(Box::new(fees_only_tx)))
443                    } else {
444                        Err(fees_only_tx.load_error)
445                    }
446                }
447                TransactionLoadResult::Loaded(loaded_transaction) => {
448                    let executed_tx = self.execute_loaded_transaction(
449                        callbacks,
450                        tx,
451                        loaded_transaction,
452                        &mut execute_timings,
453                        &mut error_metrics,
454                        &mut program_cache_for_tx_batch,
455                        environment,
456                        config,
457                    );
458
459                    // Update loaded accounts cache with account states which might have changed.
460                    // Also update local program cache with modifications made by the transaction,
461                    // if it executed successfully.
462                    account_loader.update_accounts_for_executed_tx(tx, &executed_tx);
463                    if executed_tx.was_successful() {
464                        program_cache_for_tx_batch.merge(&executed_tx.programs_modified_by_tx);
465                    }
466
467                    Ok(ProcessedTransaction::Executed(Box::new(executed_tx)))
468                }
469            });
470            execution_us = execution_us.saturating_add(single_execution_us);
471
472            processing_results.push(processing_result);
473        }
474
475        // Skip eviction when there's no chance this particular tx batch has increased the size of
476        // ProgramCache entries. Note that loaded_missing is deliberately defined, so that there's
477        // still at least one other batch, which will evict the program cache, even after the
478        // occurrences of cooperative loading.
479        if program_cache_for_tx_batch.loaded_missing || program_cache_for_tx_batch.merged_modified {
480            const SHRINK_LOADED_PROGRAMS_TO_PERCENTAGE: u8 = 90;
481            self.program_cache
482                .write()
483                .unwrap()
484                .evict_using_2s_random_selection(
485                    Percentage::from(SHRINK_LOADED_PROGRAMS_TO_PERCENTAGE),
486                    self.slot,
487                );
488        }
489
490        debug!(
491            "load: {}us execute: {}us txs_len={}",
492            load_us,
493            execution_us,
494            sanitized_txs.len(),
495        );
496
497        execute_timings
498            .saturating_add_in_place(ExecuteTimingType::ValidateFeesUs, validate_fees_us);
499        execute_timings
500            .saturating_add_in_place(ExecuteTimingType::FilterExecutableUs, filter_executable_us);
501        execute_timings
502            .saturating_add_in_place(ExecuteTimingType::ProgramCacheUs, program_cache_us);
503        execute_timings.saturating_add_in_place(ExecuteTimingType::LoadUs, load_us);
504        execute_timings.saturating_add_in_place(ExecuteTimingType::ExecuteUs, execution_us);
505
506        LoadAndExecuteSanitizedTransactionsOutput {
507            error_metrics,
508            execute_timings,
509            processing_results,
510        }
511    }
512
513    fn validate_transaction_nonce_and_fee_payer<CB: TransactionProcessingCallback>(
514        account_loader: &mut AccountLoader<CB>,
515        message: &impl SVMMessage,
516        checked_details: CheckedTransactionDetails,
517        environment_blockhash: &Hash,
518        fee_lamports_per_signature: u64,
519        rent_collector: &dyn SVMRentCollector,
520        error_counters: &mut TransactionErrorMetrics,
521        callbacks: &CB,
522    ) -> TransactionResult<ValidatedTransactionDetails> {
523        // If this is a nonce transaction, validate the nonce info.
524        // This must be done for every transaction to support SIMD83 because
525        // it may have changed due to use, authorization, or deallocation.
526        // This function is a successful no-op if given a blockhash transaction.
527        if let CheckedTransactionDetails {
528            nonce: Some(ref nonce_info),
529            lamports_per_signature: _,
530        } = checked_details
531        {
532            let next_durable_nonce = DurableNonce::from_blockhash(environment_blockhash);
533            Self::validate_transaction_nonce(
534                account_loader,
535                message,
536                nonce_info,
537                &next_durable_nonce,
538                error_counters,
539            )?;
540        }
541
542        // Now validate the fee-payer for the transaction unconditionally.
543        Self::validate_transaction_fee_payer(
544            account_loader,
545            message,
546            checked_details,
547            fee_lamports_per_signature,
548            rent_collector,
549            error_counters,
550            callbacks,
551        )
552    }
553
554    // Loads transaction fee payer, collects rent if necessary, then calculates
555    // transaction fees, and deducts them from the fee payer balance. If the
556    // account is not found or has insufficient funds, an error is returned.
557    fn validate_transaction_fee_payer<CB: TransactionProcessingCallback>(
558        account_loader: &mut AccountLoader<CB>,
559        message: &impl SVMMessage,
560        checked_details: CheckedTransactionDetails,
561        fee_lamports_per_signature: u64,
562        rent_collector: &dyn SVMRentCollector,
563        error_counters: &mut TransactionErrorMetrics,
564        callbacks: &CB,
565    ) -> TransactionResult<ValidatedTransactionDetails> {
566        let compute_budget_limits = process_compute_budget_instructions(
567            message.program_instructions_iter(),
568            &account_loader.feature_set,
569        )
570        .inspect_err(|_err| {
571            error_counters.invalid_compute_budget += 1;
572        })?;
573
574        let fee_payer_address = message.fee_payer();
575
576        let Some(mut loaded_fee_payer) = account_loader.load_account(fee_payer_address, true)
577        else {
578            error_counters.account_not_found += 1;
579            return Err(TransactionError::AccountNotFound);
580        };
581
582        let fee_payer_loaded_rent_epoch = loaded_fee_payer.account.rent_epoch();
583        loaded_fee_payer.rent_collected = collect_rent_from_account(
584            &account_loader.feature_set,
585            rent_collector,
586            fee_payer_address,
587            &mut loaded_fee_payer.account,
588        )
589        .rent_amount;
590
591        let CheckedTransactionDetails {
592            nonce,
593            lamports_per_signature,
594        } = checked_details;
595
596        let fee_budget_limits = FeeBudgetLimits::from(compute_budget_limits);
597        let fee_details = if lamports_per_signature == 0 {
598            FeeDetails::default()
599        } else {
600            callbacks.calculate_fee(
601                message,
602                fee_lamports_per_signature,
603                fee_budget_limits.prioritization_fee,
604                account_loader.feature_set.as_ref(),
605            )
606        };
607
608        let fee_payer_index = 0;
609        validate_fee_payer(
610            fee_payer_address,
611            &mut loaded_fee_payer.account,
612            fee_payer_index,
613            error_counters,
614            rent_collector,
615            fee_details.total_fee(),
616        )?;
617
618        // Capture fee-subtracted fee payer account and next nonce account state
619        // to commit if transaction execution fails.
620        let rollback_accounts = RollbackAccounts::new(
621            nonce,
622            *fee_payer_address,
623            loaded_fee_payer.account.clone(),
624            loaded_fee_payer.rent_collected,
625            fee_payer_loaded_rent_epoch,
626        );
627
628        Ok(ValidatedTransactionDetails {
629            fee_details,
630            rollback_accounts,
631            compute_budget_limits,
632            loaded_fee_payer_account: loaded_fee_payer,
633        })
634    }
635
636    fn validate_transaction_nonce<CB: TransactionProcessingCallback>(
637        account_loader: &mut AccountLoader<CB>,
638        message: &impl SVMMessage,
639        nonce_info: &NonceInfo,
640        next_durable_nonce: &DurableNonce,
641        error_counters: &mut TransactionErrorMetrics,
642    ) -> TransactionResult<()> {
643        // When SIMD83 is enabled, if the nonce has been used in this batch already, we must drop
644        // the transaction. This is the same as if it was used in different batches in the same slot.
645        // If the nonce account was closed in the batch, we error as if the blockhash didn't validate.
646        // We must validate the account in case it was reopened, either as a normal system account,
647        // or a fake nonce account. We must also check the signer in case the authority was changed.
648        //
649        // Note these checks are *not* obviated by fee-only transactions.
650        let nonce_is_valid = account_loader
651            .load_account(nonce_info.address(), true)
652            .and_then(|loaded_nonce| {
653                let current_nonce_account = &loaded_nonce.account;
654                system_program::check_id(current_nonce_account.owner()).then_some(())?;
655                StateMut::<NonceVersions>::state(current_nonce_account).ok()
656            })
657            .and_then(
658                |current_nonce_versions| match current_nonce_versions.state() {
659                    NonceState::Initialized(ref current_nonce_data) => {
660                        let nonce_can_be_advanced =
661                            &current_nonce_data.durable_nonce != next_durable_nonce;
662
663                        let nonce_authority_is_valid = message
664                            .account_keys()
665                            .iter()
666                            .enumerate()
667                            .any(|(i, address)| {
668                                address == &current_nonce_data.authority && message.is_signer(i)
669                            });
670
671                        if nonce_authority_is_valid {
672                            Some(nonce_can_be_advanced)
673                        } else {
674                            None
675                        }
676                    }
677                    _ => None,
678                },
679            );
680
681        match nonce_is_valid {
682            None => {
683                error_counters.blockhash_not_found += 1;
684                Err(TransactionError::BlockhashNotFound)
685            }
686            Some(false) => {
687                error_counters.account_not_found += 1;
688                Err(TransactionError::AccountNotFound)
689            }
690            Some(true) => Ok(()),
691        }
692    }
693
694    /// Returns a map from executable program accounts (all accounts owned by any loader)
695    /// to their usage counters, for the transactions with a valid blockhash or nonce.
696    fn filter_executable_program_accounts<'a, CB: TransactionProcessingCallback>(
697        callbacks: &CB,
698        txs: &[impl SVMMessage],
699        check_results: &[TransactionCheckResult],
700        program_owners: &'a [Pubkey],
701    ) -> HashMap<Pubkey, (&'a Pubkey, u64)> {
702        let mut result: HashMap<Pubkey, (&'a Pubkey, u64)> = HashMap::new();
703        check_results.iter().zip(txs).for_each(|etx| {
704            if let (Ok(_), tx) = etx {
705                tx.account_keys()
706                    .iter()
707                    .for_each(|key| match result.entry(*key) {
708                        Entry::Occupied(mut entry) => {
709                            let (_, count) = entry.get_mut();
710                            *count = count.saturating_add(1);
711                        }
712                        Entry::Vacant(entry) => {
713                            if let Some(index) =
714                                callbacks.account_matches_owners(key, program_owners)
715                            {
716                                if let Some(owner) = program_owners.get(index) {
717                                    entry.insert((owner, 1));
718                                }
719                            }
720                        }
721                    });
722            }
723        });
724        result
725    }
726
727    #[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
728    fn replenish_program_cache<CB: TransactionProcessingCallback>(
729        &self,
730        callback: &CB,
731        program_accounts_map: &HashMap<Pubkey, (&Pubkey, u64)>,
732        execute_timings: &mut ExecuteTimings,
733        check_program_modification_slot: bool,
734        limit_to_load_programs: bool,
735    ) -> ProgramCacheForTxBatch {
736        let mut missing_programs: Vec<(Pubkey, (ProgramCacheMatchCriteria, u64))> =
737            program_accounts_map
738                .iter()
739                .map(|(pubkey, (_, count))| {
740                    let match_criteria = if check_program_modification_slot {
741                        get_program_modification_slot(callback, pubkey)
742                            .map_or(ProgramCacheMatchCriteria::Tombstone, |slot| {
743                                ProgramCacheMatchCriteria::DeployedOnOrAfterSlot(slot)
744                            })
745                    } else {
746                        ProgramCacheMatchCriteria::NoCriteria
747                    };
748                    (*pubkey, (match_criteria, *count))
749                })
750                .collect();
751
752        let mut loaded_programs_for_txs: Option<ProgramCacheForTxBatch> = None;
753        loop {
754            let (program_to_store, task_cookie, task_waiter) = {
755                // Lock the global cache.
756                let program_cache = self.program_cache.read().unwrap();
757                // Initialize our local cache.
758                let is_first_round = loaded_programs_for_txs.is_none();
759                if is_first_round {
760                    loaded_programs_for_txs = Some(ProgramCacheForTxBatch::new_from_cache(
761                        self.slot,
762                        self.epoch,
763                        &program_cache,
764                    ));
765                }
766                // Figure out which program needs to be loaded next.
767                let program_to_load = program_cache.extract(
768                    &mut missing_programs,
769                    loaded_programs_for_txs.as_mut().unwrap(),
770                    is_first_round,
771                );
772
773                let program_to_store = program_to_load.map(|(key, count)| {
774                    // Load, verify and compile one program.
775                    let program = load_program_with_pubkey(
776                        callback,
777                        &program_cache.get_environments_for_epoch(self.epoch),
778                        &key,
779                        self.slot,
780                        execute_timings,
781                        false,
782                    )
783                    .expect("called load_program_with_pubkey() with nonexistent account");
784                    program.tx_usage_counter.store(count, Ordering::Relaxed);
785                    (key, program)
786                });
787
788                let task_waiter = Arc::clone(&program_cache.loading_task_waiter);
789                (program_to_store, task_waiter.cookie(), task_waiter)
790                // Unlock the global cache again.
791            };
792
793            if let Some((key, program)) = program_to_store {
794                loaded_programs_for_txs.as_mut().unwrap().loaded_missing = true;
795                let mut program_cache = self.program_cache.write().unwrap();
796                // Submit our last completed loading task.
797                if program_cache.finish_cooperative_loading_task(self.slot, key, program)
798                    && limit_to_load_programs
799                {
800                    // This branch is taken when there is an error in assigning a program to a
801                    // cache slot. It is not possible to mock this error for SVM unit
802                    // tests purposes.
803                    let mut ret = ProgramCacheForTxBatch::new_from_cache(
804                        self.slot,
805                        self.epoch,
806                        &program_cache,
807                    );
808                    ret.hit_max_limit = true;
809                    return ret;
810                }
811            } else if missing_programs.is_empty() {
812                break;
813            } else {
814                // Sleep until the next finish_cooperative_loading_task() call.
815                // Once a task completes we'll wake up and try to load the
816                // missing programs inside the tx batch again.
817                let _new_cookie = task_waiter.wait(task_cookie);
818            }
819        }
820
821        loaded_programs_for_txs.unwrap()
822    }
823
824    pub fn prepare_program_cache_for_upcoming_feature_set<CB: TransactionProcessingCallback>(
825        &self,
826        callbacks: &CB,
827        upcoming_feature_set: &FeatureSet,
828        compute_budget: &ComputeBudget,
829        slot_index: u64,
830        slots_in_epoch: u64,
831    ) {
832        // Recompile loaded programs one at a time before the next epoch hits
833        let slots_in_recompilation_phase =
834            (solana_program_runtime::loaded_programs::MAX_LOADED_ENTRY_COUNT as u64)
835                .min(slots_in_epoch)
836                .checked_div(2)
837                .unwrap();
838        let mut program_cache = self.program_cache.write().unwrap();
839        if program_cache.upcoming_environments.is_some() {
840            if let Some((key, program_to_recompile)) = program_cache.programs_to_recompile.pop() {
841                let effective_epoch = program_cache.latest_root_epoch.saturating_add(1);
842                drop(program_cache);
843                let environments_for_epoch = self
844                    .program_cache
845                    .read()
846                    .unwrap()
847                    .get_environments_for_epoch(effective_epoch);
848                if let Some(recompiled) = load_program_with_pubkey(
849                    callbacks,
850                    &environments_for_epoch,
851                    &key,
852                    self.slot,
853                    &mut ExecuteTimings::default(),
854                    false,
855                ) {
856                    recompiled.tx_usage_counter.fetch_add(
857                        program_to_recompile
858                            .tx_usage_counter
859                            .load(Ordering::Relaxed),
860                        Ordering::Relaxed,
861                    );
862                    recompiled.ix_usage_counter.fetch_add(
863                        program_to_recompile
864                            .ix_usage_counter
865                            .load(Ordering::Relaxed),
866                        Ordering::Relaxed,
867                    );
868                    let mut program_cache = self.program_cache.write().unwrap();
869                    program_cache.assign_program(key, recompiled);
870                }
871            }
872        } else if self.epoch != program_cache.latest_root_epoch
873            || slot_index.saturating_add(slots_in_recompilation_phase) >= slots_in_epoch
874        {
875            // Anticipate the upcoming program runtime environment for the next epoch,
876            // so we can try to recompile loaded programs before the feature transition hits.
877            drop(program_cache);
878            let mut program_cache = self.program_cache.write().unwrap();
879            let program_runtime_environment_v1 = create_program_runtime_environment_v1(
880                upcoming_feature_set,
881                compute_budget,
882                false, /* deployment */
883                false, /* debugging_features */
884            )
885            .unwrap();
886            let program_runtime_environment_v2 = create_program_runtime_environment_v2(
887                compute_budget,
888                false, /* debugging_features */
889            );
890            let mut upcoming_environments = program_cache.environments.clone();
891            let changed_program_runtime_v1 =
892                *upcoming_environments.program_runtime_v1 != program_runtime_environment_v1;
893            let changed_program_runtime_v2 =
894                *upcoming_environments.program_runtime_v2 != program_runtime_environment_v2;
895            if changed_program_runtime_v1 {
896                upcoming_environments.program_runtime_v1 = Arc::new(program_runtime_environment_v1);
897            }
898            if changed_program_runtime_v2 {
899                upcoming_environments.program_runtime_v2 = Arc::new(program_runtime_environment_v2);
900            }
901            program_cache.upcoming_environments = Some(upcoming_environments);
902            program_cache.programs_to_recompile = program_cache
903                .get_flattened_entries(changed_program_runtime_v1, changed_program_runtime_v2);
904            program_cache
905                .programs_to_recompile
906                .sort_by_cached_key(|(_id, program)| program.decayed_usage_counter(self.slot));
907        }
908    }
909
910    /// Execute a transaction using the provided loaded accounts and update
911    /// the executors cache if the transaction was successful.
912    #[allow(clippy::too_many_arguments)]
913    fn execute_loaded_transaction<CB: TransactionProcessingCallback>(
914        &self,
915        callback: &CB,
916        tx: &impl SVMTransaction,
917        mut loaded_transaction: LoadedTransaction,
918        execute_timings: &mut ExecuteTimings,
919        error_metrics: &mut TransactionErrorMetrics,
920        program_cache_for_tx_batch: &mut ProgramCacheForTxBatch,
921        environment: &TransactionProcessingEnvironment,
922        config: &TransactionProcessingConfig,
923    ) -> ExecutedTransaction {
924        let transaction_accounts = std::mem::take(&mut loaded_transaction.accounts);
925
926        // Ensure the length of accounts matches the expected length from tx.account_keys().
927        // This is a sanity check in case that someone starts adding some additional accounts
928        // since this has been done before. See discussion in PR #4497 for details
929        debug_assert!(transaction_accounts.len() == tx.account_keys().len());
930
931        fn transaction_accounts_lamports_sum(
932            accounts: &[(Pubkey, AccountSharedData)],
933        ) -> Option<u128> {
934            accounts.iter().try_fold(0u128, |sum, (_, account)| {
935                sum.checked_add(u128::from(account.lamports()))
936            })
937        }
938
939        let default_rent_collector = RentCollector::default();
940        let rent_collector = environment
941            .rent_collector
942            .unwrap_or(&default_rent_collector);
943
944        let lamports_before_tx =
945            transaction_accounts_lamports_sum(&transaction_accounts).unwrap_or(0);
946
947        let compute_budget = config
948            .compute_budget
949            .unwrap_or_else(|| ComputeBudget::from(loaded_transaction.compute_budget_limits));
950
951        let mut transaction_context = TransactionContext::new(
952            transaction_accounts,
953            rent_collector.get_rent().clone(),
954            compute_budget.max_instruction_stack_depth,
955            compute_budget.max_instruction_trace_length,
956        );
957        transaction_context.set_remove_accounts_executable_flag_checks(
958            environment
959                .feature_set
960                .is_active(&remove_accounts_executable_flag_checks::id()),
961        );
962        #[cfg(debug_assertions)]
963        transaction_context.set_signature(tx.signature());
964
965        let pre_account_state_info =
966            TransactionAccountStateInfo::new(&transaction_context, tx, rent_collector);
967
968        let log_collector = if config.recording_config.enable_log_recording {
969            match config.log_messages_bytes_limit {
970                None => Some(LogCollector::new_ref()),
971                Some(log_messages_bytes_limit) => Some(LogCollector::new_ref_with_limit(Some(
972                    log_messages_bytes_limit,
973                ))),
974            }
975        } else {
976            None
977        };
978
979        let mut executed_units = 0u64;
980        let sysvar_cache = &self.sysvar_cache.read().unwrap();
981        let epoch_vote_account_stake_callback =
982            |pubkey| callback.get_current_epoch_vote_account_stake(pubkey);
983
984        let mut invoke_context = InvokeContext::new(
985            &mut transaction_context,
986            program_cache_for_tx_batch,
987            EnvironmentConfig::new(
988                environment.blockhash,
989                environment.blockhash_lamports_per_signature,
990                environment.epoch_total_stake,
991                &epoch_vote_account_stake_callback,
992                Arc::clone(&environment.feature_set),
993                sysvar_cache,
994            ),
995            log_collector.clone(),
996            compute_budget,
997        );
998
999        let mut process_message_time = Measure::start("process_message_time");
1000        let process_result = process_message(
1001            tx,
1002            &loaded_transaction.program_indices,
1003            &mut invoke_context,
1004            execute_timings,
1005            &mut executed_units,
1006        );
1007        process_message_time.stop();
1008
1009        drop(invoke_context);
1010
1011        execute_timings.execute_accessories.process_message_us += process_message_time.as_us();
1012
1013        let mut status = process_result
1014            .and_then(|info| {
1015                let post_account_state_info =
1016                    TransactionAccountStateInfo::new(&transaction_context, tx, rent_collector);
1017                TransactionAccountStateInfo::verify_changes(
1018                    &pre_account_state_info,
1019                    &post_account_state_info,
1020                    &transaction_context,
1021                    rent_collector,
1022                )
1023                .map(|_| info)
1024            })
1025            .map_err(|err| {
1026                match err {
1027                    TransactionError::InvalidRentPayingAccount
1028                    | TransactionError::InsufficientFundsForRent { .. } => {
1029                        error_metrics.invalid_rent_paying_account += 1;
1030                    }
1031                    TransactionError::InvalidAccountIndex => {
1032                        error_metrics.invalid_account_index += 1;
1033                    }
1034                    _ => {
1035                        error_metrics.instruction_error += 1;
1036                    }
1037                }
1038                err
1039            });
1040
1041        let log_messages: Option<TransactionLogMessages> =
1042            log_collector.and_then(|log_collector| {
1043                Rc::try_unwrap(log_collector)
1044                    .map(|log_collector| log_collector.into_inner().into_messages())
1045                    .ok()
1046            });
1047
1048        let inner_instructions = if config.recording_config.enable_cpi_recording {
1049            Some(Self::inner_instructions_list_from_instruction_trace(
1050                &transaction_context,
1051            ))
1052        } else {
1053            None
1054        };
1055
1056        let ExecutionRecord {
1057            accounts,
1058            return_data,
1059            touched_account_count,
1060            accounts_resize_delta: accounts_data_len_delta,
1061        } = transaction_context.into();
1062
1063        if status.is_ok()
1064            && transaction_accounts_lamports_sum(&accounts)
1065                .filter(|lamports_after_tx| lamports_before_tx == *lamports_after_tx)
1066                .is_none()
1067        {
1068            status = Err(TransactionError::UnbalancedTransaction);
1069        }
1070        let status = status.map(|_| ());
1071
1072        loaded_transaction.accounts = accounts;
1073        execute_timings.details.total_account_count += loaded_transaction.accounts.len() as u64;
1074        execute_timings.details.changed_account_count += touched_account_count;
1075
1076        let return_data = if config.recording_config.enable_return_data_recording
1077            && !return_data.data.is_empty()
1078        {
1079            Some(return_data)
1080        } else {
1081            None
1082        };
1083
1084        ExecutedTransaction {
1085            execution_details: TransactionExecutionDetails {
1086                status,
1087                log_messages,
1088                inner_instructions,
1089                return_data,
1090                executed_units,
1091                accounts_data_len_delta,
1092            },
1093            loaded_transaction,
1094            programs_modified_by_tx: program_cache_for_tx_batch.drain_modified_entries(),
1095        }
1096    }
1097
1098    /// Extract the InnerInstructionsList from a TransactionContext
1099    fn inner_instructions_list_from_instruction_trace(
1100        transaction_context: &TransactionContext,
1101    ) -> InnerInstructionsList {
1102        debug_assert!(transaction_context
1103            .get_instruction_context_at_index_in_trace(0)
1104            .map(|instruction_context| instruction_context.get_stack_height()
1105                == TRANSACTION_LEVEL_STACK_HEIGHT)
1106            .unwrap_or(true));
1107        let mut outer_instructions = Vec::new();
1108        for index_in_trace in 0..transaction_context.get_instruction_trace_length() {
1109            if let Ok(instruction_context) =
1110                transaction_context.get_instruction_context_at_index_in_trace(index_in_trace)
1111            {
1112                let stack_height = instruction_context.get_stack_height();
1113                if stack_height == TRANSACTION_LEVEL_STACK_HEIGHT {
1114                    outer_instructions.push(Vec::new());
1115                } else if let Some(inner_instructions) = outer_instructions.last_mut() {
1116                    let stack_height = u8::try_from(stack_height).unwrap_or(u8::MAX);
1117                    let instruction = CompiledInstruction::new_from_raw_parts(
1118                        instruction_context
1119                            .get_index_of_program_account_in_transaction(
1120                                instruction_context
1121                                    .get_number_of_program_accounts()
1122                                    .saturating_sub(1),
1123                            )
1124                            .unwrap_or_default() as u8,
1125                        instruction_context.get_instruction_data().to_vec(),
1126                        (0..instruction_context.get_number_of_instruction_accounts())
1127                            .map(|instruction_account_index| {
1128                                instruction_context
1129                                    .get_index_of_instruction_account_in_transaction(
1130                                        instruction_account_index,
1131                                    )
1132                                    .unwrap_or_default() as u8
1133                            })
1134                            .collect(),
1135                    );
1136                    inner_instructions.push(InnerInstruction {
1137                        instruction,
1138                        stack_height,
1139                    });
1140                } else {
1141                    debug_assert!(false);
1142                }
1143            } else {
1144                debug_assert!(false);
1145            }
1146        }
1147        outer_instructions
1148    }
1149
1150    pub fn fill_missing_sysvar_cache_entries<CB: TransactionProcessingCallback>(
1151        &self,
1152        callbacks: &CB,
1153    ) {
1154        let mut sysvar_cache = self.sysvar_cache.write().unwrap();
1155        sysvar_cache.fill_missing_entries(|pubkey, set_sysvar| {
1156            if let Some(account) = callbacks.get_account_shared_data(pubkey) {
1157                set_sysvar(account.data());
1158            }
1159        });
1160    }
1161
1162    pub fn reset_sysvar_cache(&self) {
1163        let mut sysvar_cache = self.sysvar_cache.write().unwrap();
1164        sysvar_cache.reset();
1165    }
1166
1167    pub fn get_sysvar_cache_for_tests(&self) -> SysvarCache {
1168        self.sysvar_cache.read().unwrap().clone()
1169    }
1170
1171    /// Add a built-in program
1172    pub fn add_builtin<CB: TransactionProcessingCallback>(
1173        &self,
1174        callbacks: &CB,
1175        program_id: Pubkey,
1176        name: &str,
1177        builtin: ProgramCacheEntry,
1178    ) {
1179        debug!("Adding program {} under {:?}", name, program_id);
1180        callbacks.add_builtin_account(name, &program_id);
1181        self.builtin_program_ids.write().unwrap().insert(program_id);
1182        self.program_cache
1183            .write()
1184            .unwrap()
1185            .assign_program(program_id, Arc::new(builtin));
1186        debug!("Added program {} under {:?}", name, program_id);
1187    }
1188
1189    #[cfg(feature = "dev-context-only-utils")]
1190    #[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
1191    fn writable_sysvar_cache(&self) -> &RwLock<SysvarCache> {
1192        &self.sysvar_cache
1193    }
1194}
1195
1196#[cfg(test)]
1197mod tests {
1198    #[allow(deprecated)]
1199    use solana_sysvar::fees::Fees;
1200    use {
1201        super::*,
1202        crate::{
1203            account_loader::{LoadedTransactionAccount, ValidatedTransactionDetails},
1204            nonce_info::NonceInfo,
1205            rollback_accounts::RollbackAccounts,
1206            transaction_processing_callback::AccountState,
1207        },
1208        solana_account::{create_account_shared_data_for_test, WritableAccount},
1209        solana_clock::Clock,
1210        solana_compute_budget::compute_budget_limits::ComputeBudgetLimits,
1211        solana_compute_budget_interface::ComputeBudgetInstruction,
1212        solana_epoch_schedule::EpochSchedule,
1213        solana_feature_set::FeatureSet,
1214        solana_fee_calculator::FeeCalculator,
1215        solana_fee_structure::{FeeDetails, FeeStructure},
1216        solana_hash::Hash,
1217        solana_keypair::Keypair,
1218        solana_message::{LegacyMessage, Message, MessageHeader, SanitizedMessage},
1219        solana_nonce as nonce,
1220        solana_program_runtime::loaded_programs::{BlockRelation, ProgramCacheEntryType},
1221        solana_rent::Rent,
1222        solana_rent_debits::RentDebits,
1223        solana_reserved_account_keys::ReservedAccountKeys,
1224        solana_sdk::rent_collector::{RentCollector, RENT_EXEMPT_RENT_EPOCH},
1225        solana_sdk_ids::{bpf_loader, system_program, sysvar},
1226        solana_signature::Signature,
1227        solana_transaction::{sanitized::SanitizedTransaction, Transaction},
1228        solana_transaction_context::TransactionContext,
1229        solana_transaction_error::TransactionError,
1230        test_case::test_case,
1231    };
1232
1233    fn new_unchecked_sanitized_message(message: Message) -> SanitizedMessage {
1234        SanitizedMessage::Legacy(LegacyMessage::new(
1235            message,
1236            &ReservedAccountKeys::empty_key_set(),
1237        ))
1238    }
1239
1240    struct TestForkGraph {}
1241
1242    impl ForkGraph for TestForkGraph {
1243        fn relationship(&self, _a: Slot, _b: Slot) -> BlockRelation {
1244            BlockRelation::Unknown
1245        }
1246    }
1247
1248    #[derive(Default, Clone)]
1249    struct MockBankCallback {
1250        account_shared_data: Arc<RwLock<HashMap<Pubkey, AccountSharedData>>>,
1251        #[allow(clippy::type_complexity)]
1252        inspected_accounts:
1253            Arc<RwLock<HashMap<Pubkey, Vec<(Option<AccountSharedData>, /* is_writable */ bool)>>>>,
1254    }
1255
1256    impl TransactionProcessingCallback for MockBankCallback {
1257        fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option<usize> {
1258            if let Some(data) = self.account_shared_data.read().unwrap().get(account) {
1259                if data.lamports() == 0 {
1260                    None
1261                } else {
1262                    owners.iter().position(|entry| data.owner() == entry)
1263                }
1264            } else {
1265                None
1266            }
1267        }
1268
1269        fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option<AccountSharedData> {
1270            self.account_shared_data
1271                .read()
1272                .unwrap()
1273                .get(pubkey)
1274                .cloned()
1275        }
1276
1277        fn add_builtin_account(&self, name: &str, program_id: &Pubkey) {
1278            let mut account_data = AccountSharedData::default();
1279            account_data.set_data(name.as_bytes().to_vec());
1280            self.account_shared_data
1281                .write()
1282                .unwrap()
1283                .insert(*program_id, account_data);
1284        }
1285
1286        fn inspect_account(
1287            &self,
1288            address: &Pubkey,
1289            account_state: AccountState,
1290            is_writable: bool,
1291        ) {
1292            let account = match account_state {
1293                AccountState::Dead => None,
1294                AccountState::Alive(account) => Some(account.clone()),
1295            };
1296            self.inspected_accounts
1297                .write()
1298                .unwrap()
1299                .entry(*address)
1300                .or_default()
1301                .push((account, is_writable));
1302        }
1303
1304        fn calculate_fee(
1305            &self,
1306            message: &impl SVMMessage,
1307            lamports_per_signature: u64,
1308            prioritization_fee: u64,
1309            _feature_set: &FeatureSet,
1310        ) -> FeeDetails {
1311            let signature_count = message
1312                .num_transaction_signatures()
1313                .saturating_add(message.num_ed25519_signatures())
1314                .saturating_add(message.num_secp256k1_signatures())
1315                .saturating_add(message.num_secp256r1_signatures());
1316
1317            FeeDetails::new(
1318                signature_count.saturating_mul(lamports_per_signature),
1319                prioritization_fee,
1320            )
1321        }
1322    }
1323
1324    impl<'a> From<&'a MockBankCallback> for AccountLoader<'a, MockBankCallback> {
1325        fn from(callbacks: &'a MockBankCallback) -> AccountLoader<'a, MockBankCallback> {
1326            AccountLoader::new_with_account_cache_capacity(
1327                None,
1328                callbacks,
1329                Arc::<FeatureSet>::default(),
1330                0,
1331            )
1332        }
1333    }
1334
1335    #[test_case(1; "Check results too small")]
1336    #[test_case(3; "Check results too large")]
1337    #[should_panic(expected = "Length of check_results does not match length of sanitized_txs")]
1338    fn test_check_results_txs_length_mismatch(check_results_len: usize) {
1339        let sanitized_message = new_unchecked_sanitized_message(Message {
1340            account_keys: vec![Pubkey::new_from_array([0; 32])],
1341            header: MessageHeader::default(),
1342            instructions: vec![CompiledInstruction {
1343                program_id_index: 0,
1344                accounts: vec![],
1345                data: vec![],
1346            }],
1347            recent_blockhash: Hash::default(),
1348        });
1349
1350        // Transactions, length 2.
1351        let sanitized_txs = vec![
1352            SanitizedTransaction::new_for_tests(
1353                sanitized_message,
1354                vec![Signature::new_unique()],
1355                false,
1356            );
1357            2
1358        ];
1359
1360        let check_results = vec![
1361            TransactionCheckResult::Ok(CheckedTransactionDetails::default());
1362            check_results_len
1363        ];
1364
1365        let batch_processor = TransactionBatchProcessor::<TestForkGraph>::default();
1366        let callback = MockBankCallback::default();
1367
1368        batch_processor.load_and_execute_sanitized_transactions(
1369            &callback,
1370            &sanitized_txs,
1371            check_results,
1372            &TransactionProcessingEnvironment::default(),
1373            &TransactionProcessingConfig::default(),
1374        );
1375    }
1376
1377    #[test]
1378    fn test_inner_instructions_list_from_instruction_trace() {
1379        let instruction_trace = [1, 2, 1, 1, 2, 3, 2];
1380        let mut transaction_context =
1381            TransactionContext::new(vec![], Rent::default(), 3, instruction_trace.len());
1382        for (index_in_trace, stack_height) in instruction_trace.into_iter().enumerate() {
1383            while stack_height <= transaction_context.get_instruction_context_stack_height() {
1384                transaction_context.pop().unwrap();
1385            }
1386            if stack_height > transaction_context.get_instruction_context_stack_height() {
1387                transaction_context
1388                    .get_next_instruction_context()
1389                    .unwrap()
1390                    .configure(&[], &[], &[index_in_trace as u8]);
1391                transaction_context.push().unwrap();
1392            }
1393        }
1394        let inner_instructions =
1395            TransactionBatchProcessor::<TestForkGraph>::inner_instructions_list_from_instruction_trace(
1396                &transaction_context,
1397            );
1398
1399        assert_eq!(
1400            inner_instructions,
1401            vec![
1402                vec![InnerInstruction {
1403                    instruction: CompiledInstruction::new_from_raw_parts(0, vec![1], vec![]),
1404                    stack_height: 2,
1405                }],
1406                vec![],
1407                vec![
1408                    InnerInstruction {
1409                        instruction: CompiledInstruction::new_from_raw_parts(0, vec![4], vec![]),
1410                        stack_height: 2,
1411                    },
1412                    InnerInstruction {
1413                        instruction: CompiledInstruction::new_from_raw_parts(0, vec![5], vec![]),
1414                        stack_height: 3,
1415                    },
1416                    InnerInstruction {
1417                        instruction: CompiledInstruction::new_from_raw_parts(0, vec![6], vec![]),
1418                        stack_height: 2,
1419                    },
1420                ]
1421            ]
1422        );
1423    }
1424
1425    #[test]
1426    fn test_execute_loaded_transaction_recordings() {
1427        // Setting all the arguments correctly is too burdensome for testing
1428        // execute_loaded_transaction separately.This function will be tested in an integration
1429        // test with load_and_execute_sanitized_transactions
1430        let message = Message {
1431            account_keys: vec![Pubkey::new_from_array([0; 32])],
1432            header: MessageHeader::default(),
1433            instructions: vec![CompiledInstruction {
1434                program_id_index: 0,
1435                accounts: vec![],
1436                data: vec![],
1437            }],
1438            recent_blockhash: Hash::default(),
1439        };
1440
1441        let sanitized_message = new_unchecked_sanitized_message(message);
1442        let mut program_cache_for_tx_batch = ProgramCacheForTxBatch::default();
1443        let batch_processor = TransactionBatchProcessor::<TestForkGraph>::default();
1444
1445        let sanitized_transaction = SanitizedTransaction::new_for_tests(
1446            sanitized_message,
1447            vec![Signature::new_unique()],
1448            false,
1449        );
1450
1451        let loaded_transaction = LoadedTransaction {
1452            accounts: vec![(Pubkey::new_unique(), AccountSharedData::default())],
1453            program_indices: vec![vec![0]],
1454            fee_details: FeeDetails::default(),
1455            rollback_accounts: RollbackAccounts::default(),
1456            compute_budget_limits: ComputeBudgetLimits::default(),
1457            rent: 0,
1458            rent_debits: RentDebits::default(),
1459            loaded_accounts_data_size: 32,
1460        };
1461
1462        let processing_environment = TransactionProcessingEnvironment::default();
1463
1464        let mut processing_config = TransactionProcessingConfig::default();
1465        processing_config.recording_config.enable_log_recording = true;
1466
1467        let mock_bank = MockBankCallback::default();
1468
1469        let executed_tx = batch_processor.execute_loaded_transaction(
1470            &mock_bank,
1471            &sanitized_transaction,
1472            loaded_transaction.clone(),
1473            &mut ExecuteTimings::default(),
1474            &mut TransactionErrorMetrics::default(),
1475            &mut program_cache_for_tx_batch,
1476            &processing_environment,
1477            &processing_config,
1478        );
1479        assert!(executed_tx.execution_details.log_messages.is_some());
1480
1481        processing_config.log_messages_bytes_limit = Some(2);
1482
1483        let executed_tx = batch_processor.execute_loaded_transaction(
1484            &mock_bank,
1485            &sanitized_transaction,
1486            loaded_transaction.clone(),
1487            &mut ExecuteTimings::default(),
1488            &mut TransactionErrorMetrics::default(),
1489            &mut program_cache_for_tx_batch,
1490            &processing_environment,
1491            &processing_config,
1492        );
1493        assert!(executed_tx.execution_details.log_messages.is_some());
1494        assert!(executed_tx.execution_details.inner_instructions.is_none());
1495
1496        processing_config.recording_config.enable_log_recording = false;
1497        processing_config.recording_config.enable_cpi_recording = true;
1498        processing_config.log_messages_bytes_limit = None;
1499
1500        let executed_tx = batch_processor.execute_loaded_transaction(
1501            &mock_bank,
1502            &sanitized_transaction,
1503            loaded_transaction,
1504            &mut ExecuteTimings::default(),
1505            &mut TransactionErrorMetrics::default(),
1506            &mut program_cache_for_tx_batch,
1507            &processing_environment,
1508            &processing_config,
1509        );
1510
1511        assert!(executed_tx.execution_details.log_messages.is_none());
1512        assert!(executed_tx.execution_details.inner_instructions.is_some());
1513    }
1514
1515    #[test]
1516    fn test_execute_loaded_transaction_error_metrics() {
1517        // Setting all the arguments correctly is too burdensome for testing
1518        // execute_loaded_transaction separately.This function will be tested in an integration
1519        // test with load_and_execute_sanitized_transactions
1520        let key1 = Pubkey::new_unique();
1521        let key2 = Pubkey::new_unique();
1522        let message = Message {
1523            account_keys: vec![key1, key2],
1524            header: MessageHeader::default(),
1525            instructions: vec![CompiledInstruction {
1526                program_id_index: 0,
1527                accounts: vec![2],
1528                data: vec![],
1529            }],
1530            recent_blockhash: Hash::default(),
1531        };
1532
1533        let sanitized_message = new_unchecked_sanitized_message(message);
1534        let mut program_cache_for_tx_batch = ProgramCacheForTxBatch::default();
1535        let batch_processor = TransactionBatchProcessor::<TestForkGraph>::default();
1536
1537        let sanitized_transaction = SanitizedTransaction::new_for_tests(
1538            sanitized_message,
1539            vec![Signature::new_unique()],
1540            false,
1541        );
1542
1543        let mut account_data = AccountSharedData::default();
1544        account_data.set_owner(bpf_loader::id());
1545        let loaded_transaction = LoadedTransaction {
1546            accounts: vec![
1547                (key1, AccountSharedData::default()),
1548                (key2, AccountSharedData::default()),
1549            ],
1550            program_indices: vec![vec![0]],
1551            fee_details: FeeDetails::default(),
1552            rollback_accounts: RollbackAccounts::default(),
1553            compute_budget_limits: ComputeBudgetLimits::default(),
1554            rent: 0,
1555            rent_debits: RentDebits::default(),
1556            loaded_accounts_data_size: 0,
1557        };
1558
1559        let processing_config = TransactionProcessingConfig {
1560            recording_config: ExecutionRecordingConfig::new_single_setting(false),
1561            ..Default::default()
1562        };
1563        let mut error_metrics = TransactionErrorMetrics::new();
1564        let mock_bank = MockBankCallback::default();
1565
1566        let _ = batch_processor.execute_loaded_transaction(
1567            &mock_bank,
1568            &sanitized_transaction,
1569            loaded_transaction,
1570            &mut ExecuteTimings::default(),
1571            &mut error_metrics,
1572            &mut program_cache_for_tx_batch,
1573            &TransactionProcessingEnvironment::default(),
1574            &processing_config,
1575        );
1576
1577        assert_eq!(error_metrics.instruction_error.0, 1);
1578    }
1579
1580    #[test]
1581    #[should_panic = "called load_program_with_pubkey() with nonexistent account"]
1582    fn test_replenish_program_cache_with_nonexistent_accounts() {
1583        let mock_bank = MockBankCallback::default();
1584        let fork_graph = Arc::new(RwLock::new(TestForkGraph {}));
1585        let batch_processor =
1586            TransactionBatchProcessor::new(0, 0, Arc::downgrade(&fork_graph), None, None);
1587        let key = Pubkey::new_unique();
1588        let owner = Pubkey::new_unique();
1589
1590        let mut account_maps: HashMap<Pubkey, (&Pubkey, u64)> = HashMap::new();
1591        account_maps.insert(key, (&owner, 4));
1592
1593        batch_processor.replenish_program_cache(
1594            &mock_bank,
1595            &account_maps,
1596            &mut ExecuteTimings::default(),
1597            false,
1598            true,
1599        );
1600    }
1601
1602    #[test]
1603    fn test_replenish_program_cache() {
1604        let mock_bank = MockBankCallback::default();
1605        let fork_graph = Arc::new(RwLock::new(TestForkGraph {}));
1606        let batch_processor =
1607            TransactionBatchProcessor::new(0, 0, Arc::downgrade(&fork_graph), None, None);
1608        let key = Pubkey::new_unique();
1609        let owner = Pubkey::new_unique();
1610
1611        let mut account_data = AccountSharedData::default();
1612        account_data.set_owner(bpf_loader::id());
1613        mock_bank
1614            .account_shared_data
1615            .write()
1616            .unwrap()
1617            .insert(key, account_data);
1618
1619        let mut account_maps: HashMap<Pubkey, (&Pubkey, u64)> = HashMap::new();
1620        account_maps.insert(key, (&owner, 4));
1621        let mut loaded_missing = 0;
1622
1623        for limit_to_load_programs in [false, true] {
1624            let result = batch_processor.replenish_program_cache(
1625                &mock_bank,
1626                &account_maps,
1627                &mut ExecuteTimings::default(),
1628                false,
1629                limit_to_load_programs,
1630            );
1631            assert!(!result.hit_max_limit);
1632            if result.loaded_missing {
1633                loaded_missing += 1;
1634            }
1635
1636            let program = result.find(&key).unwrap();
1637            assert!(matches!(
1638                program.program,
1639                ProgramCacheEntryType::FailedVerification(_)
1640            ));
1641        }
1642        assert!(loaded_missing > 0);
1643    }
1644
1645    #[test]
1646    fn test_filter_executable_program_accounts() {
1647        let mock_bank = MockBankCallback::default();
1648        let key1 = Pubkey::new_unique();
1649        let owner1 = Pubkey::new_unique();
1650
1651        let mut data = AccountSharedData::default();
1652        data.set_owner(owner1);
1653        data.set_lamports(93);
1654        mock_bank
1655            .account_shared_data
1656            .write()
1657            .unwrap()
1658            .insert(key1, data);
1659
1660        let message = Message {
1661            account_keys: vec![key1],
1662            header: MessageHeader::default(),
1663            instructions: vec![CompiledInstruction {
1664                program_id_index: 0,
1665                accounts: vec![],
1666                data: vec![],
1667            }],
1668            recent_blockhash: Hash::default(),
1669        };
1670
1671        let sanitized_message = new_unchecked_sanitized_message(message);
1672
1673        let sanitized_transaction_1 = SanitizedTransaction::new_for_tests(
1674            sanitized_message,
1675            vec![Signature::new_unique()],
1676            false,
1677        );
1678
1679        let key2 = Pubkey::new_unique();
1680        let owner2 = Pubkey::new_unique();
1681
1682        let mut account_data = AccountSharedData::default();
1683        account_data.set_owner(owner2);
1684        account_data.set_lamports(90);
1685        mock_bank
1686            .account_shared_data
1687            .write()
1688            .unwrap()
1689            .insert(key2, account_data);
1690
1691        let message = Message {
1692            account_keys: vec![key1, key2],
1693            header: MessageHeader::default(),
1694            instructions: vec![CompiledInstruction {
1695                program_id_index: 0,
1696                accounts: vec![],
1697                data: vec![],
1698            }],
1699            recent_blockhash: Hash::default(),
1700        };
1701
1702        let sanitized_message = new_unchecked_sanitized_message(message);
1703
1704        let sanitized_transaction_2 = SanitizedTransaction::new_for_tests(
1705            sanitized_message,
1706            vec![Signature::new_unique()],
1707            false,
1708        );
1709
1710        let transactions = vec![
1711            sanitized_transaction_1.clone(),
1712            sanitized_transaction_2.clone(),
1713            sanitized_transaction_1,
1714        ];
1715        let check_results = vec![
1716            Ok(CheckedTransactionDetails::default()),
1717            Ok(CheckedTransactionDetails::default()),
1718            Err(TransactionError::ProgramAccountNotFound),
1719        ];
1720        let owners = vec![owner1, owner2];
1721
1722        let result = TransactionBatchProcessor::<TestForkGraph>::filter_executable_program_accounts(
1723            &mock_bank,
1724            &transactions,
1725            &check_results,
1726            &owners,
1727        );
1728
1729        assert_eq!(result.len(), 2);
1730        assert_eq!(result[&key1], (&owner1, 2));
1731        assert_eq!(result[&key2], (&owner2, 1));
1732    }
1733
1734    #[test]
1735    fn test_filter_executable_program_accounts_no_errors() {
1736        let keypair1 = Keypair::new();
1737        let keypair2 = Keypair::new();
1738
1739        let non_program_pubkey1 = Pubkey::new_unique();
1740        let non_program_pubkey2 = Pubkey::new_unique();
1741        let program1_pubkey = Pubkey::new_unique();
1742        let program2_pubkey = Pubkey::new_unique();
1743        let account1_pubkey = Pubkey::new_unique();
1744        let account2_pubkey = Pubkey::new_unique();
1745        let account3_pubkey = Pubkey::new_unique();
1746        let account4_pubkey = Pubkey::new_unique();
1747
1748        let account5_pubkey = Pubkey::new_unique();
1749
1750        let bank = MockBankCallback::default();
1751        bank.account_shared_data.write().unwrap().insert(
1752            non_program_pubkey1,
1753            AccountSharedData::new(1, 10, &account5_pubkey),
1754        );
1755        bank.account_shared_data.write().unwrap().insert(
1756            non_program_pubkey2,
1757            AccountSharedData::new(1, 10, &account5_pubkey),
1758        );
1759        bank.account_shared_data.write().unwrap().insert(
1760            program1_pubkey,
1761            AccountSharedData::new(40, 1, &account5_pubkey),
1762        );
1763        bank.account_shared_data.write().unwrap().insert(
1764            program2_pubkey,
1765            AccountSharedData::new(40, 1, &account5_pubkey),
1766        );
1767        bank.account_shared_data.write().unwrap().insert(
1768            account1_pubkey,
1769            AccountSharedData::new(1, 10, &non_program_pubkey1),
1770        );
1771        bank.account_shared_data.write().unwrap().insert(
1772            account2_pubkey,
1773            AccountSharedData::new(1, 10, &non_program_pubkey2),
1774        );
1775        bank.account_shared_data.write().unwrap().insert(
1776            account3_pubkey,
1777            AccountSharedData::new(40, 1, &program1_pubkey),
1778        );
1779        bank.account_shared_data.write().unwrap().insert(
1780            account4_pubkey,
1781            AccountSharedData::new(40, 1, &program2_pubkey),
1782        );
1783
1784        let tx1 = Transaction::new_with_compiled_instructions(
1785            &[&keypair1],
1786            &[non_program_pubkey1],
1787            Hash::new_unique(),
1788            vec![account1_pubkey, account2_pubkey, account3_pubkey],
1789            vec![CompiledInstruction::new(1, &(), vec![0])],
1790        );
1791        let sanitized_tx1 = SanitizedTransaction::from_transaction_for_tests(tx1);
1792
1793        let tx2 = Transaction::new_with_compiled_instructions(
1794            &[&keypair2],
1795            &[non_program_pubkey2],
1796            Hash::new_unique(),
1797            vec![account4_pubkey, account3_pubkey, account2_pubkey],
1798            vec![CompiledInstruction::new(1, &(), vec![0])],
1799        );
1800        let sanitized_tx2 = SanitizedTransaction::from_transaction_for_tests(tx2);
1801
1802        let owners = &[program1_pubkey, program2_pubkey];
1803        let programs =
1804            TransactionBatchProcessor::<TestForkGraph>::filter_executable_program_accounts(
1805                &bank,
1806                &[sanitized_tx1, sanitized_tx2],
1807                &[
1808                    Ok(CheckedTransactionDetails::default()),
1809                    Ok(CheckedTransactionDetails::default()),
1810                ],
1811                owners,
1812            );
1813
1814        // The result should contain only account3_pubkey, and account4_pubkey as the program accounts
1815        assert_eq!(programs.len(), 2);
1816        assert_eq!(
1817            programs
1818                .get(&account3_pubkey)
1819                .expect("failed to find the program account"),
1820            &(&program1_pubkey, 2)
1821        );
1822        assert_eq!(
1823            programs
1824                .get(&account4_pubkey)
1825                .expect("failed to find the program account"),
1826            &(&program2_pubkey, 1)
1827        );
1828    }
1829
1830    #[test]
1831    fn test_filter_executable_program_accounts_invalid_blockhash() {
1832        let keypair1 = Keypair::new();
1833        let keypair2 = Keypair::new();
1834
1835        let non_program_pubkey1 = Pubkey::new_unique();
1836        let non_program_pubkey2 = Pubkey::new_unique();
1837        let program1_pubkey = Pubkey::new_unique();
1838        let program2_pubkey = Pubkey::new_unique();
1839        let account1_pubkey = Pubkey::new_unique();
1840        let account2_pubkey = Pubkey::new_unique();
1841        let account3_pubkey = Pubkey::new_unique();
1842        let account4_pubkey = Pubkey::new_unique();
1843
1844        let account5_pubkey = Pubkey::new_unique();
1845
1846        let bank = MockBankCallback::default();
1847        bank.account_shared_data.write().unwrap().insert(
1848            non_program_pubkey1,
1849            AccountSharedData::new(1, 10, &account5_pubkey),
1850        );
1851        bank.account_shared_data.write().unwrap().insert(
1852            non_program_pubkey2,
1853            AccountSharedData::new(1, 10, &account5_pubkey),
1854        );
1855        bank.account_shared_data.write().unwrap().insert(
1856            program1_pubkey,
1857            AccountSharedData::new(40, 1, &account5_pubkey),
1858        );
1859        bank.account_shared_data.write().unwrap().insert(
1860            program2_pubkey,
1861            AccountSharedData::new(40, 1, &account5_pubkey),
1862        );
1863        bank.account_shared_data.write().unwrap().insert(
1864            account1_pubkey,
1865            AccountSharedData::new(1, 10, &non_program_pubkey1),
1866        );
1867        bank.account_shared_data.write().unwrap().insert(
1868            account2_pubkey,
1869            AccountSharedData::new(1, 10, &non_program_pubkey2),
1870        );
1871        bank.account_shared_data.write().unwrap().insert(
1872            account3_pubkey,
1873            AccountSharedData::new(40, 1, &program1_pubkey),
1874        );
1875        bank.account_shared_data.write().unwrap().insert(
1876            account4_pubkey,
1877            AccountSharedData::new(40, 1, &program2_pubkey),
1878        );
1879
1880        let tx1 = Transaction::new_with_compiled_instructions(
1881            &[&keypair1],
1882            &[non_program_pubkey1],
1883            Hash::new_unique(),
1884            vec![account1_pubkey, account2_pubkey, account3_pubkey],
1885            vec![CompiledInstruction::new(1, &(), vec![0])],
1886        );
1887        let sanitized_tx1 = SanitizedTransaction::from_transaction_for_tests(tx1);
1888
1889        let tx2 = Transaction::new_with_compiled_instructions(
1890            &[&keypair2],
1891            &[non_program_pubkey2],
1892            Hash::new_unique(),
1893            vec![account4_pubkey, account3_pubkey, account2_pubkey],
1894            vec![CompiledInstruction::new(1, &(), vec![0])],
1895        );
1896        // Let's not register blockhash from tx2. This should cause the tx2 to fail
1897        let sanitized_tx2 = SanitizedTransaction::from_transaction_for_tests(tx2);
1898
1899        let owners = &[program1_pubkey, program2_pubkey];
1900        let check_results = vec![
1901            Ok(CheckedTransactionDetails::default()),
1902            Err(TransactionError::BlockhashNotFound),
1903        ];
1904        let programs =
1905            TransactionBatchProcessor::<TestForkGraph>::filter_executable_program_accounts(
1906                &bank,
1907                &[sanitized_tx1, sanitized_tx2],
1908                &check_results,
1909                owners,
1910            );
1911
1912        // The result should contain only account3_pubkey as the program accounts
1913        assert_eq!(programs.len(), 1);
1914        assert_eq!(
1915            programs
1916                .get(&account3_pubkey)
1917                .expect("failed to find the program account"),
1918            &(&program1_pubkey, 1)
1919        );
1920    }
1921
1922    #[test]
1923    #[allow(deprecated)]
1924    fn test_sysvar_cache_initialization1() {
1925        let mock_bank = MockBankCallback::default();
1926
1927        let clock = Clock {
1928            slot: 1,
1929            epoch_start_timestamp: 2,
1930            epoch: 3,
1931            leader_schedule_epoch: 4,
1932            unix_timestamp: 5,
1933        };
1934        let clock_account = create_account_shared_data_for_test(&clock);
1935        mock_bank
1936            .account_shared_data
1937            .write()
1938            .unwrap()
1939            .insert(sysvar::clock::id(), clock_account);
1940
1941        let epoch_schedule = EpochSchedule::custom(64, 2, true);
1942        let epoch_schedule_account = create_account_shared_data_for_test(&epoch_schedule);
1943        mock_bank
1944            .account_shared_data
1945            .write()
1946            .unwrap()
1947            .insert(sysvar::epoch_schedule::id(), epoch_schedule_account);
1948
1949        let fees = Fees {
1950            fee_calculator: FeeCalculator {
1951                lamports_per_signature: 123,
1952            },
1953        };
1954        let fees_account = create_account_shared_data_for_test(&fees);
1955        mock_bank
1956            .account_shared_data
1957            .write()
1958            .unwrap()
1959            .insert(sysvar::fees::id(), fees_account);
1960
1961        let rent = Rent::with_slots_per_epoch(2048);
1962        let rent_account = create_account_shared_data_for_test(&rent);
1963        mock_bank
1964            .account_shared_data
1965            .write()
1966            .unwrap()
1967            .insert(sysvar::rent::id(), rent_account);
1968
1969        let transaction_processor = TransactionBatchProcessor::<TestForkGraph>::default();
1970        transaction_processor.fill_missing_sysvar_cache_entries(&mock_bank);
1971
1972        let sysvar_cache = transaction_processor.sysvar_cache.read().unwrap();
1973        let cached_clock = sysvar_cache.get_clock();
1974        let cached_epoch_schedule = sysvar_cache.get_epoch_schedule();
1975        let cached_fees = sysvar_cache.get_fees();
1976        let cached_rent = sysvar_cache.get_rent();
1977
1978        assert_eq!(
1979            cached_clock.expect("clock sysvar missing in cache"),
1980            clock.into()
1981        );
1982        assert_eq!(
1983            cached_epoch_schedule.expect("epoch_schedule sysvar missing in cache"),
1984            epoch_schedule.into()
1985        );
1986        assert_eq!(
1987            cached_fees.expect("fees sysvar missing in cache"),
1988            fees.into()
1989        );
1990        assert_eq!(
1991            cached_rent.expect("rent sysvar missing in cache"),
1992            rent.into()
1993        );
1994        assert!(sysvar_cache.get_slot_hashes().is_err());
1995        assert!(sysvar_cache.get_epoch_rewards().is_err());
1996    }
1997
1998    #[test]
1999    #[allow(deprecated)]
2000    fn test_reset_and_fill_sysvar_cache() {
2001        let mock_bank = MockBankCallback::default();
2002
2003        let clock = Clock {
2004            slot: 1,
2005            epoch_start_timestamp: 2,
2006            epoch: 3,
2007            leader_schedule_epoch: 4,
2008            unix_timestamp: 5,
2009        };
2010        let clock_account = create_account_shared_data_for_test(&clock);
2011        mock_bank
2012            .account_shared_data
2013            .write()
2014            .unwrap()
2015            .insert(sysvar::clock::id(), clock_account);
2016
2017        let epoch_schedule = EpochSchedule::custom(64, 2, true);
2018        let epoch_schedule_account = create_account_shared_data_for_test(&epoch_schedule);
2019        mock_bank
2020            .account_shared_data
2021            .write()
2022            .unwrap()
2023            .insert(sysvar::epoch_schedule::id(), epoch_schedule_account);
2024
2025        let fees = Fees {
2026            fee_calculator: FeeCalculator {
2027                lamports_per_signature: 123,
2028            },
2029        };
2030        let fees_account = create_account_shared_data_for_test(&fees);
2031        mock_bank
2032            .account_shared_data
2033            .write()
2034            .unwrap()
2035            .insert(sysvar::fees::id(), fees_account);
2036
2037        let rent = Rent::with_slots_per_epoch(2048);
2038        let rent_account = create_account_shared_data_for_test(&rent);
2039        mock_bank
2040            .account_shared_data
2041            .write()
2042            .unwrap()
2043            .insert(sysvar::rent::id(), rent_account);
2044
2045        let transaction_processor = TransactionBatchProcessor::<TestForkGraph>::default();
2046        // Fill the sysvar cache
2047        transaction_processor.fill_missing_sysvar_cache_entries(&mock_bank);
2048        // Reset the sysvar cache
2049        transaction_processor.reset_sysvar_cache();
2050
2051        {
2052            let sysvar_cache = transaction_processor.sysvar_cache.read().unwrap();
2053            // Test that sysvar cache is empty and none of the values are found
2054            assert!(sysvar_cache.get_clock().is_err());
2055            assert!(sysvar_cache.get_epoch_schedule().is_err());
2056            assert!(sysvar_cache.get_fees().is_err());
2057            assert!(sysvar_cache.get_epoch_rewards().is_err());
2058            assert!(sysvar_cache.get_rent().is_err());
2059            assert!(sysvar_cache.get_epoch_rewards().is_err());
2060        }
2061
2062        // Refill the cache and test the values are available.
2063        transaction_processor.fill_missing_sysvar_cache_entries(&mock_bank);
2064
2065        let sysvar_cache = transaction_processor.sysvar_cache.read().unwrap();
2066        let cached_clock = sysvar_cache.get_clock();
2067        let cached_epoch_schedule = sysvar_cache.get_epoch_schedule();
2068        let cached_fees = sysvar_cache.get_fees();
2069        let cached_rent = sysvar_cache.get_rent();
2070
2071        assert_eq!(
2072            cached_clock.expect("clock sysvar missing in cache"),
2073            clock.into()
2074        );
2075        assert_eq!(
2076            cached_epoch_schedule.expect("epoch_schedule sysvar missing in cache"),
2077            epoch_schedule.into()
2078        );
2079        assert_eq!(
2080            cached_fees.expect("fees sysvar missing in cache"),
2081            fees.into()
2082        );
2083        assert_eq!(
2084            cached_rent.expect("rent sysvar missing in cache"),
2085            rent.into()
2086        );
2087        assert!(sysvar_cache.get_slot_hashes().is_err());
2088        assert!(sysvar_cache.get_epoch_rewards().is_err());
2089    }
2090
2091    #[test]
2092    fn test_add_builtin() {
2093        let mock_bank = MockBankCallback::default();
2094        let fork_graph = Arc::new(RwLock::new(TestForkGraph {}));
2095        let batch_processor =
2096            TransactionBatchProcessor::new(0, 0, Arc::downgrade(&fork_graph), None, None);
2097
2098        let key = Pubkey::new_unique();
2099        let name = "a_builtin_name";
2100        let program = ProgramCacheEntry::new_builtin(
2101            0,
2102            name.len(),
2103            |_invoke_context, _param0, _param1, _param2, _param3, _param4| {},
2104        );
2105
2106        batch_processor.add_builtin(&mock_bank, key, name, program);
2107
2108        assert_eq!(
2109            mock_bank.account_shared_data.read().unwrap()[&key].data(),
2110            name.as_bytes()
2111        );
2112
2113        let mut loaded_programs_for_tx_batch = ProgramCacheForTxBatch::new_from_cache(
2114            0,
2115            0,
2116            &batch_processor.program_cache.read().unwrap(),
2117        );
2118        batch_processor.program_cache.write().unwrap().extract(
2119            &mut vec![(key, (ProgramCacheMatchCriteria::NoCriteria, 1))],
2120            &mut loaded_programs_for_tx_batch,
2121            true,
2122        );
2123        let entry = loaded_programs_for_tx_batch.find(&key).unwrap();
2124
2125        // Repeating code because ProgramCacheEntry does not implement clone.
2126        let program = ProgramCacheEntry::new_builtin(
2127            0,
2128            name.len(),
2129            |_invoke_context, _param0, _param1, _param2, _param3, _param4| {},
2130        );
2131        assert_eq!(entry, Arc::new(program));
2132    }
2133
2134    #[test]
2135    fn test_validate_transaction_fee_payer_exact_balance() {
2136        let lamports_per_signature = 5000;
2137        let message = new_unchecked_sanitized_message(Message::new_with_blockhash(
2138            &[
2139                ComputeBudgetInstruction::set_compute_unit_limit(2000u32),
2140                ComputeBudgetInstruction::set_compute_unit_price(1_000_000_000),
2141            ],
2142            Some(&Pubkey::new_unique()),
2143            &Hash::new_unique(),
2144        ));
2145        let compute_budget_limits = process_compute_budget_instructions(
2146            SVMMessage::program_instructions_iter(&message),
2147            &FeatureSet::default(),
2148        )
2149        .unwrap();
2150        let fee_payer_address = message.fee_payer();
2151        let current_epoch = 42;
2152        let rent_collector = RentCollector {
2153            epoch: current_epoch,
2154            ..RentCollector::default()
2155        };
2156        let min_balance = rent_collector
2157            .rent
2158            .minimum_balance(nonce::state::State::size());
2159        let transaction_fee = lamports_per_signature;
2160        let priority_fee = 2_000_000u64;
2161        let starting_balance = transaction_fee + priority_fee;
2162        assert!(
2163            starting_balance > min_balance,
2164            "we're testing that a rent exempt fee payer can be fully drained, \
2165        so ensure that the starting balance is more than the min balance"
2166        );
2167
2168        let fee_payer_rent_epoch = current_epoch;
2169        let fee_payer_rent_debit = 0;
2170        let fee_payer_account = AccountSharedData::new_rent_epoch(
2171            starting_balance,
2172            0,
2173            &Pubkey::default(),
2174            fee_payer_rent_epoch,
2175        );
2176        let mut mock_accounts = HashMap::new();
2177        mock_accounts.insert(*fee_payer_address, fee_payer_account.clone());
2178        let mock_bank = MockBankCallback {
2179            account_shared_data: Arc::new(RwLock::new(mock_accounts)),
2180            ..Default::default()
2181        };
2182        let mut account_loader = (&mock_bank).into();
2183
2184        let mut error_counters = TransactionErrorMetrics::default();
2185        let result =
2186            TransactionBatchProcessor::<TestForkGraph>::validate_transaction_nonce_and_fee_payer(
2187                &mut account_loader,
2188                &message,
2189                CheckedTransactionDetails::new(None, lamports_per_signature),
2190                &Hash::default(),
2191                FeeStructure::default().lamports_per_signature,
2192                &rent_collector,
2193                &mut error_counters,
2194                &mock_bank,
2195            );
2196
2197        let post_validation_fee_payer_account = {
2198            let mut account = fee_payer_account.clone();
2199            account.set_rent_epoch(RENT_EXEMPT_RENT_EPOCH);
2200            account.set_lamports(0);
2201            account
2202        };
2203
2204        assert_eq!(
2205            result,
2206            Ok(ValidatedTransactionDetails {
2207                rollback_accounts: RollbackAccounts::new(
2208                    None, // nonce
2209                    *fee_payer_address,
2210                    post_validation_fee_payer_account.clone(),
2211                    fee_payer_rent_debit,
2212                    fee_payer_rent_epoch
2213                ),
2214                compute_budget_limits,
2215                fee_details: FeeDetails::new(transaction_fee, priority_fee),
2216                loaded_fee_payer_account: LoadedTransactionAccount {
2217                    loaded_size: fee_payer_account.data().len(),
2218                    account: post_validation_fee_payer_account,
2219                    rent_collected: fee_payer_rent_debit,
2220                },
2221            })
2222        );
2223    }
2224
2225    #[test]
2226    fn test_validate_transaction_fee_payer_rent_paying() {
2227        let lamports_per_signature = 5000;
2228        let message = new_unchecked_sanitized_message(Message::new_with_blockhash(
2229            &[],
2230            Some(&Pubkey::new_unique()),
2231            &Hash::new_unique(),
2232        ));
2233        let compute_budget_limits = process_compute_budget_instructions(
2234            SVMMessage::program_instructions_iter(&message),
2235            &FeatureSet::default(),
2236        )
2237        .unwrap();
2238        let fee_payer_address = message.fee_payer();
2239        let mut rent_collector = RentCollector::default();
2240        rent_collector.rent.lamports_per_byte_year = 1_000_000;
2241        let min_balance = rent_collector.rent.minimum_balance(0);
2242        let transaction_fee = lamports_per_signature;
2243        let starting_balance = min_balance - 1;
2244        let fee_payer_account = AccountSharedData::new(starting_balance, 0, &Pubkey::default());
2245        let fee_payer_rent_debit = rent_collector
2246            .get_rent_due(
2247                fee_payer_account.lamports(),
2248                fee_payer_account.data().len(),
2249                fee_payer_account.rent_epoch(),
2250            )
2251            .lamports();
2252        assert!(fee_payer_rent_debit > 0);
2253
2254        let mut mock_accounts = HashMap::new();
2255        mock_accounts.insert(*fee_payer_address, fee_payer_account.clone());
2256        let mock_bank = MockBankCallback {
2257            account_shared_data: Arc::new(RwLock::new(mock_accounts)),
2258            ..Default::default()
2259        };
2260        let mut account_loader = (&mock_bank).into();
2261
2262        let mut error_counters = TransactionErrorMetrics::default();
2263        let result =
2264            TransactionBatchProcessor::<TestForkGraph>::validate_transaction_nonce_and_fee_payer(
2265                &mut account_loader,
2266                &message,
2267                CheckedTransactionDetails::new(None, lamports_per_signature),
2268                &Hash::default(),
2269                FeeStructure::default().lamports_per_signature,
2270                &rent_collector,
2271                &mut error_counters,
2272                &mock_bank,
2273            );
2274
2275        let post_validation_fee_payer_account = {
2276            let mut account = fee_payer_account.clone();
2277            account.set_rent_epoch(1);
2278            account.set_lamports(starting_balance - transaction_fee - fee_payer_rent_debit);
2279            account
2280        };
2281
2282        assert_eq!(
2283            result,
2284            Ok(ValidatedTransactionDetails {
2285                rollback_accounts: RollbackAccounts::new(
2286                    None, // nonce
2287                    *fee_payer_address,
2288                    post_validation_fee_payer_account.clone(),
2289                    fee_payer_rent_debit,
2290                    0, // rent epoch
2291                ),
2292                compute_budget_limits,
2293                fee_details: FeeDetails::new(transaction_fee, 0),
2294                loaded_fee_payer_account: LoadedTransactionAccount {
2295                    loaded_size: fee_payer_account.data().len(),
2296                    account: post_validation_fee_payer_account,
2297                    rent_collected: fee_payer_rent_debit,
2298                }
2299            })
2300        );
2301    }
2302
2303    #[test]
2304    fn test_validate_transaction_fee_payer_not_found() {
2305        let lamports_per_signature = 5000;
2306        let message =
2307            new_unchecked_sanitized_message(Message::new(&[], Some(&Pubkey::new_unique())));
2308
2309        let mock_bank = MockBankCallback::default();
2310        let mut account_loader = (&mock_bank).into();
2311        let mut error_counters = TransactionErrorMetrics::default();
2312        let result =
2313            TransactionBatchProcessor::<TestForkGraph>::validate_transaction_nonce_and_fee_payer(
2314                &mut account_loader,
2315                &message,
2316                CheckedTransactionDetails::new(None, lamports_per_signature),
2317                &Hash::default(),
2318                FeeStructure::default().lamports_per_signature,
2319                &RentCollector::default(),
2320                &mut error_counters,
2321                &mock_bank,
2322            );
2323
2324        assert_eq!(error_counters.account_not_found.0, 1);
2325        assert_eq!(result, Err(TransactionError::AccountNotFound));
2326    }
2327
2328    #[test]
2329    fn test_validate_transaction_fee_payer_insufficient_funds() {
2330        let lamports_per_signature = 5000;
2331        let message =
2332            new_unchecked_sanitized_message(Message::new(&[], Some(&Pubkey::new_unique())));
2333        let fee_payer_address = message.fee_payer();
2334        let fee_payer_account = AccountSharedData::new(1, 0, &Pubkey::default());
2335        let mut mock_accounts = HashMap::new();
2336        mock_accounts.insert(*fee_payer_address, fee_payer_account.clone());
2337        let mock_bank = MockBankCallback {
2338            account_shared_data: Arc::new(RwLock::new(mock_accounts)),
2339            ..Default::default()
2340        };
2341        let mut account_loader = (&mock_bank).into();
2342
2343        let mut error_counters = TransactionErrorMetrics::default();
2344        let result =
2345            TransactionBatchProcessor::<TestForkGraph>::validate_transaction_nonce_and_fee_payer(
2346                &mut account_loader,
2347                &message,
2348                CheckedTransactionDetails::new(None, lamports_per_signature),
2349                &Hash::default(),
2350                FeeStructure::default().lamports_per_signature,
2351                &RentCollector::default(),
2352                &mut error_counters,
2353                &mock_bank,
2354            );
2355
2356        assert_eq!(error_counters.insufficient_funds.0, 1);
2357        assert_eq!(result, Err(TransactionError::InsufficientFundsForFee));
2358    }
2359
2360    #[test]
2361    fn test_validate_transaction_fee_payer_insufficient_rent() {
2362        let lamports_per_signature = 5000;
2363        let message =
2364            new_unchecked_sanitized_message(Message::new(&[], Some(&Pubkey::new_unique())));
2365        let fee_payer_address = message.fee_payer();
2366        let transaction_fee = lamports_per_signature;
2367        let rent_collector = RentCollector::default();
2368        let min_balance = rent_collector.rent.minimum_balance(0);
2369        let starting_balance = min_balance + transaction_fee - 1;
2370        let fee_payer_account = AccountSharedData::new(starting_balance, 0, &Pubkey::default());
2371        let mut mock_accounts = HashMap::new();
2372        mock_accounts.insert(*fee_payer_address, fee_payer_account.clone());
2373        let mock_bank = MockBankCallback {
2374            account_shared_data: Arc::new(RwLock::new(mock_accounts)),
2375            ..Default::default()
2376        };
2377        let mut account_loader = (&mock_bank).into();
2378
2379        let mut error_counters = TransactionErrorMetrics::default();
2380        let result =
2381            TransactionBatchProcessor::<TestForkGraph>::validate_transaction_nonce_and_fee_payer(
2382                &mut account_loader,
2383                &message,
2384                CheckedTransactionDetails::new(None, lamports_per_signature),
2385                &Hash::default(),
2386                FeeStructure::default().lamports_per_signature,
2387                &rent_collector,
2388                &mut error_counters,
2389                &mock_bank,
2390            );
2391
2392        assert_eq!(
2393            result,
2394            Err(TransactionError::InsufficientFundsForRent { account_index: 0 })
2395        );
2396    }
2397
2398    #[test]
2399    fn test_validate_transaction_fee_payer_invalid() {
2400        let lamports_per_signature = 5000;
2401        let message =
2402            new_unchecked_sanitized_message(Message::new(&[], Some(&Pubkey::new_unique())));
2403        let fee_payer_address = message.fee_payer();
2404        let fee_payer_account = AccountSharedData::new(1_000_000, 0, &Pubkey::new_unique());
2405        let mut mock_accounts = HashMap::new();
2406        mock_accounts.insert(*fee_payer_address, fee_payer_account.clone());
2407        let mock_bank = MockBankCallback {
2408            account_shared_data: Arc::new(RwLock::new(mock_accounts)),
2409            ..Default::default()
2410        };
2411        let mut account_loader = (&mock_bank).into();
2412
2413        let mut error_counters = TransactionErrorMetrics::default();
2414        let result =
2415            TransactionBatchProcessor::<TestForkGraph>::validate_transaction_nonce_and_fee_payer(
2416                &mut account_loader,
2417                &message,
2418                CheckedTransactionDetails::new(None, lamports_per_signature),
2419                &Hash::default(),
2420                FeeStructure::default().lamports_per_signature,
2421                &RentCollector::default(),
2422                &mut error_counters,
2423                &mock_bank,
2424            );
2425
2426        assert_eq!(error_counters.invalid_account_for_fee.0, 1);
2427        assert_eq!(result, Err(TransactionError::InvalidAccountForFee));
2428    }
2429
2430    #[test]
2431    fn test_validate_transaction_fee_payer_invalid_compute_budget() {
2432        let lamports_per_signature = 5000;
2433        let message = new_unchecked_sanitized_message(Message::new(
2434            &[
2435                ComputeBudgetInstruction::set_compute_unit_limit(2000u32),
2436                ComputeBudgetInstruction::set_compute_unit_limit(42u32),
2437            ],
2438            Some(&Pubkey::new_unique()),
2439        ));
2440
2441        let mock_bank = MockBankCallback::default();
2442        let mut account_loader = (&mock_bank).into();
2443        let mut error_counters = TransactionErrorMetrics::default();
2444        let result =
2445            TransactionBatchProcessor::<TestForkGraph>::validate_transaction_nonce_and_fee_payer(
2446                &mut account_loader,
2447                &message,
2448                CheckedTransactionDetails::new(None, lamports_per_signature),
2449                &Hash::default(),
2450                FeeStructure::default().lamports_per_signature,
2451                &RentCollector::default(),
2452                &mut error_counters,
2453                &mock_bank,
2454            );
2455
2456        assert_eq!(error_counters.invalid_compute_budget.0, 1);
2457        assert_eq!(result, Err(TransactionError::DuplicateInstruction(1u8)));
2458    }
2459
2460    #[test]
2461    fn test_validate_transaction_fee_payer_is_nonce() {
2462        let lamports_per_signature = 5000;
2463        let rent_collector = RentCollector::default();
2464        let compute_unit_limit = 2 * solana_compute_budget_program::DEFAULT_COMPUTE_UNITS;
2465        let last_blockhash = Hash::new_unique();
2466        let message = new_unchecked_sanitized_message(Message::new_with_blockhash(
2467            &[
2468                ComputeBudgetInstruction::set_compute_unit_limit(compute_unit_limit as u32),
2469                ComputeBudgetInstruction::set_compute_unit_price(1_000_000),
2470            ],
2471            Some(&Pubkey::new_unique()),
2472            &last_blockhash,
2473        ));
2474        let compute_budget_limits = process_compute_budget_instructions(
2475            SVMMessage::program_instructions_iter(&message),
2476            &FeatureSet::default(),
2477        )
2478        .unwrap();
2479        let fee_payer_address = message.fee_payer();
2480        let min_balance = Rent::default().minimum_balance(nonce::state::State::size());
2481        let transaction_fee = lamports_per_signature;
2482        let priority_fee = compute_unit_limit;
2483
2484        // Sufficient Fees
2485        {
2486            let fee_payer_account = AccountSharedData::new_data(
2487                min_balance + transaction_fee + priority_fee,
2488                &nonce::versions::Versions::new(nonce::state::State::Initialized(
2489                    nonce::state::Data::new(
2490                        *fee_payer_address,
2491                        DurableNonce::default(),
2492                        lamports_per_signature,
2493                    ),
2494                )),
2495                &system_program::id(),
2496            )
2497            .unwrap();
2498
2499            let mut mock_accounts = HashMap::new();
2500            mock_accounts.insert(*fee_payer_address, fee_payer_account.clone());
2501            let mock_bank = MockBankCallback {
2502                account_shared_data: Arc::new(RwLock::new(mock_accounts)),
2503                ..Default::default()
2504            };
2505            let mut account_loader = (&mock_bank).into();
2506
2507            let mut error_counters = TransactionErrorMetrics::default();
2508
2509            let environment_blockhash = Hash::new_unique();
2510            let next_durable_nonce = DurableNonce::from_blockhash(&environment_blockhash);
2511            let mut future_nonce = NonceInfo::new(*fee_payer_address, fee_payer_account.clone());
2512            future_nonce
2513                .try_advance_nonce(next_durable_nonce, lamports_per_signature)
2514                .unwrap();
2515
2516            let tx_details =
2517                CheckedTransactionDetails::new(Some(future_nonce.clone()), lamports_per_signature);
2518
2519            let result = TransactionBatchProcessor::<TestForkGraph>::validate_transaction_nonce_and_fee_payer(
2520                &mut account_loader,
2521                &message,
2522                tx_details,
2523                &environment_blockhash,
2524                FeeStructure::default().lamports_per_signature,
2525                &rent_collector,
2526                &mut error_counters,
2527                &mock_bank,
2528            );
2529
2530            let post_validation_fee_payer_account = {
2531                let mut account = fee_payer_account.clone();
2532                account.set_rent_epoch(RENT_EXEMPT_RENT_EPOCH);
2533                account.set_lamports(min_balance);
2534                account
2535            };
2536
2537            assert_eq!(
2538                result,
2539                Ok(ValidatedTransactionDetails {
2540                    rollback_accounts: RollbackAccounts::new(
2541                        Some(future_nonce),
2542                        *fee_payer_address,
2543                        post_validation_fee_payer_account.clone(),
2544                        0, // fee_payer_rent_debit
2545                        0, // fee_payer_rent_epoch
2546                    ),
2547                    compute_budget_limits,
2548                    fee_details: FeeDetails::new(transaction_fee, priority_fee),
2549                    loaded_fee_payer_account: LoadedTransactionAccount {
2550                        loaded_size: fee_payer_account.data().len(),
2551                        account: post_validation_fee_payer_account,
2552                        rent_collected: 0,
2553                    }
2554                })
2555            );
2556        }
2557
2558        // Insufficient Fees
2559        {
2560            let fee_payer_account = AccountSharedData::new_data(
2561                transaction_fee + priority_fee, // no min_balance this time
2562                &nonce::versions::Versions::new(nonce::state::State::Initialized(
2563                    nonce::state::Data::default(),
2564                )),
2565                &system_program::id(),
2566            )
2567            .unwrap();
2568
2569            let mut mock_accounts = HashMap::new();
2570            mock_accounts.insert(*fee_payer_address, fee_payer_account.clone());
2571            let mock_bank = MockBankCallback {
2572                account_shared_data: Arc::new(RwLock::new(mock_accounts)),
2573                ..Default::default()
2574            };
2575            let mut account_loader = (&mock_bank).into();
2576
2577            let mut error_counters = TransactionErrorMetrics::default();
2578            let result = TransactionBatchProcessor::<TestForkGraph>::validate_transaction_nonce_and_fee_payer(
2579                &mut account_loader,
2580                &message,
2581                CheckedTransactionDetails::new(None, lamports_per_signature),
2582                &Hash::default(),
2583                FeeStructure::default().lamports_per_signature,
2584                &rent_collector,
2585                &mut error_counters,
2586                &mock_bank,
2587            );
2588
2589            assert_eq!(error_counters.insufficient_funds.0, 1);
2590            assert_eq!(result, Err(TransactionError::InsufficientFundsForFee));
2591        }
2592    }
2593
2594    // Ensure `TransactionProcessingCallback::inspect_account()` is called when
2595    // validating the fee payer, since that's when the fee payer account is loaded.
2596    #[test]
2597    fn test_inspect_account_fee_payer() {
2598        let fee_payer_address = Pubkey::new_unique();
2599        let fee_payer_account = AccountSharedData::new_rent_epoch(
2600            123_000_000_000,
2601            0,
2602            &Pubkey::default(),
2603            RENT_EXEMPT_RENT_EPOCH,
2604        );
2605        let mock_bank = MockBankCallback::default();
2606        mock_bank
2607            .account_shared_data
2608            .write()
2609            .unwrap()
2610            .insert(fee_payer_address, fee_payer_account.clone());
2611        let mut account_loader = (&mock_bank).into();
2612
2613        let message = new_unchecked_sanitized_message(Message::new_with_blockhash(
2614            &[
2615                ComputeBudgetInstruction::set_compute_unit_limit(2000u32),
2616                ComputeBudgetInstruction::set_compute_unit_price(1_000_000_000),
2617            ],
2618            Some(&fee_payer_address),
2619            &Hash::new_unique(),
2620        ));
2621        TransactionBatchProcessor::<TestForkGraph>::validate_transaction_nonce_and_fee_payer(
2622            &mut account_loader,
2623            &message,
2624            CheckedTransactionDetails::new(None, 5000),
2625            &Hash::default(),
2626            FeeStructure::default().lamports_per_signature,
2627            &RentCollector::default(),
2628            &mut TransactionErrorMetrics::default(),
2629            &mock_bank,
2630        )
2631        .unwrap();
2632
2633        // ensure the fee payer is an inspected account
2634        let actual_inspected_accounts: Vec<_> = mock_bank
2635            .inspected_accounts
2636            .read()
2637            .unwrap()
2638            .iter()
2639            .map(|(k, v)| (*k, v.clone()))
2640            .collect();
2641        assert_eq!(
2642            actual_inspected_accounts.as_slice(),
2643            &[(fee_payer_address, vec![(Some(fee_payer_account), true)])],
2644        );
2645    }
2646}