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