solana_svm/
transaction_processor.rs

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