solana_svm/
transaction_processor.rs

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