Skip to main content

clone_solana_ledger/
blockstore_processor.rs

1use {
2    crate::{
3        block_error::BlockError,
4        blockstore::{Blockstore, BlockstoreError},
5        blockstore_meta::SlotMeta,
6        entry_notifier_service::{EntryNotification, EntryNotifierSender},
7        leader_schedule_cache::LeaderScheduleCache,
8        token_balances::collect_token_balances,
9        use_snapshot_archives_at_startup::UseSnapshotArchivesAtStartup,
10    },
11    chrono_humanize::{Accuracy, HumanTime, Tense},
12    crossbeam_channel::Sender,
13    itertools::Itertools,
14    log::*,
15    rayon::{prelude::*, ThreadPool},
16    scopeguard::defer,
17    clone_solana_accounts_db::{
18        accounts_db::AccountsDbConfig, accounts_update_notifier_interface::AccountsUpdateNotifier,
19        epoch_accounts_hash::EpochAccountsHash,
20    },
21    clone_solana_cost_model::cost_model::CostModel,
22    clone_solana_entry::entry::{
23        self, create_ticks, Entry, EntrySlice, EntryType, EntryVerificationStatus, VerifyRecyclers,
24    },
25    clone_solana_measure::{measure::Measure, measure_us},
26    clone_solana_metrics::datapoint_error,
27    clone_solana_rayon_threadlimit::{get_max_thread_count, get_thread_count},
28    clone_solana_runtime::{
29        accounts_background_service::{AbsRequestSender, SnapshotRequestKind},
30        bank::{Bank, PreCommitResult, TransactionBalancesSet},
31        bank_forks::{BankForks, SetRootError},
32        bank_utils,
33        commitment::VOTE_THRESHOLD_SIZE,
34        installed_scheduler_pool::BankWithScheduler,
35        prioritization_fee_cache::PrioritizationFeeCache,
36        runtime_config::RuntimeConfig,
37        transaction_batch::{OwnedOrBorrowed, TransactionBatch},
38        vote_sender_types::ReplayVoteSender,
39    },
40    clone_solana_runtime_transaction::{
41        runtime_transaction::RuntimeTransaction, transaction_with_meta::TransactionWithMeta,
42    },
43    clone_solana_sdk::{
44        clock::{Slot, MAX_PROCESSING_AGE},
45        genesis_config::GenesisConfig,
46        hash::Hash,
47        pubkey::Pubkey,
48        signature::{Keypair, Signature},
49        transaction::{
50            Result, SanitizedTransaction, TransactionError, TransactionVerificationMode,
51            VersionedTransaction,
52        },
53    },
54    clone_solana_svm::{
55        transaction_commit_result::{TransactionCommitResult, TransactionCommitResultExtensions},
56        transaction_processing_result::{ProcessedTransaction, TransactionProcessingResult},
57        transaction_processor::ExecutionRecordingConfig,
58    },
59    clone_solana_svm_transaction::{svm_message::SVMMessage, svm_transaction::SVMTransaction},
60    clone_solana_timings::{report_execute_timings, ExecuteTimingType, ExecuteTimings},
61    clone_solana_transaction_status::token_balances::TransactionTokenBalancesSet,
62    clone_solana_vote::vote_account::VoteAccountsHashMap,
63    std::{
64        borrow::Cow,
65        collections::{HashMap, HashSet},
66        num::Saturating,
67        ops::{Index, Range},
68        path::PathBuf,
69        result,
70        sync::{
71            atomic::{AtomicBool, Ordering::Relaxed},
72            Arc, Mutex, RwLock,
73        },
74        time::{Duration, Instant},
75        vec::Drain,
76    },
77    thiserror::Error,
78    ExecuteTimingType::{NumExecuteBatches, TotalBatchesLen},
79};
80#[cfg(feature = "dev-context-only-utils")]
81use {qualifier_attr::qualifiers, clone_solana_runtime::bank::HashOverrides};
82
83pub struct TransactionBatchWithIndexes<'a, 'b, Tx: SVMMessage> {
84    pub batch: TransactionBatch<'a, 'b, Tx>,
85    pub transaction_indexes: Vec<usize>,
86}
87
88// `TransactionBatchWithIndexes` but without the `Drop` that prevents
89// us from nicely unwinding these with manual unlocking.
90pub struct LockedTransactionsWithIndexes<Tx: SVMMessage> {
91    lock_results: Vec<Result<()>>,
92    transactions: Vec<RuntimeTransaction<Tx>>,
93    starting_index: usize,
94}
95
96struct ReplayEntry {
97    entry: EntryType<RuntimeTransaction<SanitizedTransaction>>,
98    starting_index: usize,
99}
100
101fn first_err(results: &[Result<()>]) -> Result<()> {
102    for r in results {
103        if r.is_err() {
104            return r.clone();
105        }
106    }
107    Ok(())
108}
109
110// Includes transaction signature for unit-testing
111fn do_get_first_error<T, Tx: SVMTransaction>(
112    batch: &TransactionBatch<Tx>,
113    results: &[Result<T>],
114) -> Option<(Result<()>, Signature)> {
115    let mut first_err = None;
116    for (result, transaction) in results.iter().zip(batch.sanitized_transactions()) {
117        if let Err(err) = result {
118            if first_err.is_none() {
119                first_err = Some((Err(err.clone()), *transaction.signature()));
120            }
121            warn!(
122                "Unexpected validator error: {:?}, transaction: {:?}",
123                err, transaction
124            );
125            datapoint_error!(
126                "validator_process_entry_error",
127                (
128                    "error",
129                    format!("error: {err:?}, transaction: {transaction:?}"),
130                    String
131                )
132            );
133        }
134    }
135    first_err
136}
137
138fn get_first_error<T, Tx: SVMTransaction>(
139    batch: &TransactionBatch<Tx>,
140    commit_results: &[Result<T>],
141) -> Result<()> {
142    do_get_first_error(batch, commit_results)
143        .map(|(error, _signature)| error)
144        .unwrap_or(Ok(()))
145}
146
147fn create_thread_pool(num_threads: usize) -> ThreadPool {
148    rayon::ThreadPoolBuilder::new()
149        .num_threads(num_threads)
150        .thread_name(|i| format!("solReplayTx{i:02}"))
151        .build()
152        .expect("new rayon threadpool")
153}
154
155pub fn execute_batch<'a>(
156    batch: &'a TransactionBatchWithIndexes<impl TransactionWithMeta>,
157    bank: &'a Arc<Bank>,
158    transaction_status_sender: Option<&'a TransactionStatusSender>,
159    replay_vote_sender: Option<&'a ReplayVoteSender>,
160    timings: &'a mut ExecuteTimings,
161    log_messages_bytes_limit: Option<usize>,
162    prioritization_fee_cache: &'a PrioritizationFeeCache,
163    // None is meaningfully used to detect this is called from the block producing unified
164    // scheduler. If so, suppress too verbose logging for the code path.
165    extra_pre_commit_callback: Option<
166        impl FnOnce(&Result<ProcessedTransaction>) -> Result<Option<usize>>,
167    >,
168) -> Result<()> {
169    let TransactionBatchWithIndexes {
170        batch,
171        transaction_indexes,
172    } = batch;
173    let record_token_balances = transaction_status_sender.is_some();
174    let mut transaction_indexes = Cow::from(transaction_indexes);
175
176    let mut mint_decimals: HashMap<Pubkey, u8> = HashMap::new();
177
178    let pre_token_balances = if record_token_balances {
179        collect_token_balances(bank, batch, &mut mint_decimals)
180    } else {
181        vec![]
182    };
183
184    let pre_commit_callback = |timings: &mut _, processing_results: &_| -> PreCommitResult {
185        match extra_pre_commit_callback {
186            None => {
187                get_first_error(batch, processing_results)?;
188                check_block_cost_limits_if_enabled(batch, bank, timings, processing_results)?;
189                Ok(None)
190            }
191            Some(extra_pre_commit_callback) => {
192                // We're entering into the block-producing unified scheduler special case...
193                // `processing_results` should always contain exactly only 1 result in that case.
194                assert_eq!(processing_results.len(), 1);
195                assert!(transaction_indexes.is_empty());
196
197                // From now on, we need to freeze-lock the tpu bank, in order to prevent it from
198                // freezing in the middle of this code-path. Otherwise, the assertion at the start
199                // of commit_transactions() would trigger panic because it's fatal runtime
200                // invariant violation.
201                let freeze_lock = bank.freeze_lock();
202
203                if let Some(index) = extra_pre_commit_callback(&processing_results[0])? {
204                    let transaction_indexes = transaction_indexes.to_mut();
205                    // Adjust the empty new vec with the exact needed capacity. Otherwise, excess
206                    // cap would be reserved on `.push()` in it.
207                    transaction_indexes.reserve_exact(1);
208                    transaction_indexes.push(index);
209                }
210                // At this point, poh should have been succeeded so it's guaranteed that the bank
211                // hasn't been frozen yet and we're still holding the lock. So, it's okay to pass
212                // down freeze_lock without any introspection here to be unconditionally dropped
213                // after commit_transactions(). This reasoning is same as
214                // clone_solana_core::banking_stage::Consumer::execute_and_commit_transactions_locked()
215                Ok(Some(freeze_lock))
216            }
217        }
218    };
219
220    let (commit_results, balances) = batch
221        .bank()
222        .load_execute_and_commit_transactions_with_pre_commit_callback(
223            batch,
224            MAX_PROCESSING_AGE,
225            transaction_status_sender.is_some(),
226            ExecutionRecordingConfig::new_single_setting(transaction_status_sender.is_some()),
227            timings,
228            log_messages_bytes_limit,
229            pre_commit_callback,
230        )?;
231
232    bank_utils::find_and_send_votes(
233        batch.sanitized_transactions(),
234        &commit_results,
235        replay_vote_sender,
236    );
237
238    let committed_transactions = commit_results
239        .iter()
240        .zip(batch.sanitized_transactions())
241        .filter_map(|(commit_result, tx)| commit_result.was_committed().then_some(tx))
242        .collect_vec();
243
244    if let Some(transaction_status_sender) = transaction_status_sender {
245        let transactions: Vec<SanitizedTransaction> = batch
246            .sanitized_transactions()
247            .iter()
248            .map(|tx| tx.as_sanitized_transaction().into_owned())
249            .collect();
250        let post_token_balances = if record_token_balances {
251            collect_token_balances(bank, batch, &mut mint_decimals)
252        } else {
253            vec![]
254        };
255
256        let token_balances =
257            TransactionTokenBalancesSet::new(pre_token_balances, post_token_balances);
258
259        transaction_status_sender.send_transaction_status_batch(
260            bank.slot(),
261            transactions,
262            commit_results,
263            balances,
264            token_balances,
265            transaction_indexes.into_owned(),
266        );
267    }
268
269    prioritization_fee_cache.update(bank, committed_transactions.into_iter());
270
271    Ok(())
272}
273
274// collect transactions actual execution costs, subject to block limits;
275// block will be marked as dead if exceeds cost limits, details will be
276// reported to metric `replay-stage-mark_dead_slot`
277fn check_block_cost_limits(
278    bank: &Bank,
279    processing_results: &[TransactionProcessingResult],
280    sanitized_transactions: &[impl TransactionWithMeta],
281) -> Result<()> {
282    assert_eq!(sanitized_transactions.len(), processing_results.len());
283
284    let tx_costs_with_actual_execution_units: Vec<_> = processing_results
285        .iter()
286        .zip(sanitized_transactions)
287        .filter_map(|(processing_result, tx)| {
288            if let Ok(processed_tx) = processing_result {
289                Some(CostModel::calculate_cost_for_executed_transaction(
290                    tx,
291                    processed_tx.executed_units(),
292                    processed_tx.loaded_accounts_data_size(),
293                    &bank.feature_set,
294                ))
295            } else {
296                None
297            }
298        })
299        .collect();
300
301    {
302        let mut cost_tracker = bank.write_cost_tracker().unwrap();
303        for tx_cost in &tx_costs_with_actual_execution_units {
304            cost_tracker
305                .try_add(tx_cost)
306                .map_err(TransactionError::from)?;
307        }
308    }
309    Ok(())
310}
311
312fn check_block_cost_limits_if_enabled(
313    batch: &TransactionBatch<impl TransactionWithMeta>,
314    bank: &Bank,
315    timings: &mut ExecuteTimings,
316    processing_results: &[TransactionProcessingResult],
317) -> Result<()> {
318    let (check_block_cost_limits_result, check_block_cost_limits_us) = measure_us!(if bank
319        .feature_set
320        .is_active(&clone_agave_feature_set::apply_cost_tracker_during_replay::id())
321    {
322        check_block_cost_limits(bank, processing_results, batch.sanitized_transactions())
323    } else {
324        Ok(())
325    });
326
327    timings.saturating_add_in_place(
328        ExecuteTimingType::CheckBlockLimitsUs,
329        check_block_cost_limits_us,
330    );
331    check_block_cost_limits_result
332}
333
334#[derive(Default)]
335pub struct ExecuteBatchesInternalMetrics {
336    execution_timings_per_thread: HashMap<usize, ThreadExecuteTimings>,
337    total_batches_len: u64,
338    execute_batches_us: u64,
339}
340
341impl ExecuteBatchesInternalMetrics {
342    pub fn new_with_timings_from_all_threads(execute_timings: ExecuteTimings) -> Self {
343        const DUMMY_THREAD_INDEX: usize = 999;
344        let mut new = Self::default();
345        new.execution_timings_per_thread.insert(
346            DUMMY_THREAD_INDEX,
347            ThreadExecuteTimings {
348                execute_timings,
349                ..ThreadExecuteTimings::default()
350            },
351        );
352        new
353    }
354}
355
356fn execute_batches_internal(
357    bank: &Arc<Bank>,
358    replay_tx_thread_pool: &ThreadPool,
359    batches: &[TransactionBatchWithIndexes<RuntimeTransaction<SanitizedTransaction>>],
360    transaction_status_sender: Option<&TransactionStatusSender>,
361    replay_vote_sender: Option<&ReplayVoteSender>,
362    log_messages_bytes_limit: Option<usize>,
363    prioritization_fee_cache: &PrioritizationFeeCache,
364) -> Result<ExecuteBatchesInternalMetrics> {
365    assert!(!batches.is_empty());
366    let execution_timings_per_thread: Mutex<HashMap<usize, ThreadExecuteTimings>> =
367        Mutex::new(HashMap::new());
368
369    let mut execute_batches_elapsed = Measure::start("execute_batches_elapsed");
370    let results: Vec<Result<()>> = replay_tx_thread_pool.install(|| {
371        batches
372            .into_par_iter()
373            .map(|transaction_batch| {
374                let transaction_count =
375                    transaction_batch.batch.sanitized_transactions().len() as u64;
376                let mut timings = ExecuteTimings::default();
377                let (result, execute_batches_us) = measure_us!(execute_batch(
378                    transaction_batch,
379                    bank,
380                    transaction_status_sender,
381                    replay_vote_sender,
382                    &mut timings,
383                    log_messages_bytes_limit,
384                    prioritization_fee_cache,
385                    None::<fn(&_) -> _>,
386                ));
387
388                let thread_index = replay_tx_thread_pool.current_thread_index().unwrap();
389                execution_timings_per_thread
390                    .lock()
391                    .unwrap()
392                    .entry(thread_index)
393                    .and_modify(|thread_execution_time| {
394                        let ThreadExecuteTimings {
395                            total_thread_us,
396                            total_transactions_executed,
397                            execute_timings: total_thread_execute_timings,
398                        } = thread_execution_time;
399                        *total_thread_us += execute_batches_us;
400                        *total_transactions_executed += transaction_count;
401                        total_thread_execute_timings
402                            .saturating_add_in_place(ExecuteTimingType::TotalBatchesLen, 1);
403                        total_thread_execute_timings.accumulate(&timings);
404                    })
405                    .or_insert(ThreadExecuteTimings {
406                        total_thread_us: Saturating(execute_batches_us),
407                        total_transactions_executed: Saturating(transaction_count),
408                        execute_timings: timings,
409                    });
410                result
411            })
412            .collect()
413    });
414    execute_batches_elapsed.stop();
415
416    first_err(&results)?;
417
418    Ok(ExecuteBatchesInternalMetrics {
419        execution_timings_per_thread: execution_timings_per_thread.into_inner().unwrap(),
420        total_batches_len: batches.len() as u64,
421        execute_batches_us: execute_batches_elapsed.as_us(),
422    })
423}
424
425// This fn diverts the code-path into two variants. Both must provide exactly the same set of
426// validations. For this reason, this fn is deliberately inserted into the code path to be called
427// inside process_entries(), so that Bank::prepare_sanitized_batch() has been called on all of
428// batches already, while minimizing code duplication (thus divergent behavior risk) at the cost of
429// acceptable overhead of meaningless buffering of batches for the scheduler variant.
430//
431// Also note that the scheduler variant can't implement the batch-level sanitization naively, due
432// to the nature of individual tx processing. That's another reason of this particular placement of
433// divergent point in the code-path (i.e. not one layer up with its own prepare_sanitized_batch()
434// invocation).
435fn process_batches(
436    bank: &BankWithScheduler,
437    replay_tx_thread_pool: &ThreadPool,
438    locked_entries: impl ExactSizeIterator<Item = LockedTransactionsWithIndexes<SanitizedTransaction>>,
439    transaction_status_sender: Option<&TransactionStatusSender>,
440    replay_vote_sender: Option<&ReplayVoteSender>,
441    batch_execution_timing: &mut BatchExecutionTiming,
442    log_messages_bytes_limit: Option<usize>,
443    prioritization_fee_cache: &PrioritizationFeeCache,
444) -> Result<()> {
445    if bank.has_installed_scheduler() {
446        debug!(
447            "process_batches()/schedule_batches_for_execution({} batches)",
448            locked_entries.len()
449        );
450        // Scheduling usually succeeds (immediately returns `Ok(())`) here without being blocked on
451        // the actual transaction executions.
452        //
453        // As an exception, this code path could propagate the transaction execution _errors of
454        // previously-scheduled transactions_ to notify the replay stage. Then, the replay stage
455        // will bail out the further processing of the malformed (possibly malicious) block
456        // immediately, not to waste any system resources. Note that this propagation is of early
457        // hints. Even if errors won't be propagated in this way, they are guaranteed to be
458        // propagated eventually via the blocking fn called
459        // BankWithScheduler::wait_for_completed_scheduler().
460        //
461        // To recite, the returned error is completely unrelated to the argument's `locked_entries`
462        // at the hand. While being awkward, the _async_ unified scheduler is abusing this existing
463        // error propagation code path to the replay stage for compatibility and ease of
464        // integration, exploiting the fact that the replay stage doesn't care _which transaction
465        // the returned error is originating from_.
466        //
467        // In the future, more proper error propagation mechanism will be introduced once after we
468        // fully transition to the unified scheduler for the block verification. That one would be
469        // a push based one from the unified scheduler to the replay stage to eliminate the current
470        // overhead: 1 read lock per batch in
471        // `BankWithScheduler::schedule_transaction_executions()`.
472        schedule_batches_for_execution(bank, locked_entries)
473    } else {
474        debug!(
475            "process_batches()/rebatch_and_execute_batches({} batches)",
476            locked_entries.len()
477        );
478        rebatch_and_execute_batches(
479            bank,
480            replay_tx_thread_pool,
481            locked_entries,
482            transaction_status_sender,
483            replay_vote_sender,
484            batch_execution_timing,
485            log_messages_bytes_limit,
486            prioritization_fee_cache,
487        )
488    }
489}
490
491fn schedule_batches_for_execution(
492    bank: &BankWithScheduler,
493    locked_entries: impl Iterator<Item = LockedTransactionsWithIndexes<SanitizedTransaction>>,
494) -> Result<()> {
495    // Track the first error encountered in the loop below, if any.
496    // This error will be propagated to the replay stage, or Ok(()).
497    let mut first_err = Ok(());
498
499    for LockedTransactionsWithIndexes {
500        lock_results,
501        transactions,
502        starting_index,
503    } in locked_entries
504    {
505        // unlock before sending to scheduler.
506        bank.unlock_accounts(transactions.iter().zip(lock_results.iter()));
507        // give ownership to scheduler. capture the first error, but continue the loop
508        // to unlock.
509        // scheduling is skipped if we have already detected an error in this loop
510        let indexes = starting_index..starting_index + transactions.len();
511        first_err = first_err.and_then(|()| {
512            bank.schedule_transaction_executions(transactions.into_iter().zip_eq(indexes))
513        });
514    }
515    first_err
516}
517
518fn rebatch_transactions<'a, Tx: TransactionWithMeta>(
519    lock_results: &'a [Result<()>],
520    bank: &'a Arc<Bank>,
521    sanitized_txs: &'a [Tx],
522    range: Range<usize>,
523    transaction_indexes: &'a [usize],
524) -> TransactionBatchWithIndexes<'a, 'a, Tx> {
525    let txs = &sanitized_txs[range.clone()];
526    let results = &lock_results[range.clone()];
527    let mut tx_batch =
528        TransactionBatch::new(results.to_vec(), bank, OwnedOrBorrowed::Borrowed(txs));
529    tx_batch.set_needs_unlock(true); // unlock on drop for easier clean up
530
531    let transaction_indexes = transaction_indexes[range].to_vec();
532    TransactionBatchWithIndexes {
533        batch: tx_batch,
534        transaction_indexes,
535    }
536}
537
538fn rebatch_and_execute_batches(
539    bank: &Arc<Bank>,
540    replay_tx_thread_pool: &ThreadPool,
541    locked_entries: impl ExactSizeIterator<Item = LockedTransactionsWithIndexes<SanitizedTransaction>>,
542    transaction_status_sender: Option<&TransactionStatusSender>,
543    replay_vote_sender: Option<&ReplayVoteSender>,
544    timing: &mut BatchExecutionTiming,
545    log_messages_bytes_limit: Option<usize>,
546    prioritization_fee_cache: &PrioritizationFeeCache,
547) -> Result<()> {
548    if locked_entries.len() == 0 {
549        return Ok(());
550    }
551
552    // Flatten the locked entries. Store the original entry lengths to avoid rebatching logic
553    // for small entries.
554    let mut original_entry_lengths = Vec::with_capacity(locked_entries.len());
555    let ((lock_results, sanitized_txs), transaction_indexes): ((Vec<_>, Vec<_>), Vec<_>) =
556        locked_entries
557            .flat_map(
558                |LockedTransactionsWithIndexes {
559                     lock_results,
560                     transactions,
561                     starting_index,
562                 }| {
563                    let num_transactions = transactions.len();
564                    original_entry_lengths.push(num_transactions);
565                    lock_results
566                        .into_iter()
567                        .zip_eq(transactions)
568                        .zip_eq(starting_index..starting_index + num_transactions)
569                },
570            )
571            .unzip();
572
573    let mut minimal_tx_cost = u64::MAX;
574    let mut total_cost: u64 = 0;
575    let tx_costs = sanitized_txs
576        .iter()
577        .map(|tx| {
578            let tx_cost = CostModel::calculate_cost(tx, &bank.feature_set);
579            let cost = tx_cost.sum();
580            minimal_tx_cost = std::cmp::min(minimal_tx_cost, cost);
581            total_cost = total_cost.saturating_add(cost);
582            cost
583        })
584        .collect::<Vec<_>>();
585
586    let target_batch_count = get_thread_count() as u64;
587
588    let mut tx_batches = vec![];
589    let rebatched_txs = if total_cost > target_batch_count.saturating_mul(minimal_tx_cost) {
590        let target_batch_cost = total_cost / target_batch_count;
591        let mut batch_cost: u64 = 0;
592        let mut slice_start = 0;
593        tx_costs.into_iter().enumerate().for_each(|(index, cost)| {
594            let next_index = index + 1;
595            batch_cost = batch_cost.saturating_add(cost);
596            if batch_cost >= target_batch_cost || next_index == sanitized_txs.len() {
597                let tx_batch = rebatch_transactions(
598                    &lock_results,
599                    bank,
600                    &sanitized_txs,
601                    slice_start..next_index,
602                    &transaction_indexes,
603                );
604                slice_start = next_index;
605                tx_batches.push(tx_batch);
606                batch_cost = 0;
607            }
608        });
609        &tx_batches[..]
610    } else {
611        let mut slice_start = 0;
612        for num_transactions in original_entry_lengths {
613            let next_index = slice_start + num_transactions;
614            // this is more of a "re-construction" of the original batches than
615            // a rebatching. But the logic is the same, with the transfer of
616            // unlocking responsibility to the batch.
617            let tx_batch = rebatch_transactions(
618                &lock_results,
619                bank,
620                &sanitized_txs,
621                slice_start..next_index,
622                &transaction_indexes,
623            );
624            slice_start = next_index;
625            tx_batches.push(tx_batch);
626        }
627
628        &tx_batches[..]
629    };
630
631    let execute_batches_internal_metrics = execute_batches_internal(
632        bank,
633        replay_tx_thread_pool,
634        rebatched_txs,
635        transaction_status_sender,
636        replay_vote_sender,
637        log_messages_bytes_limit,
638        prioritization_fee_cache,
639    )?;
640
641    // Pass false because this code-path is never touched by unified scheduler.
642    timing.accumulate(execute_batches_internal_metrics, false);
643    Ok(())
644}
645
646/// Process an ordered list of entries in parallel
647/// 1. In order lock accounts for each entry while the lock succeeds, up to a Tick entry
648/// 2. Process the locked group in parallel
649/// 3. Register the `Tick` if it's available
650/// 4. Update the leader scheduler, goto 1
651///
652/// This method is for use testing against a single Bank, and assumes `Bank::transaction_count()`
653/// represents the number of transactions executed in this Bank
654pub fn process_entries_for_tests(
655    bank: &BankWithScheduler,
656    entries: Vec<Entry>,
657    transaction_status_sender: Option<&TransactionStatusSender>,
658    replay_vote_sender: Option<&ReplayVoteSender>,
659) -> Result<()> {
660    let replay_tx_thread_pool = create_thread_pool(1);
661    let verify_transaction = {
662        let bank = bank.clone_with_scheduler();
663        move |versioned_tx: VersionedTransaction| -> Result<RuntimeTransaction<SanitizedTransaction>> {
664            bank.verify_transaction(versioned_tx, TransactionVerificationMode::FullVerification)
665        }
666    };
667
668    let mut entry_starting_index: usize = bank.transaction_count().try_into().unwrap();
669    let mut batch_timing = BatchExecutionTiming::default();
670    let replay_entries: Vec<_> = entry::verify_transactions(
671        entries,
672        &replay_tx_thread_pool,
673        Arc::new(verify_transaction),
674    )?
675    .into_iter()
676    .map(|entry| {
677        let starting_index = entry_starting_index;
678        if let EntryType::Transactions(ref transactions) = entry {
679            entry_starting_index = entry_starting_index.saturating_add(transactions.len());
680        }
681        ReplayEntry {
682            entry,
683            starting_index,
684        }
685    })
686    .collect();
687
688    let ignored_prioritization_fee_cache = PrioritizationFeeCache::new(0u64);
689    let result = process_entries(
690        bank,
691        &replay_tx_thread_pool,
692        replay_entries,
693        transaction_status_sender,
694        replay_vote_sender,
695        &mut batch_timing,
696        None,
697        &ignored_prioritization_fee_cache,
698    );
699
700    debug!("process_entries: {:?}", batch_timing);
701    result
702}
703
704fn process_entries(
705    bank: &BankWithScheduler,
706    replay_tx_thread_pool: &ThreadPool,
707    entries: Vec<ReplayEntry>,
708    transaction_status_sender: Option<&TransactionStatusSender>,
709    replay_vote_sender: Option<&ReplayVoteSender>,
710    batch_timing: &mut BatchExecutionTiming,
711    log_messages_bytes_limit: Option<usize>,
712    prioritization_fee_cache: &PrioritizationFeeCache,
713) -> Result<()> {
714    // accumulator for entries that can be processed in parallel
715    let mut batches = vec![];
716    let mut tick_hashes = vec![];
717
718    for ReplayEntry {
719        entry,
720        starting_index,
721    } in entries
722    {
723        match entry {
724            EntryType::Tick(hash) => {
725                // If it's a tick, save it for later
726                tick_hashes.push(hash);
727                if bank.is_block_boundary(bank.tick_height() + tick_hashes.len() as u64) {
728                    // If it's a tick that will cause a new blockhash to be created,
729                    // execute the group and register the tick
730                    process_batches(
731                        bank,
732                        replay_tx_thread_pool,
733                        batches.drain(..),
734                        transaction_status_sender,
735                        replay_vote_sender,
736                        batch_timing,
737                        log_messages_bytes_limit,
738                        prioritization_fee_cache,
739                    )?;
740                    for hash in tick_hashes.drain(..) {
741                        bank.register_tick(&hash);
742                    }
743                }
744            }
745            EntryType::Transactions(transactions) => {
746                queue_batches_with_lock_retry(
747                    bank,
748                    starting_index,
749                    transactions,
750                    &mut batches,
751                    |batches| {
752                        process_batches(
753                            bank,
754                            replay_tx_thread_pool,
755                            batches,
756                            transaction_status_sender,
757                            replay_vote_sender,
758                            batch_timing,
759                            log_messages_bytes_limit,
760                            prioritization_fee_cache,
761                        )
762                    },
763                )?;
764            }
765        }
766    }
767    process_batches(
768        bank,
769        replay_tx_thread_pool,
770        batches.into_iter(),
771        transaction_status_sender,
772        replay_vote_sender,
773        batch_timing,
774        log_messages_bytes_limit,
775        prioritization_fee_cache,
776    )?;
777    for hash in tick_hashes {
778        bank.register_tick(&hash);
779    }
780    Ok(())
781}
782
783/// If an entry can be locked without failure, the transactions are pushed
784/// as a batch to `batches`. If the lock fails, the transactions are unlocked
785/// and the batches are processed.
786/// The locking process is retried, and if it fails again the block is marked
787/// as dead.
788/// If the lock retry succeeds, then the batch is pushed into `batches`.
789fn queue_batches_with_lock_retry(
790    bank: &Bank,
791    starting_index: usize,
792    transactions: Vec<RuntimeTransaction<SanitizedTransaction>>,
793    batches: &mut Vec<LockedTransactionsWithIndexes<SanitizedTransaction>>,
794    mut process_batches: impl FnMut(
795        Drain<LockedTransactionsWithIndexes<SanitizedTransaction>>,
796    ) -> Result<()>,
797) -> Result<()> {
798    // try to lock the accounts
799    let lock_results = bank.try_lock_accounts(&transactions);
800    let first_lock_err = first_err(&lock_results);
801    if first_lock_err.is_ok() {
802        batches.push(LockedTransactionsWithIndexes {
803            lock_results,
804            transactions,
805            starting_index,
806        });
807        return Ok(());
808    }
809
810    // We need to unlock the transactions that succeeded to lock before the
811    // retry.
812    bank.unlock_accounts(transactions.iter().zip(lock_results.iter()));
813
814    // We failed to lock, there are 2 possible reasons:
815    // 1. A batch already in `batches` holds the lock.
816    // 2. The batch is "self-conflicting" (i.e. the batch has account lock conflicts with itself)
817
818    // Use the callback to process batches, and clear them.
819    // Clearing the batches will `Drop` the batches which will unlock the accounts.
820    process_batches(batches.drain(..))?;
821
822    // Retry the lock
823    let lock_results = bank.try_lock_accounts(&transactions);
824    match first_err(&lock_results) {
825        Ok(()) => {
826            batches.push(LockedTransactionsWithIndexes {
827                lock_results,
828                transactions,
829                starting_index,
830            });
831            Ok(())
832        }
833        Err(err) => {
834            // We still may have succeeded to lock some accounts, unlock them.
835            bank.unlock_accounts(transactions.iter().zip(lock_results.iter()));
836
837            // An entry has account lock conflicts with *itself*, which should not happen
838            // if generated by a properly functioning leader
839            datapoint_error!(
840                "validator_process_entry_error",
841                (
842                    "error",
843                    format!(
844                        "Lock accounts error, entry conflicts with itself, txs: {transactions:?}"
845                    ),
846                    String
847                )
848            );
849            Err(err)
850        }
851    }
852}
853
854#[derive(Error, Debug)]
855pub enum BlockstoreProcessorError {
856    #[error("failed to load entries, error: {0}")]
857    FailedToLoadEntries(#[from] BlockstoreError),
858
859    #[error("failed to load meta")]
860    FailedToLoadMeta,
861
862    #[error("invalid block error: {0}")]
863    InvalidBlock(#[from] BlockError),
864
865    #[error("invalid transaction error: {0}")]
866    InvalidTransaction(#[from] TransactionError),
867
868    #[error("no valid forks found")]
869    NoValidForksFound,
870
871    #[error("invalid hard fork slot {0}")]
872    InvalidHardFork(Slot),
873
874    #[error("root bank with mismatched capitalization at {0}")]
875    RootBankWithMismatchedCapitalization(Slot),
876
877    #[error("set root error {0}")]
878    SetRootError(#[from] SetRootError),
879
880    #[error("incomplete final fec set")]
881    IncompleteFinalFecSet,
882
883    #[error("invalid retransmitter signature final fec set")]
884    InvalidRetransmitterSignatureFinalFecSet,
885}
886
887/// Callback for accessing bank state after each slot is confirmed while
888/// processing the blockstore
889pub type ProcessSlotCallback = Arc<dyn Fn(&Bank) + Sync + Send>;
890
891#[derive(Default, Clone)]
892pub struct ProcessOptions {
893    /// Run PoH, transaction signature and other transaction verifications on the entries.
894    pub run_verification: bool,
895    pub full_leader_cache: bool,
896    pub halt_at_slot: Option<Slot>,
897    pub slot_callback: Option<ProcessSlotCallback>,
898    pub new_hard_forks: Option<Vec<Slot>>,
899    pub debug_keys: Option<Arc<HashSet<Pubkey>>>,
900    pub limit_load_slot_count_from_snapshot: Option<usize>,
901    pub allow_dead_slots: bool,
902    pub accounts_db_test_hash_calculation: bool,
903    pub accounts_db_skip_shrink: bool,
904    pub accounts_db_force_initial_clean: bool,
905    pub accounts_db_config: Option<AccountsDbConfig>,
906    pub verify_index: bool,
907    pub runtime_config: RuntimeConfig,
908    pub on_halt_store_hash_raw_data_for_debug: bool,
909    /// true if after processing the contents of the blockstore at startup, we should run an accounts hash calc
910    /// This is useful for debugging.
911    pub run_final_accounts_hash_calc: bool,
912    pub use_snapshot_archives_at_startup: UseSnapshotArchivesAtStartup,
913    #[cfg(feature = "dev-context-only-utils")]
914    pub hash_overrides: Option<HashOverrides>,
915    pub abort_on_invalid_block: bool,
916    pub no_block_cost_limits: bool,
917}
918
919pub fn test_process_blockstore(
920    genesis_config: &GenesisConfig,
921    blockstore: &Blockstore,
922    opts: &ProcessOptions,
923    exit: Arc<AtomicBool>,
924) -> (Arc<RwLock<BankForks>>, LeaderScheduleCache) {
925    // Spin up a thread to be a fake Accounts Background Service.  Need to intercept and handle all
926    // EpochAccountsHash requests so future rooted banks do not hang in Bank::freeze() waiting for
927    // an in-flight EAH calculation to complete.
928    let (snapshot_request_sender, snapshot_request_receiver) = crossbeam_channel::unbounded();
929    let abs_request_sender = AbsRequestSender::new(snapshot_request_sender);
930    let bg_exit = Arc::new(AtomicBool::new(false));
931    let bg_thread = {
932        let exit = Arc::clone(&bg_exit);
933        std::thread::spawn(move || {
934            while !exit.load(Relaxed) {
935                snapshot_request_receiver
936                    .try_iter()
937                    .filter(|snapshot_request| {
938                        snapshot_request.request_kind == SnapshotRequestKind::EpochAccountsHash
939                    })
940                    .for_each(|snapshot_request| {
941                        snapshot_request
942                            .snapshot_root_bank
943                            .rc
944                            .accounts
945                            .accounts_db
946                            .epoch_accounts_hash_manager
947                            .set_valid(
948                                EpochAccountsHash::new(Hash::new_unique()),
949                                snapshot_request.snapshot_root_bank.slot(),
950                            )
951                    });
952                std::thread::sleep(Duration::from_millis(100));
953            }
954        })
955    };
956
957    let (bank_forks, leader_schedule_cache, ..) = crate::bank_forks_utils::load_bank_forks(
958        genesis_config,
959        blockstore,
960        Vec::new(),
961        None,
962        opts,
963        None,
964        None,
965        None,
966        exit,
967    )
968    .unwrap();
969
970    process_blockstore_from_root(
971        blockstore,
972        &bank_forks,
973        &leader_schedule_cache,
974        opts,
975        None,
976        None,
977        None,
978        &abs_request_sender,
979    )
980    .unwrap();
981
982    bg_exit.store(true, Relaxed);
983    bg_thread.join().unwrap();
984
985    (bank_forks, leader_schedule_cache)
986}
987
988pub(crate) fn process_blockstore_for_bank_0(
989    genesis_config: &GenesisConfig,
990    blockstore: &Blockstore,
991    account_paths: Vec<PathBuf>,
992    opts: &ProcessOptions,
993    block_meta_sender: Option<&BlockMetaSender>,
994    entry_notification_sender: Option<&EntryNotifierSender>,
995    accounts_update_notifier: Option<AccountsUpdateNotifier>,
996    exit: Arc<AtomicBool>,
997) -> Arc<RwLock<BankForks>> {
998    // Setup bank for slot 0
999    let bank0 = Bank::new_with_paths(
1000        genesis_config,
1001        Arc::new(opts.runtime_config.clone()),
1002        account_paths,
1003        opts.debug_keys.clone(),
1004        None,
1005        false,
1006        opts.accounts_db_config.clone(),
1007        accounts_update_notifier,
1008        None,
1009        exit,
1010        None,
1011        None,
1012    );
1013    let bank0_slot = bank0.slot();
1014    let bank_forks = BankForks::new_rw_arc(bank0);
1015
1016    info!("Processing ledger for slot 0...");
1017    let replay_tx_thread_pool = create_thread_pool(get_max_thread_count());
1018    process_bank_0(
1019        &bank_forks
1020            .read()
1021            .unwrap()
1022            .get_with_scheduler(bank0_slot)
1023            .unwrap(),
1024        blockstore,
1025        &replay_tx_thread_pool,
1026        opts,
1027        &VerifyRecyclers::default(),
1028        block_meta_sender,
1029        entry_notification_sender,
1030    );
1031    bank_forks
1032}
1033
1034/// Process blockstore from a known root bank
1035#[allow(clippy::too_many_arguments)]
1036pub fn process_blockstore_from_root(
1037    blockstore: &Blockstore,
1038    bank_forks: &RwLock<BankForks>,
1039    leader_schedule_cache: &LeaderScheduleCache,
1040    opts: &ProcessOptions,
1041    transaction_status_sender: Option<&TransactionStatusSender>,
1042    block_meta_sender: Option<&BlockMetaSender>,
1043    entry_notification_sender: Option<&EntryNotifierSender>,
1044    accounts_background_request_sender: &AbsRequestSender,
1045) -> result::Result<(), BlockstoreProcessorError> {
1046    let (start_slot, start_slot_hash) = {
1047        // Starting slot must be a root, and thus has no parents
1048        assert_eq!(bank_forks.read().unwrap().banks().len(), 1);
1049        let bank = bank_forks.read().unwrap().root_bank();
1050        #[cfg(feature = "dev-context-only-utils")]
1051        if let Some(hash_overrides) = &opts.hash_overrides {
1052            info!(
1053                "Will override following slots' hashes: {:#?}",
1054                hash_overrides
1055            );
1056            bank.set_hash_overrides(hash_overrides.clone());
1057        }
1058        if opts.no_block_cost_limits {
1059            warn!("setting block cost limits to MAX");
1060            bank.write_cost_tracker()
1061                .unwrap()
1062                .set_limits(u64::MAX, u64::MAX, u64::MAX);
1063        }
1064        assert!(bank.parent().is_none());
1065        (bank.slot(), bank.hash())
1066    };
1067
1068    info!("Processing ledger from slot {}...", start_slot);
1069    let now = Instant::now();
1070
1071    // Ensure start_slot is rooted for correct replay; also ensure start_slot and
1072    // qualifying children are marked as connected
1073    if blockstore.is_primary_access() {
1074        blockstore
1075            .mark_slots_as_if_rooted_normally_at_startup(
1076                vec![(start_slot, Some(start_slot_hash))],
1077                true,
1078            )
1079            .expect("Couldn't mark start_slot as root in startup");
1080        blockstore
1081            .set_and_chain_connected_on_root_and_next_slots(start_slot)
1082            .expect("Couldn't mark start_slot as connected during startup")
1083    } else {
1084        info!(
1085            "Start slot {} isn't a root, and won't be updated due to secondary blockstore access",
1086            start_slot
1087        );
1088    }
1089
1090    if let Ok(Some(highest_slot)) = blockstore.highest_slot() {
1091        info!("ledger holds data through slot {}", highest_slot);
1092    }
1093
1094    let mut timing = ExecuteTimings::default();
1095    let (num_slots_processed, num_new_roots_found) = if let Some(start_slot_meta) = blockstore
1096        .meta(start_slot)
1097        .unwrap_or_else(|_| panic!("Failed to get meta for slot {start_slot}"))
1098    {
1099        let replay_tx_thread_pool = create_thread_pool(get_max_thread_count());
1100        load_frozen_forks(
1101            bank_forks,
1102            &start_slot_meta,
1103            blockstore,
1104            &replay_tx_thread_pool,
1105            leader_schedule_cache,
1106            opts,
1107            transaction_status_sender,
1108            block_meta_sender,
1109            entry_notification_sender,
1110            &mut timing,
1111            accounts_background_request_sender,
1112        )?
1113    } else {
1114        // If there's no meta in the blockstore for the input `start_slot`,
1115        // then we started from a snapshot and are unable to process anything.
1116        //
1117        // If the ledger has any data at all, the snapshot was likely taken at
1118        // a slot that is not within the range of ledger min/max slot(s).
1119        warn!(
1120            "Starting slot {} is not in Blockstore, unable to process",
1121            start_slot
1122        );
1123        (0, 0)
1124    };
1125
1126    let processing_time = now.elapsed();
1127
1128    datapoint_info!(
1129        "process_blockstore_from_root",
1130        ("total_time_us", processing_time.as_micros(), i64),
1131        (
1132            "frozen_banks",
1133            bank_forks.read().unwrap().frozen_banks().len(),
1134            i64
1135        ),
1136        ("slot", bank_forks.read().unwrap().root(), i64),
1137        ("num_slots_processed", num_slots_processed, i64),
1138        ("num_new_roots_found", num_new_roots_found, i64),
1139        ("forks", bank_forks.read().unwrap().banks().len(), i64),
1140    );
1141
1142    info!("ledger processing timing: {:?}", timing);
1143    {
1144        let bank_forks = bank_forks.read().unwrap();
1145        let mut bank_slots = bank_forks.banks().keys().copied().collect::<Vec<_>>();
1146        bank_slots.sort_unstable();
1147
1148        info!(
1149            "ledger processed in {}. root slot is {}, {} bank{}: {}",
1150            HumanTime::from(chrono::Duration::from_std(processing_time).unwrap())
1151                .to_text_en(Accuracy::Precise, Tense::Present),
1152            bank_forks.root(),
1153            bank_slots.len(),
1154            if bank_slots.len() > 1 { "s" } else { "" },
1155            bank_slots.iter().map(|slot| slot.to_string()).join(", "),
1156        );
1157        assert!(bank_forks.active_bank_slots().is_empty());
1158    }
1159
1160    Ok(())
1161}
1162
1163/// Verify that a segment of entries has the correct number of ticks and hashes
1164fn verify_ticks(
1165    bank: &Bank,
1166    entries: &[Entry],
1167    slot_full: bool,
1168    tick_hash_count: &mut u64,
1169) -> std::result::Result<(), BlockError> {
1170    let next_bank_tick_height = bank.tick_height() + entries.tick_count();
1171    let max_bank_tick_height = bank.max_tick_height();
1172
1173    if next_bank_tick_height > max_bank_tick_height {
1174        warn!("Too many entry ticks found in slot: {}", bank.slot());
1175        return Err(BlockError::TooManyTicks);
1176    }
1177
1178    if next_bank_tick_height < max_bank_tick_height && slot_full {
1179        info!("Too few entry ticks found in slot: {}", bank.slot());
1180        return Err(BlockError::TooFewTicks);
1181    }
1182
1183    if next_bank_tick_height == max_bank_tick_height {
1184        let has_trailing_entry = entries.last().map(|e| !e.is_tick()).unwrap_or_default();
1185        if has_trailing_entry {
1186            warn!("Slot: {} did not end with a tick entry", bank.slot());
1187            return Err(BlockError::TrailingEntry);
1188        }
1189
1190        if !slot_full {
1191            warn!("Slot: {} was not marked full", bank.slot());
1192            return Err(BlockError::InvalidLastTick);
1193        }
1194    }
1195
1196    let hashes_per_tick = bank.hashes_per_tick().unwrap_or(0);
1197    if !entries.verify_tick_hash_count(tick_hash_count, hashes_per_tick) {
1198        warn!(
1199            "Tick with invalid number of hashes found in slot: {}",
1200            bank.slot()
1201        );
1202        return Err(BlockError::InvalidTickHashCount);
1203    }
1204
1205    Ok(())
1206}
1207
1208#[allow(clippy::too_many_arguments)]
1209#[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
1210fn confirm_full_slot(
1211    blockstore: &Blockstore,
1212    bank: &BankWithScheduler,
1213    replay_tx_thread_pool: &ThreadPool,
1214    opts: &ProcessOptions,
1215    recyclers: &VerifyRecyclers,
1216    progress: &mut ConfirmationProgress,
1217    transaction_status_sender: Option<&TransactionStatusSender>,
1218    entry_notification_sender: Option<&EntryNotifierSender>,
1219    replay_vote_sender: Option<&ReplayVoteSender>,
1220    timing: &mut ExecuteTimings,
1221) -> result::Result<(), BlockstoreProcessorError> {
1222    let mut confirmation_timing = ConfirmationTiming::default();
1223    let skip_verification = !opts.run_verification;
1224    let ignored_prioritization_fee_cache = PrioritizationFeeCache::new(0u64);
1225
1226    confirm_slot(
1227        blockstore,
1228        bank,
1229        replay_tx_thread_pool,
1230        &mut confirmation_timing,
1231        progress,
1232        skip_verification,
1233        transaction_status_sender,
1234        entry_notification_sender,
1235        replay_vote_sender,
1236        recyclers,
1237        opts.allow_dead_slots,
1238        opts.runtime_config.log_messages_bytes_limit,
1239        &ignored_prioritization_fee_cache,
1240    )?;
1241
1242    timing.accumulate(&confirmation_timing.batch_execute.totals);
1243
1244    if !bank.is_complete() {
1245        Err(BlockstoreProcessorError::InvalidBlock(
1246            BlockError::Incomplete,
1247        ))
1248    } else {
1249        Ok(())
1250    }
1251}
1252
1253/// Measures different parts of the slot confirmation processing pipeline.
1254#[derive(Debug)]
1255pub struct ConfirmationTiming {
1256    /// Moment when the `ConfirmationTiming` instance was created.  Used to track the total wall
1257    /// clock time from the moment the first shard for the slot is received and to the moment the
1258    /// slot is complete.
1259    pub started: Instant,
1260
1261    /// Wall clock time used by the slot confirmation code, including PoH/signature verification,
1262    /// and replay.  As replay can run in parallel with the verification, this value can not be
1263    /// recovered from the `replay_elapsed` and or `{poh,transaction}_verify_elapsed`.  This
1264    /// includes failed cases, when `confirm_slot_entries` exist with an error.  In microseconds.
1265    /// When unified scheduler is enabled, replay excludes the transaction execution, only
1266    /// accounting for task creation and submission to the scheduler.
1267    pub confirmation_elapsed: u64,
1268
1269    /// Wall clock time used by the entry replay code.  Does not include the PoH or the transaction
1270    /// signature/precompiles verification, but can overlap with the PoH and signature verification.
1271    /// In microseconds.
1272    /// When unified scheduler is enabled, replay excludes the transaction execution, only
1273    /// accounting for task creation and submission to the scheduler.
1274    pub replay_elapsed: u64,
1275
1276    /// Wall clock times, used for the PoH verification of entries.  In microseconds.
1277    pub poh_verify_elapsed: u64,
1278
1279    /// Wall clock time, used for the signature verification as well as precompiles verification.
1280    /// In microseconds.
1281    pub transaction_verify_elapsed: u64,
1282
1283    /// Wall clock time spent loading data sets (and entries) from the blockstore.  This does not
1284    /// include the case when the blockstore load failed.  In microseconds.
1285    pub fetch_elapsed: u64,
1286
1287    /// Same as `fetch_elapsed` above, but for the case when the blockstore load fails.  In
1288    /// microseconds.
1289    pub fetch_fail_elapsed: u64,
1290
1291    /// `batch_execute()` measurements.
1292    pub batch_execute: BatchExecutionTiming,
1293}
1294
1295impl Default for ConfirmationTiming {
1296    fn default() -> Self {
1297        Self {
1298            started: Instant::now(),
1299            confirmation_elapsed: 0,
1300            replay_elapsed: 0,
1301            poh_verify_elapsed: 0,
1302            transaction_verify_elapsed: 0,
1303            fetch_elapsed: 0,
1304            fetch_fail_elapsed: 0,
1305            batch_execute: BatchExecutionTiming::default(),
1306        }
1307    }
1308}
1309
1310/// Measures times related to transaction execution in a slot.
1311#[derive(Debug, Default)]
1312pub struct BatchExecutionTiming {
1313    /// Time used by transaction execution.  Accumulated across multiple threads that are running
1314    /// `execute_batch()`.
1315    pub totals: ExecuteTimings,
1316
1317    /// Wall clock time used by the transaction execution part of pipeline.
1318    /// [`ConfirmationTiming::replay_elapsed`] includes this time.  In microseconds.
1319    wall_clock_us: Saturating<u64>,
1320
1321    /// Time used to execute transactions, via `execute_batch()`, in the thread that consumed the
1322    /// most time (in terms of total_thread_us) among rayon threads. Note that the slowest thread
1323    /// is determined each time a given group of batches is newly processed. So, this is a coarse
1324    /// approximation of wall-time single-threaded linearized metrics, discarding all metrics other
1325    /// than the arbitrary set of batches mixed with various transactions, which replayed slowest
1326    /// as a whole for each rayon processing session, also after blockstore_processor's rebatching.
1327    ///
1328    /// When unified scheduler is enabled, this field isn't maintained, because it's not batched at
1329    /// all.
1330    slowest_thread: ThreadExecuteTimings,
1331}
1332
1333impl BatchExecutionTiming {
1334    pub fn accumulate(
1335        &mut self,
1336        new_batch: ExecuteBatchesInternalMetrics,
1337        is_unified_scheduler_enabled: bool,
1338    ) {
1339        let Self {
1340            totals,
1341            wall_clock_us,
1342            slowest_thread,
1343        } = self;
1344
1345        // These metric fields aren't applicable for the unified scheduler
1346        if !is_unified_scheduler_enabled {
1347            *wall_clock_us += new_batch.execute_batches_us;
1348
1349            totals.saturating_add_in_place(TotalBatchesLen, new_batch.total_batches_len);
1350            totals.saturating_add_in_place(NumExecuteBatches, 1);
1351        }
1352
1353        for thread_times in new_batch.execution_timings_per_thread.values() {
1354            totals.accumulate(&thread_times.execute_timings);
1355        }
1356
1357        // This whole metric (replay-slot-end-to-end-stats) isn't applicable for the unified
1358        // scheduler.
1359        if !is_unified_scheduler_enabled {
1360            let slowest = new_batch
1361                .execution_timings_per_thread
1362                .values()
1363                .max_by_key(|thread_times| thread_times.total_thread_us);
1364
1365            if let Some(slowest) = slowest {
1366                slowest_thread.accumulate(slowest);
1367                slowest_thread
1368                    .execute_timings
1369                    .saturating_add_in_place(NumExecuteBatches, 1);
1370            };
1371        }
1372    }
1373}
1374
1375#[derive(Debug, Default)]
1376pub struct ThreadExecuteTimings {
1377    pub total_thread_us: Saturating<u64>,
1378    pub total_transactions_executed: Saturating<u64>,
1379    pub execute_timings: ExecuteTimings,
1380}
1381
1382impl ThreadExecuteTimings {
1383    pub fn report_stats(&self, slot: Slot) {
1384        lazy! {
1385            datapoint_info!(
1386                "replay-slot-end-to-end-stats",
1387                ("slot", slot as i64, i64),
1388                ("total_thread_us", self.total_thread_us.0 as i64, i64),
1389                ("total_transactions_executed", self.total_transactions_executed.0 as i64, i64),
1390                // Everything inside the `eager!` block will be eagerly expanded before
1391                // evaluation of the rest of the surrounding macro.
1392                // Pass false because this code-path is never touched by unified scheduler.
1393                eager!{report_execute_timings!(self.execute_timings, false)}
1394            );
1395        };
1396    }
1397
1398    pub fn accumulate(&mut self, other: &ThreadExecuteTimings) {
1399        self.execute_timings.accumulate(&other.execute_timings);
1400        self.total_thread_us += other.total_thread_us;
1401        self.total_transactions_executed += other.total_transactions_executed;
1402    }
1403}
1404
1405#[derive(Default)]
1406pub struct ReplaySlotStats(ConfirmationTiming);
1407impl std::ops::Deref for ReplaySlotStats {
1408    type Target = ConfirmationTiming;
1409    fn deref(&self) -> &Self::Target {
1410        &self.0
1411    }
1412}
1413impl std::ops::DerefMut for ReplaySlotStats {
1414    fn deref_mut(&mut self) -> &mut Self::Target {
1415        &mut self.0
1416    }
1417}
1418
1419impl ReplaySlotStats {
1420    pub fn report_stats(
1421        &self,
1422        slot: Slot,
1423        num_txs: usize,
1424        num_entries: usize,
1425        num_shreds: u64,
1426        bank_complete_time_us: u64,
1427        is_unified_scheduler_enabled: bool,
1428    ) {
1429        let confirmation_elapsed = if is_unified_scheduler_enabled {
1430            "confirmation_without_replay_us"
1431        } else {
1432            "confirmation_time_us"
1433        };
1434        let replay_elapsed = if is_unified_scheduler_enabled {
1435            "task_submission_us"
1436        } else {
1437            "replay_time"
1438        };
1439        let execute_batches_us = if is_unified_scheduler_enabled {
1440            None
1441        } else {
1442            Some(self.batch_execute.wall_clock_us.0 as i64)
1443        };
1444
1445        lazy! {
1446            datapoint_info!(
1447                "replay-slot-stats",
1448                ("slot", slot as i64, i64),
1449                ("fetch_entries_time", self.fetch_elapsed as i64, i64),
1450                (
1451                    "fetch_entries_fail_time",
1452                    self.fetch_fail_elapsed as i64,
1453                    i64
1454                ),
1455                (
1456                    "entry_poh_verification_time",
1457                    self.poh_verify_elapsed as i64,
1458                    i64
1459                ),
1460                (
1461                    "entry_transaction_verification_time",
1462                    self.transaction_verify_elapsed as i64,
1463                    i64
1464                ),
1465                (confirmation_elapsed, self.confirmation_elapsed as i64, i64),
1466                (replay_elapsed, self.replay_elapsed as i64, i64),
1467                ("execute_batches_us", execute_batches_us, Option<i64>),
1468                (
1469                    "replay_total_elapsed",
1470                    self.started.elapsed().as_micros() as i64,
1471                    i64
1472                ),
1473                ("bank_complete_time_us", bank_complete_time_us, i64),
1474                ("total_transactions", num_txs as i64, i64),
1475                ("total_entries", num_entries as i64, i64),
1476                ("total_shreds", num_shreds as i64, i64),
1477                // Everything inside the `eager!` block will be eagerly expanded before
1478                // evaluation of the rest of the surrounding macro.
1479                eager!{report_execute_timings!(self.batch_execute.totals, is_unified_scheduler_enabled)}
1480            );
1481        };
1482
1483        // Skip reporting replay-slot-end-to-end-stats entirely if unified scheduler is enabled,
1484        // because the whole metrics itself is only meaningful for rayon-based worker threads.
1485        //
1486        // See slowest_thread doc comment for details.
1487        if !is_unified_scheduler_enabled {
1488            self.batch_execute.slowest_thread.report_stats(slot);
1489        }
1490
1491        let mut per_pubkey_timings: Vec<_> = self
1492            .batch_execute
1493            .totals
1494            .details
1495            .per_program_timings
1496            .iter()
1497            .collect();
1498        per_pubkey_timings.sort_by(|a, b| b.1.accumulated_us.cmp(&a.1.accumulated_us));
1499        let (total_us, total_units, total_count, total_errored_units, total_errored_count) =
1500            per_pubkey_timings.iter().fold(
1501                (0, 0, 0, 0, 0),
1502                |(sum_us, sum_units, sum_count, sum_errored_units, sum_errored_count), a| {
1503                    (
1504                        sum_us + a.1.accumulated_us.0,
1505                        sum_units + a.1.accumulated_units.0,
1506                        sum_count + a.1.count.0,
1507                        sum_errored_units + a.1.total_errored_units.0,
1508                        sum_errored_count + a.1.errored_txs_compute_consumed.len(),
1509                    )
1510                },
1511            );
1512
1513        for (pubkey, time) in per_pubkey_timings.iter().take(5) {
1514            datapoint_trace!(
1515                "per_program_timings",
1516                ("slot", slot as i64, i64),
1517                ("pubkey", pubkey.to_string(), String),
1518                ("execute_us", time.accumulated_us.0, i64),
1519                ("accumulated_units", time.accumulated_units.0, i64),
1520                ("errored_units", time.total_errored_units.0, i64),
1521                ("count", time.count.0, i64),
1522                (
1523                    "errored_count",
1524                    time.errored_txs_compute_consumed.len(),
1525                    i64
1526                ),
1527            );
1528        }
1529        datapoint_info!(
1530            "per_program_timings",
1531            ("slot", slot as i64, i64),
1532            ("pubkey", "all", String),
1533            ("execute_us", total_us, i64),
1534            ("accumulated_units", total_units, i64),
1535            ("count", total_count, i64),
1536            ("errored_units", total_errored_units, i64),
1537            ("errored_count", total_errored_count, i64)
1538        );
1539    }
1540}
1541
1542#[derive(Default)]
1543pub struct ConfirmationProgress {
1544    pub last_entry: Hash,
1545    pub tick_hash_count: u64,
1546    pub num_shreds: u64,
1547    pub num_entries: usize,
1548    pub num_txs: usize,
1549}
1550
1551impl ConfirmationProgress {
1552    pub fn new(last_entry: Hash) -> Self {
1553        Self {
1554            last_entry,
1555            ..Self::default()
1556        }
1557    }
1558}
1559
1560#[allow(clippy::too_many_arguments)]
1561pub fn confirm_slot(
1562    blockstore: &Blockstore,
1563    bank: &BankWithScheduler,
1564    replay_tx_thread_pool: &ThreadPool,
1565    timing: &mut ConfirmationTiming,
1566    progress: &mut ConfirmationProgress,
1567    skip_verification: bool,
1568    transaction_status_sender: Option<&TransactionStatusSender>,
1569    entry_notification_sender: Option<&EntryNotifierSender>,
1570    replay_vote_sender: Option<&ReplayVoteSender>,
1571    recyclers: &VerifyRecyclers,
1572    allow_dead_slots: bool,
1573    log_messages_bytes_limit: Option<usize>,
1574    prioritization_fee_cache: &PrioritizationFeeCache,
1575) -> result::Result<(), BlockstoreProcessorError> {
1576    let slot = bank.slot();
1577
1578    let slot_entries_load_result = {
1579        let mut load_elapsed = Measure::start("load_elapsed");
1580        let load_result = blockstore
1581            .get_slot_entries_with_shred_info(slot, progress.num_shreds, allow_dead_slots)
1582            .map_err(BlockstoreProcessorError::FailedToLoadEntries);
1583        load_elapsed.stop();
1584        if load_result.is_err() {
1585            timing.fetch_fail_elapsed += load_elapsed.as_us();
1586        } else {
1587            timing.fetch_elapsed += load_elapsed.as_us();
1588        }
1589        load_result
1590    }?;
1591
1592    confirm_slot_entries(
1593        bank,
1594        replay_tx_thread_pool,
1595        slot_entries_load_result,
1596        timing,
1597        progress,
1598        skip_verification,
1599        transaction_status_sender,
1600        entry_notification_sender,
1601        replay_vote_sender,
1602        recyclers,
1603        log_messages_bytes_limit,
1604        prioritization_fee_cache,
1605    )
1606}
1607
1608#[allow(clippy::too_many_arguments)]
1609fn confirm_slot_entries(
1610    bank: &BankWithScheduler,
1611    replay_tx_thread_pool: &ThreadPool,
1612    slot_entries_load_result: (Vec<Entry>, u64, bool),
1613    timing: &mut ConfirmationTiming,
1614    progress: &mut ConfirmationProgress,
1615    skip_verification: bool,
1616    transaction_status_sender: Option<&TransactionStatusSender>,
1617    entry_notification_sender: Option<&EntryNotifierSender>,
1618    replay_vote_sender: Option<&ReplayVoteSender>,
1619    recyclers: &VerifyRecyclers,
1620    log_messages_bytes_limit: Option<usize>,
1621    prioritization_fee_cache: &PrioritizationFeeCache,
1622) -> result::Result<(), BlockstoreProcessorError> {
1623    let ConfirmationTiming {
1624        confirmation_elapsed,
1625        replay_elapsed,
1626        poh_verify_elapsed,
1627        transaction_verify_elapsed,
1628        batch_execute: batch_execute_timing,
1629        ..
1630    } = timing;
1631
1632    let confirmation_elapsed_timer = Measure::start("confirmation_elapsed");
1633    defer! {
1634        *confirmation_elapsed += confirmation_elapsed_timer.end_as_us();
1635    };
1636
1637    let slot = bank.slot();
1638    let (entries, num_shreds, slot_full) = slot_entries_load_result;
1639    let num_entries = entries.len();
1640    let mut entry_tx_starting_indexes = Vec::with_capacity(num_entries);
1641    let mut entry_tx_starting_index = progress.num_txs;
1642    let num_txs = entries
1643        .iter()
1644        .enumerate()
1645        .map(|(i, entry)| {
1646            if let Some(entry_notification_sender) = entry_notification_sender {
1647                let entry_index = progress.num_entries.saturating_add(i);
1648                if let Err(err) = entry_notification_sender.send(EntryNotification {
1649                    slot,
1650                    index: entry_index,
1651                    entry: entry.into(),
1652                    starting_transaction_index: entry_tx_starting_index,
1653                }) {
1654                    warn!(
1655                        "Slot {}, entry {} entry_notification_sender send failed: {:?}",
1656                        slot, entry_index, err
1657                    );
1658                }
1659            }
1660            let num_txs = entry.transactions.len();
1661            let next_tx_starting_index = entry_tx_starting_index.saturating_add(num_txs);
1662            entry_tx_starting_indexes.push(entry_tx_starting_index);
1663            entry_tx_starting_index = next_tx_starting_index;
1664            num_txs
1665        })
1666        .sum::<usize>();
1667    trace!(
1668        "Fetched entries for slot {}, num_entries: {}, num_shreds: {}, num_txs: {}, slot_full: {}",
1669        slot,
1670        num_entries,
1671        num_shreds,
1672        num_txs,
1673        slot_full,
1674    );
1675
1676    if !skip_verification {
1677        let tick_hash_count = &mut progress.tick_hash_count;
1678        verify_ticks(bank, &entries, slot_full, tick_hash_count).map_err(|err| {
1679            warn!(
1680                "{:#?}, slot: {}, entry len: {}, tick_height: {}, last entry: {}, last_blockhash: \
1681                 {}, shred_index: {}, slot_full: {}",
1682                err,
1683                slot,
1684                num_entries,
1685                bank.tick_height(),
1686                progress.last_entry,
1687                bank.last_blockhash(),
1688                num_shreds,
1689                slot_full,
1690            );
1691            err
1692        })?;
1693    }
1694
1695    let last_entry_hash = entries.last().map(|e| e.hash);
1696    let verifier = if !skip_verification {
1697        datapoint_debug!("verify-batch-size", ("size", num_entries as i64, i64));
1698        let entry_state = entries.start_verify(
1699            &progress.last_entry,
1700            replay_tx_thread_pool,
1701            recyclers.clone(),
1702        );
1703        if entry_state.status() == EntryVerificationStatus::Failure {
1704            warn!("Ledger proof of history failed at slot: {}", slot);
1705            return Err(BlockError::InvalidEntryHash.into());
1706        }
1707        Some(entry_state)
1708    } else {
1709        None
1710    };
1711
1712    let verify_transaction = {
1713        let bank = bank.clone_with_scheduler();
1714        move |versioned_tx: VersionedTransaction,
1715              verification_mode: TransactionVerificationMode|
1716              -> Result<RuntimeTransaction<SanitizedTransaction>> {
1717            bank.verify_transaction(versioned_tx, verification_mode)
1718        }
1719    };
1720
1721    let transaction_verification_start = Instant::now();
1722    let transaction_verification_result = entry::start_verify_transactions(
1723        entries,
1724        skip_verification,
1725        replay_tx_thread_pool,
1726        recyclers.clone(),
1727        Arc::new(verify_transaction),
1728    );
1729    let transaction_cpu_duration_us = transaction_verification_start.elapsed().as_micros() as u64;
1730
1731    let mut transaction_verification_result = match transaction_verification_result {
1732        Ok(transaction_verification_result) => transaction_verification_result,
1733        Err(err) => {
1734            warn!(
1735                "Ledger transaction signature verification failed at slot: {}",
1736                bank.slot()
1737            );
1738            return Err(err.into());
1739        }
1740    };
1741
1742    let entries = transaction_verification_result
1743        .entries()
1744        .expect("Transaction verification generates entries");
1745
1746    let mut replay_timer = Measure::start("replay_elapsed");
1747    let replay_entries: Vec<_> = entries
1748        .into_iter()
1749        .zip(entry_tx_starting_indexes)
1750        .map(|(entry, tx_starting_index)| ReplayEntry {
1751            entry,
1752            starting_index: tx_starting_index,
1753        })
1754        .collect();
1755    let process_result = process_entries(
1756        bank,
1757        replay_tx_thread_pool,
1758        replay_entries,
1759        transaction_status_sender,
1760        replay_vote_sender,
1761        batch_execute_timing,
1762        log_messages_bytes_limit,
1763        prioritization_fee_cache,
1764    )
1765    .map_err(BlockstoreProcessorError::from);
1766    replay_timer.stop();
1767    *replay_elapsed += replay_timer.as_us();
1768
1769    {
1770        // If running signature verification on the GPU, wait for that computation to finish, and
1771        // get the result of it. If we did the signature verification on the CPU, this just returns
1772        // the already-computed result produced in start_verify_transactions.  Either way, check the
1773        // result of the signature verification.
1774        let valid = transaction_verification_result.finish_verify();
1775
1776        // The GPU Entry verification (if any) is kicked off right when the CPU-side Entry
1777        // verification finishes, so these times should be disjoint
1778        *transaction_verify_elapsed +=
1779            transaction_cpu_duration_us + transaction_verification_result.gpu_verify_duration();
1780
1781        if !valid {
1782            warn!(
1783                "Ledger transaction signature verification failed at slot: {}",
1784                bank.slot()
1785            );
1786            return Err(TransactionError::SignatureFailure.into());
1787        }
1788    }
1789
1790    if let Some(mut verifier) = verifier {
1791        let verified = verifier.finish_verify(replay_tx_thread_pool);
1792        *poh_verify_elapsed += verifier.poh_duration_us();
1793        if !verified {
1794            warn!("Ledger proof of history failed at slot: {}", bank.slot());
1795            return Err(BlockError::InvalidEntryHash.into());
1796        }
1797    }
1798
1799    process_result?;
1800
1801    progress.num_shreds += num_shreds;
1802    progress.num_entries += num_entries;
1803    progress.num_txs += num_txs;
1804    if let Some(last_entry_hash) = last_entry_hash {
1805        progress.last_entry = last_entry_hash;
1806    }
1807
1808    Ok(())
1809}
1810
1811// Special handling required for processing the entries in slot 0
1812#[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
1813fn process_bank_0(
1814    bank0: &BankWithScheduler,
1815    blockstore: &Blockstore,
1816    replay_tx_thread_pool: &ThreadPool,
1817    opts: &ProcessOptions,
1818    recyclers: &VerifyRecyclers,
1819    block_meta_sender: Option<&BlockMetaSender>,
1820    entry_notification_sender: Option<&EntryNotifierSender>,
1821) {
1822    assert_eq!(bank0.slot(), 0);
1823    let mut progress = ConfirmationProgress::new(bank0.last_blockhash());
1824    confirm_full_slot(
1825        blockstore,
1826        bank0,
1827        replay_tx_thread_pool,
1828        opts,
1829        recyclers,
1830        &mut progress,
1831        None,
1832        entry_notification_sender,
1833        None,
1834        &mut ExecuteTimings::default(),
1835    )
1836    .expect("Failed to process bank 0 from ledger. Did you forget to provide a snapshot?");
1837    if let Some((result, _timings)) = bank0.wait_for_completed_scheduler() {
1838        result.unwrap();
1839    }
1840    bank0.freeze();
1841    if blockstore.is_primary_access() {
1842        blockstore.insert_bank_hash(bank0.slot(), bank0.hash(), false);
1843    }
1844    send_block_meta(bank0, block_meta_sender);
1845}
1846
1847// Given a bank, add its children to the pending slots queue if those children slots are
1848// complete
1849fn process_next_slots(
1850    bank: &Arc<Bank>,
1851    meta: &SlotMeta,
1852    blockstore: &Blockstore,
1853    leader_schedule_cache: &LeaderScheduleCache,
1854    pending_slots: &mut Vec<(SlotMeta, Bank, Hash)>,
1855    opts: &ProcessOptions,
1856) -> result::Result<(), BlockstoreProcessorError> {
1857    if meta.next_slots.is_empty() {
1858        return Ok(());
1859    }
1860
1861    // This is a fork point if there are multiple children, create a new child bank for each fork
1862    for next_slot in &meta.next_slots {
1863        if opts
1864            .halt_at_slot
1865            .is_some_and(|halt_at_slot| *next_slot > halt_at_slot)
1866        {
1867            continue;
1868        }
1869        if !opts.allow_dead_slots && blockstore.is_dead(*next_slot) {
1870            continue;
1871        }
1872
1873        let next_meta = blockstore
1874            .meta(*next_slot)
1875            .map_err(|err| {
1876                warn!("Failed to load meta for slot {}: {:?}", next_slot, err);
1877                BlockstoreProcessorError::FailedToLoadMeta
1878            })?
1879            .unwrap();
1880
1881        // Only process full slots in blockstore_processor, replay_stage
1882        // handles any partials
1883        if next_meta.is_full() {
1884            let next_bank = Bank::new_from_parent(
1885                bank.clone(),
1886                &leader_schedule_cache
1887                    .slot_leader_at(*next_slot, Some(bank))
1888                    .unwrap(),
1889                *next_slot,
1890            );
1891            trace!(
1892                "New bank for slot {}, parent slot is {}",
1893                next_slot,
1894                bank.slot(),
1895            );
1896            pending_slots.push((next_meta, next_bank, bank.last_blockhash()));
1897        }
1898    }
1899
1900    // Reverse sort by slot, so the next slot to be processed can be popped
1901    pending_slots.sort_by(|a, b| b.1.slot().cmp(&a.1.slot()));
1902    Ok(())
1903}
1904
1905/// Starting with the root slot corresponding to `start_slot_meta`, iteratively
1906/// find and process children slots from the blockstore.
1907///
1908/// Returns a tuple (a, b) where a is the number of slots processed and b is
1909/// the number of newly found cluster roots.
1910#[allow(clippy::too_many_arguments)]
1911fn load_frozen_forks(
1912    bank_forks: &RwLock<BankForks>,
1913    start_slot_meta: &SlotMeta,
1914    blockstore: &Blockstore,
1915    replay_tx_thread_pool: &ThreadPool,
1916    leader_schedule_cache: &LeaderScheduleCache,
1917    opts: &ProcessOptions,
1918    transaction_status_sender: Option<&TransactionStatusSender>,
1919    block_meta_sender: Option<&BlockMetaSender>,
1920    entry_notification_sender: Option<&EntryNotifierSender>,
1921    timing: &mut ExecuteTimings,
1922    accounts_background_request_sender: &AbsRequestSender,
1923) -> result::Result<(u64, usize), BlockstoreProcessorError> {
1924    let blockstore_max_root = blockstore.max_root();
1925    let mut root = bank_forks.read().unwrap().root();
1926    let max_root = std::cmp::max(root, blockstore_max_root);
1927    info!(
1928        "load_frozen_forks() latest root from blockstore: {}, max_root: {}",
1929        blockstore_max_root, max_root,
1930    );
1931
1932    // The total number of slots processed
1933    let mut total_slots_processed = 0;
1934    // The total number of newly identified root slots
1935    let mut total_rooted_slots = 0;
1936
1937    let mut pending_slots = vec![];
1938    process_next_slots(
1939        &bank_forks
1940            .read()
1941            .unwrap()
1942            .get(start_slot_meta.slot)
1943            .unwrap(),
1944        start_slot_meta,
1945        blockstore,
1946        leader_schedule_cache,
1947        &mut pending_slots,
1948        opts,
1949    )?;
1950
1951    let on_halt_store_hash_raw_data_for_debug = opts.on_halt_store_hash_raw_data_for_debug;
1952    if Some(bank_forks.read().unwrap().root()) != opts.halt_at_slot {
1953        let recyclers = VerifyRecyclers::default();
1954        let mut all_banks = HashMap::new();
1955
1956        const STATUS_REPORT_INTERVAL: Duration = Duration::from_secs(2);
1957        let mut last_status_report = Instant::now();
1958        let mut slots_processed = 0;
1959        let mut txs = 0;
1960        let mut set_root_us = 0;
1961        let mut root_retain_us = 0;
1962        let mut process_single_slot_us = 0;
1963        let mut voting_us = 0;
1964
1965        while !pending_slots.is_empty() {
1966            timing.details.per_program_timings.clear();
1967            let (meta, bank, last_entry_hash) = pending_slots.pop().unwrap();
1968            let slot = bank.slot();
1969            if last_status_report.elapsed() > STATUS_REPORT_INTERVAL {
1970                let secs = last_status_report.elapsed().as_secs() as f32;
1971                let slots_per_sec = slots_processed as f32 / secs;
1972                let txs_per_sec = txs as f32 / secs;
1973                info!(
1974                    "processing ledger: slot={slot}, root_slot={root} slots={slots_processed}, \
1975                     slots/s={slots_per_sec}, txs/s={txs_per_sec}"
1976                );
1977                debug!(
1978                    "processing ledger timing: set_root_us={set_root_us}, \
1979                     root_retain_us={root_retain_us}, \
1980                     process_single_slot_us:{process_single_slot_us}, voting_us: {voting_us}"
1981                );
1982
1983                last_status_report = Instant::now();
1984                slots_processed = 0;
1985                txs = 0;
1986                set_root_us = 0;
1987                root_retain_us = 0;
1988                process_single_slot_us = 0;
1989                voting_us = 0;
1990            }
1991
1992            let mut progress = ConfirmationProgress::new(last_entry_hash);
1993            let mut m = Measure::start("process_single_slot");
1994            let bank = bank_forks.write().unwrap().insert_from_ledger(bank);
1995            if let Err(error) = process_single_slot(
1996                blockstore,
1997                &bank,
1998                replay_tx_thread_pool,
1999                opts,
2000                &recyclers,
2001                &mut progress,
2002                transaction_status_sender,
2003                block_meta_sender,
2004                entry_notification_sender,
2005                None,
2006                timing,
2007            ) {
2008                assert!(bank_forks.write().unwrap().remove(bank.slot()).is_some());
2009                if opts.abort_on_invalid_block {
2010                    Err(error)?
2011                }
2012                continue;
2013            }
2014            txs += progress.num_txs;
2015
2016            // Block must be frozen by this point; otherwise,
2017            // process_single_slot() would have errored above.
2018            assert!(bank.is_frozen());
2019            all_banks.insert(bank.slot(), bank.clone_with_scheduler());
2020            m.stop();
2021            process_single_slot_us += m.as_us();
2022
2023            let mut m = Measure::start("voting");
2024            // If we've reached the last known root in blockstore, start looking
2025            // for newer cluster confirmed roots
2026            let new_root_bank = {
2027                if bank_forks.read().unwrap().root() >= max_root {
2028                    supermajority_root_from_vote_accounts(
2029                        bank.total_epoch_stake(),
2030                        &bank.vote_accounts(),
2031                    ).and_then(|supermajority_root| {
2032                        if supermajority_root > root {
2033                            // If there's a cluster confirmed root greater than our last
2034                            // replayed root, then because the cluster confirmed root should
2035                            // be descended from our last root, it must exist in `all_banks`
2036                            let cluster_root_bank = all_banks.get(&supermajority_root).unwrap();
2037
2038                            // cluster root must be a descendant of our root, otherwise something
2039                            // is drastically wrong
2040                            assert!(cluster_root_bank.ancestors.contains_key(&root));
2041                            info!(
2042                                "blockstore processor found new cluster confirmed root: {}, observed in bank: {}",
2043                                cluster_root_bank.slot(), bank.slot()
2044                            );
2045
2046                            // Ensure cluster-confirmed root and parents are set as root in blockstore
2047                            let mut rooted_slots = vec![];
2048                            let mut new_root_bank = cluster_root_bank.clone_without_scheduler();
2049                            loop {
2050                                if new_root_bank.slot() == root { break; } // Found the last root in the chain, yay!
2051                                assert!(new_root_bank.slot() > root);
2052
2053                                rooted_slots.push((new_root_bank.slot(), Some(new_root_bank.hash())));
2054                                // As noted, the cluster confirmed root should be descended from
2055                                // our last root; therefore parent should be set
2056                                new_root_bank = new_root_bank.parent().unwrap();
2057                            }
2058                            total_rooted_slots += rooted_slots.len();
2059                            if blockstore.is_primary_access() {
2060                                blockstore
2061                                    .mark_slots_as_if_rooted_normally_at_startup(rooted_slots, true)
2062                                    .expect("Blockstore::mark_slots_as_if_rooted_normally_at_startup() should succeed");
2063                            }
2064                            Some(cluster_root_bank)
2065                        } else {
2066                            None
2067                        }
2068                    })
2069                } else if blockstore.is_root(slot) {
2070                    Some(&bank)
2071                } else {
2072                    None
2073                }
2074            };
2075            m.stop();
2076            voting_us += m.as_us();
2077
2078            if let Some(new_root_bank) = new_root_bank {
2079                let mut m = Measure::start("set_root");
2080                root = new_root_bank.slot();
2081
2082                leader_schedule_cache.set_root(new_root_bank);
2083                new_root_bank.prune_program_cache(root, new_root_bank.epoch());
2084                let _ = bank_forks.write().unwrap().set_root(
2085                    root,
2086                    accounts_background_request_sender,
2087                    None,
2088                )?;
2089                m.stop();
2090                set_root_us += m.as_us();
2091
2092                // Filter out all non descendants of the new root
2093                let mut m = Measure::start("filter pending slots");
2094                pending_slots
2095                    .retain(|(_, pending_bank, _)| pending_bank.ancestors.contains_key(&root));
2096                all_banks.retain(|_, bank| bank.ancestors.contains_key(&root));
2097                m.stop();
2098                root_retain_us += m.as_us();
2099            }
2100
2101            slots_processed += 1;
2102            total_slots_processed += 1;
2103
2104            trace!(
2105                "Bank for {}slot {} is complete",
2106                if root == slot { "root " } else { "" },
2107                slot,
2108            );
2109
2110            let done_processing = opts
2111                .halt_at_slot
2112                .map(|halt_at_slot| slot >= halt_at_slot)
2113                .unwrap_or(false);
2114            if done_processing {
2115                if opts.run_final_accounts_hash_calc {
2116                    bank.run_final_hash_calc(on_halt_store_hash_raw_data_for_debug);
2117                }
2118                break;
2119            }
2120
2121            process_next_slots(
2122                &bank,
2123                &meta,
2124                blockstore,
2125                leader_schedule_cache,
2126                &mut pending_slots,
2127                opts,
2128            )?;
2129        }
2130    } else if on_halt_store_hash_raw_data_for_debug {
2131        bank_forks
2132            .read()
2133            .unwrap()
2134            .root_bank()
2135            .run_final_hash_calc(on_halt_store_hash_raw_data_for_debug);
2136    }
2137
2138    Ok((total_slots_processed, total_rooted_slots))
2139}
2140
2141// `roots` is sorted largest to smallest by root slot
2142fn supermajority_root(roots: &[(Slot, u64)], total_epoch_stake: u64) -> Option<Slot> {
2143    if roots.is_empty() {
2144        return None;
2145    }
2146
2147    // Find latest root
2148    let mut total = 0;
2149    let mut prev_root = roots[0].0;
2150    for (root, stake) in roots.iter() {
2151        assert!(*root <= prev_root);
2152        total += stake;
2153        if total as f64 / total_epoch_stake as f64 > VOTE_THRESHOLD_SIZE {
2154            return Some(*root);
2155        }
2156        prev_root = *root;
2157    }
2158
2159    None
2160}
2161
2162fn supermajority_root_from_vote_accounts(
2163    total_epoch_stake: u64,
2164    vote_accounts: &VoteAccountsHashMap,
2165) -> Option<Slot> {
2166    let mut roots_stakes: Vec<(Slot, u64)> = vote_accounts
2167        .values()
2168        .filter_map(|(stake, account)| {
2169            if *stake == 0 {
2170                return None;
2171            }
2172
2173            Some((account.vote_state().root_slot?, *stake))
2174        })
2175        .collect();
2176
2177    // Sort from greatest to smallest slot
2178    roots_stakes.sort_unstable_by(|a, b| a.0.cmp(&b.0).reverse());
2179
2180    // Find latest root
2181    supermajority_root(&roots_stakes, total_epoch_stake)
2182}
2183
2184// Processes and replays the contents of a single slot, returns Error
2185// if failed to play the slot
2186#[allow(clippy::too_many_arguments)]
2187pub fn process_single_slot(
2188    blockstore: &Blockstore,
2189    bank: &BankWithScheduler,
2190    replay_tx_thread_pool: &ThreadPool,
2191    opts: &ProcessOptions,
2192    recyclers: &VerifyRecyclers,
2193    progress: &mut ConfirmationProgress,
2194    transaction_status_sender: Option<&TransactionStatusSender>,
2195    block_meta_sender: Option<&BlockMetaSender>,
2196    entry_notification_sender: Option<&EntryNotifierSender>,
2197    replay_vote_sender: Option<&ReplayVoteSender>,
2198    timing: &mut ExecuteTimings,
2199) -> result::Result<(), BlockstoreProcessorError> {
2200    let slot = bank.slot();
2201    // Mark corrupt slots as dead so validators don't replay this slot and
2202    // see AlreadyProcessed errors later in ReplayStage
2203    confirm_full_slot(
2204        blockstore,
2205        bank,
2206        replay_tx_thread_pool,
2207        opts,
2208        recyclers,
2209        progress,
2210        transaction_status_sender,
2211        entry_notification_sender,
2212        replay_vote_sender,
2213        timing,
2214    )
2215    .and_then(|()| {
2216        if let Some((result, completed_timings)) = bank.wait_for_completed_scheduler() {
2217            timing.accumulate(&completed_timings);
2218            result?
2219        }
2220        Ok(())
2221    })
2222    .map_err(|err| {
2223        warn!("slot {} failed to verify: {}", slot, err);
2224        if blockstore.is_primary_access() {
2225            blockstore
2226                .set_dead_slot(slot)
2227                .expect("Failed to mark slot as dead in blockstore");
2228        } else {
2229            info!(
2230                "Failed slot {} won't be marked dead due to being secondary blockstore access",
2231                slot
2232            );
2233        }
2234        err
2235    })?;
2236
2237    if let Some((result, _timings)) = bank.wait_for_completed_scheduler() {
2238        result?
2239    }
2240
2241    let block_id = blockstore.check_last_fec_set_and_get_block_id(slot, bank.hash(), &bank.feature_set)
2242        .inspect_err(|err| {
2243            warn!("slot {} failed last fec set checks: {}", slot, err);
2244            if blockstore.is_primary_access() {
2245                blockstore.set_dead_slot(slot).expect("Failed to mark slot as dead in blockstore");
2246            } else {
2247                info!("Failed last fec set checks slot {slot} won't be marked dead due to being secondary blockstore access");
2248            }
2249    })?;
2250    bank.set_block_id(block_id);
2251    bank.freeze(); // all banks handled by this routine are created from complete slots
2252
2253    if let Some(slot_callback) = &opts.slot_callback {
2254        slot_callback(bank);
2255    }
2256
2257    if blockstore.is_primary_access() {
2258        blockstore.insert_bank_hash(bank.slot(), bank.hash(), false);
2259    }
2260    send_block_meta(bank, block_meta_sender);
2261
2262    Ok(())
2263}
2264
2265#[allow(clippy::large_enum_variant)]
2266#[derive(Debug)]
2267pub enum TransactionStatusMessage {
2268    Batch(TransactionStatusBatch),
2269    Freeze(Slot),
2270}
2271
2272#[derive(Debug)]
2273pub struct TransactionStatusBatch {
2274    pub slot: Slot,
2275    pub transactions: Vec<SanitizedTransaction>,
2276    pub commit_results: Vec<TransactionCommitResult>,
2277    pub balances: TransactionBalancesSet,
2278    pub token_balances: TransactionTokenBalancesSet,
2279    pub transaction_indexes: Vec<usize>,
2280}
2281
2282#[derive(Clone, Debug)]
2283pub struct TransactionStatusSender {
2284    pub sender: Sender<TransactionStatusMessage>,
2285}
2286
2287impl TransactionStatusSender {
2288    pub fn send_transaction_status_batch(
2289        &self,
2290        slot: Slot,
2291        transactions: Vec<SanitizedTransaction>,
2292        commit_results: Vec<TransactionCommitResult>,
2293        balances: TransactionBalancesSet,
2294        token_balances: TransactionTokenBalancesSet,
2295        transaction_indexes: Vec<usize>,
2296    ) {
2297        if let Err(e) = self
2298            .sender
2299            .send(TransactionStatusMessage::Batch(TransactionStatusBatch {
2300                slot,
2301                transactions,
2302                commit_results,
2303                balances,
2304                token_balances,
2305                transaction_indexes,
2306            }))
2307        {
2308            trace!(
2309                "Slot {} transaction_status send batch failed: {:?}",
2310                slot,
2311                e
2312            );
2313        }
2314    }
2315
2316    pub fn send_transaction_status_freeze_message(&self, bank: &Arc<Bank>) {
2317        let slot = bank.slot();
2318        if let Err(e) = self.sender.send(TransactionStatusMessage::Freeze(slot)) {
2319            trace!(
2320                "Slot {} transaction_status send freeze message failed: {:?}",
2321                slot,
2322                e
2323            );
2324        }
2325    }
2326}
2327
2328pub type BlockMetaSender = Sender<Arc<Bank>>;
2329
2330pub fn send_block_meta(bank: &Arc<Bank>, block_meta_sender: Option<&BlockMetaSender>) {
2331    if let Some(block_meta_sender) = block_meta_sender {
2332        block_meta_sender
2333            .send(bank.clone())
2334            .unwrap_or_else(|err| warn!("block_meta_sender failed: {:?}", err));
2335    }
2336}
2337
2338// used for tests only
2339pub fn fill_blockstore_slot_with_ticks(
2340    blockstore: &Blockstore,
2341    ticks_per_slot: u64,
2342    slot: u64,
2343    parent_slot: u64,
2344    last_entry_hash: Hash,
2345) -> Hash {
2346    // Only slot 0 can be equal to the parent_slot
2347    assert!(slot.saturating_sub(1) >= parent_slot);
2348    let num_slots = (slot - parent_slot).max(1);
2349    let entries = create_ticks(num_slots * ticks_per_slot, 0, last_entry_hash);
2350    let last_entry_hash = entries.last().unwrap().hash;
2351
2352    blockstore
2353        .write_entries(
2354            slot,
2355            0,
2356            0,
2357            ticks_per_slot,
2358            Some(parent_slot),
2359            true,
2360            &Arc::new(Keypair::new()),
2361            entries,
2362            0,
2363        )
2364        .unwrap();
2365
2366    last_entry_hash
2367}
2368
2369#[cfg(test)]
2370pub mod tests {
2371    use {
2372        super::*,
2373        crate::{
2374            blockstore_options::{AccessType, BlockstoreOptions},
2375            genesis_utils::{
2376                create_genesis_config, create_genesis_config_with_leader,
2377                create_genesis_config_with_mint_keypair, GenesisConfigInfo,
2378            },
2379        },
2380        assert_matches::assert_matches,
2381        rand::{thread_rng, Rng},
2382        clone_solana_cost_model::transaction_cost::TransactionCost,
2383        clone_solana_entry::entry::{create_ticks, next_entry, next_entry_mut},
2384        clone_solana_program_runtime::declare_process_instruction,
2385        clone_solana_runtime::{
2386            bank::bank_hash_details::SlotDetails,
2387            genesis_utils::{
2388                self, create_genesis_config_with_vote_accounts, ValidatorVoteKeypairs,
2389            },
2390            installed_scheduler_pool::{
2391                MockInstalledScheduler, MockUninstalledScheduler, SchedulerAborted,
2392                SchedulingContext,
2393            },
2394        },
2395        clone_solana_sdk::{
2396            account::{AccountSharedData, WritableAccount},
2397            epoch_schedule::EpochSchedule,
2398            hash::Hash,
2399            instruction::{Instruction, InstructionError},
2400            native_token::LAMPORTS_PER_SOL,
2401            pubkey::Pubkey,
2402            signature::{Keypair, Signer},
2403            signer::SeedDerivable,
2404            system_instruction::SystemError,
2405            system_transaction,
2406            transaction::{Transaction, TransactionError},
2407        },
2408        clone_solana_svm::{
2409            account_loader::LoadedTransaction,
2410            transaction_execution_result::{ExecutedTransaction, TransactionExecutionDetails},
2411            transaction_processing_result::ProcessedTransaction,
2412            transaction_processor::ExecutionRecordingConfig,
2413        },
2414        clone_solana_vote::vote_account::VoteAccount,
2415        clone_solana_vote_program::{
2416            self,
2417            vote_state::{TowerSync, VoteState, VoteStateVersions, MAX_LOCKOUT_HISTORY},
2418            vote_transaction,
2419        },
2420        std::{collections::BTreeSet, slice, sync::RwLock},
2421        test_case::{test_case, test_matrix},
2422        trees::tr,
2423    };
2424
2425    // Convenience wrapper to optionally process blockstore with Secondary access.
2426    //
2427    // Setting up the ledger for a test requires Primary access as items will need to be inserted.
2428    // However, once a Secondary access has been opened, it won't automaticaly see updates made by
2429    // the Primary access. So, open (and close) the Secondary access within this function to ensure
2430    // that "stale" Secondary accesses don't propagate.
2431    fn test_process_blockstore_with_custom_options(
2432        genesis_config: &GenesisConfig,
2433        blockstore: &Blockstore,
2434        opts: &ProcessOptions,
2435        access_type: AccessType,
2436    ) -> (Arc<RwLock<BankForks>>, LeaderScheduleCache) {
2437        match access_type {
2438            AccessType::Primary | AccessType::PrimaryForMaintenance => {
2439                // Attempting to open a second Primary access would fail, so
2440                // just pass the original session if it is a Primary variant
2441                test_process_blockstore(genesis_config, blockstore, opts, Arc::default())
2442            }
2443            AccessType::Secondary => {
2444                let secondary_blockstore = Blockstore::open_with_options(
2445                    blockstore.ledger_path(),
2446                    BlockstoreOptions {
2447                        access_type,
2448                        ..BlockstoreOptions::default()
2449                    },
2450                )
2451                .expect("Unable to open access to blockstore");
2452                test_process_blockstore(genesis_config, &secondary_blockstore, opts, Arc::default())
2453            }
2454        }
2455    }
2456
2457    fn process_entries_for_tests_without_scheduler(
2458        bank: &Arc<Bank>,
2459        entries: Vec<Entry>,
2460    ) -> Result<()> {
2461        process_entries_for_tests(
2462            &BankWithScheduler::new_without_scheduler(bank.clone()),
2463            entries,
2464            None,
2465            None,
2466        )
2467    }
2468
2469    #[test]
2470    fn test_process_blockstore_with_missing_hashes() {
2471        do_test_process_blockstore_with_missing_hashes(AccessType::Primary);
2472    }
2473
2474    #[test]
2475    fn test_process_blockstore_with_missing_hashes_secondary_access() {
2476        do_test_process_blockstore_with_missing_hashes(AccessType::Secondary);
2477    }
2478
2479    // Intentionally make slot 1 faulty and ensure that processing sees it as dead
2480    fn do_test_process_blockstore_with_missing_hashes(blockstore_access_type: AccessType) {
2481        clone_solana_logger::setup();
2482
2483        let hashes_per_tick = 2;
2484        let GenesisConfigInfo {
2485            mut genesis_config, ..
2486        } = create_genesis_config(10_000);
2487        genesis_config.poh_config.hashes_per_tick = Some(hashes_per_tick);
2488        let ticks_per_slot = genesis_config.ticks_per_slot;
2489
2490        let (ledger_path, blockhash) = create_new_tmp_ledger_auto_delete!(&genesis_config);
2491        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
2492
2493        let parent_slot = 0;
2494        let slot = 1;
2495        let entries = create_ticks(ticks_per_slot, hashes_per_tick - 1, blockhash);
2496        assert_matches!(
2497            blockstore.write_entries(
2498                slot,
2499                0,
2500                0,
2501                ticks_per_slot,
2502                Some(parent_slot),
2503                true,
2504                &Arc::new(Keypair::new()),
2505                entries,
2506                0,
2507            ),
2508            Ok(_)
2509        );
2510
2511        let (bank_forks, ..) = test_process_blockstore_with_custom_options(
2512            &genesis_config,
2513            &blockstore,
2514            &ProcessOptions {
2515                run_verification: true,
2516                ..ProcessOptions::default()
2517            },
2518            blockstore_access_type.clone(),
2519        );
2520        assert_eq!(frozen_bank_slots(&bank_forks.read().unwrap()), vec![0]);
2521
2522        let dead_slots: Vec<Slot> = blockstore.dead_slots_iterator(0).unwrap().collect();
2523        match blockstore_access_type {
2524            // Secondary access is immutable so even though a dead slot
2525            // will be identified, it won't actually be marked dead.
2526            AccessType::Secondary => {
2527                assert_eq!(dead_slots.len(), 0);
2528            }
2529            AccessType::Primary | AccessType::PrimaryForMaintenance => {
2530                assert_eq!(&dead_slots, &[1]);
2531            }
2532        }
2533    }
2534
2535    #[test]
2536    fn test_process_blockstore_with_invalid_slot_tick_count() {
2537        clone_solana_logger::setup();
2538
2539        let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
2540        let ticks_per_slot = genesis_config.ticks_per_slot;
2541
2542        // Create a new ledger with slot 0 full of ticks
2543        let (ledger_path, blockhash) = create_new_tmp_ledger_auto_delete!(&genesis_config);
2544        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
2545
2546        // Write slot 1 with one tick missing
2547        let parent_slot = 0;
2548        let slot = 1;
2549        let entries = create_ticks(ticks_per_slot - 1, 0, blockhash);
2550        assert_matches!(
2551            blockstore.write_entries(
2552                slot,
2553                0,
2554                0,
2555                ticks_per_slot,
2556                Some(parent_slot),
2557                true,
2558                &Arc::new(Keypair::new()),
2559                entries,
2560                0,
2561            ),
2562            Ok(_)
2563        );
2564
2565        // Should return slot 0, the last slot on the fork that is valid
2566        let (bank_forks, ..) = test_process_blockstore(
2567            &genesis_config,
2568            &blockstore,
2569            &ProcessOptions {
2570                run_verification: true,
2571                ..ProcessOptions::default()
2572            },
2573            Arc::default(),
2574        );
2575        assert_eq!(frozen_bank_slots(&bank_forks.read().unwrap()), vec![0]);
2576
2577        // Write slot 2 fully
2578        let _last_slot2_entry_hash =
2579            fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 2, 0, blockhash);
2580
2581        let (bank_forks, ..) = test_process_blockstore(
2582            &genesis_config,
2583            &blockstore,
2584            &ProcessOptions {
2585                run_verification: true,
2586                ..ProcessOptions::default()
2587            },
2588            Arc::default(),
2589        );
2590
2591        // One valid fork, one bad fork.  process_blockstore() should only return the valid fork
2592        assert_eq!(frozen_bank_slots(&bank_forks.read().unwrap()), vec![0, 2]);
2593        assert_eq!(bank_forks.read().unwrap().working_bank().slot(), 2);
2594        assert_eq!(bank_forks.read().unwrap().root(), 0);
2595    }
2596
2597    #[test]
2598    fn test_process_blockstore_with_slot_with_trailing_entry() {
2599        clone_solana_logger::setup();
2600
2601        let GenesisConfigInfo {
2602            mint_keypair,
2603            genesis_config,
2604            ..
2605        } = create_genesis_config(10_000);
2606        let ticks_per_slot = genesis_config.ticks_per_slot;
2607
2608        let (ledger_path, blockhash) = create_new_tmp_ledger_auto_delete!(&genesis_config);
2609        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
2610
2611        let mut entries = create_ticks(ticks_per_slot, 0, blockhash);
2612        let trailing_entry = {
2613            let keypair = Keypair::new();
2614            let tx = system_transaction::transfer(&mint_keypair, &keypair.pubkey(), 1, blockhash);
2615            next_entry(&blockhash, 1, vec![tx])
2616        };
2617        entries.push(trailing_entry);
2618
2619        // Tricks blockstore into writing the trailing entry by lying that there is one more tick
2620        // per slot.
2621        let parent_slot = 0;
2622        let slot = 1;
2623        assert_matches!(
2624            blockstore.write_entries(
2625                slot,
2626                0,
2627                0,
2628                ticks_per_slot + 1,
2629                Some(parent_slot),
2630                true,
2631                &Arc::new(Keypair::new()),
2632                entries,
2633                0,
2634            ),
2635            Ok(_)
2636        );
2637
2638        let opts = ProcessOptions {
2639            run_verification: true,
2640            accounts_db_test_hash_calculation: true,
2641            ..ProcessOptions::default()
2642        };
2643        let (bank_forks, ..) =
2644            test_process_blockstore(&genesis_config, &blockstore, &opts, Arc::default());
2645        assert_eq!(frozen_bank_slots(&bank_forks.read().unwrap()), vec![0]);
2646    }
2647
2648    #[test]
2649    fn test_process_blockstore_with_incomplete_slot() {
2650        clone_solana_logger::setup();
2651
2652        let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
2653        let ticks_per_slot = genesis_config.ticks_per_slot;
2654
2655        /*
2656          Build a blockstore in the ledger with the following fork structure:
2657
2658               slot 0 (all ticks)
2659                 |
2660               slot 1 (all ticks but one)
2661                 |
2662               slot 2 (all ticks)
2663
2664           where slot 1 is incomplete (missing 1 tick at the end)
2665        */
2666
2667        // Create a new ledger with slot 0 full of ticks
2668        let (ledger_path, mut blockhash) = create_new_tmp_ledger_auto_delete!(&genesis_config);
2669        debug!("ledger_path: {:?}", ledger_path);
2670
2671        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
2672
2673        // Write slot 1
2674        // slot 1, points at slot 0.  Missing one tick
2675        {
2676            let parent_slot = 0;
2677            let slot = 1;
2678            let mut entries = create_ticks(ticks_per_slot, 0, blockhash);
2679            blockhash = entries.last().unwrap().hash;
2680
2681            // throw away last one
2682            entries.pop();
2683
2684            assert_matches!(
2685                blockstore.write_entries(
2686                    slot,
2687                    0,
2688                    0,
2689                    ticks_per_slot,
2690                    Some(parent_slot),
2691                    false,
2692                    &Arc::new(Keypair::new()),
2693                    entries,
2694                    0,
2695                ),
2696                Ok(_)
2697            );
2698        }
2699
2700        // slot 2, points at slot 1
2701        fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 2, 1, blockhash);
2702
2703        let opts = ProcessOptions {
2704            run_verification: true,
2705            accounts_db_test_hash_calculation: true,
2706            ..ProcessOptions::default()
2707        };
2708        let (bank_forks, ..) =
2709            test_process_blockstore(&genesis_config, &blockstore, &opts, Arc::default());
2710
2711        assert_eq!(frozen_bank_slots(&bank_forks.read().unwrap()), vec![0]); // slot 1 isn't "full", we stop at slot zero
2712
2713        /* Add a complete slot such that the store looks like:
2714
2715                                 slot 0 (all ticks)
2716                               /                  \
2717               slot 1 (all ticks but one)        slot 3 (all ticks)
2718                      |
2719               slot 2 (all ticks)
2720        */
2721        let opts = ProcessOptions {
2722            run_verification: true,
2723            accounts_db_test_hash_calculation: true,
2724            ..ProcessOptions::default()
2725        };
2726        fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 3, 0, blockhash);
2727        // Slot 0 should not show up in the ending bank_forks_info
2728        let (bank_forks, ..) =
2729            test_process_blockstore(&genesis_config, &blockstore, &opts, Arc::default());
2730
2731        // slot 1 isn't "full", we stop at slot zero
2732        assert_eq!(frozen_bank_slots(&bank_forks.read().unwrap()), vec![0, 3]);
2733    }
2734
2735    #[test]
2736    fn test_process_blockstore_with_two_forks_and_squash() {
2737        clone_solana_logger::setup();
2738
2739        let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
2740        let ticks_per_slot = genesis_config.ticks_per_slot;
2741
2742        // Create a new ledger with slot 0 full of ticks
2743        let (ledger_path, blockhash) = create_new_tmp_ledger_auto_delete!(&genesis_config);
2744        debug!("ledger_path: {:?}", ledger_path);
2745        let mut last_entry_hash = blockhash;
2746
2747        /*
2748            Build a blockstore in the ledger with the following fork structure:
2749
2750                 slot 0
2751                   |
2752                 slot 1
2753                 /   \
2754            slot 2   |
2755               /     |
2756            slot 3   |
2757                     |
2758                   slot 4 <-- set_root(true)
2759
2760        */
2761        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
2762
2763        // Fork 1, ending at slot 3
2764        let last_slot1_entry_hash =
2765            fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 1, 0, last_entry_hash);
2766        last_entry_hash = fill_blockstore_slot_with_ticks(
2767            &blockstore,
2768            ticks_per_slot,
2769            2,
2770            1,
2771            last_slot1_entry_hash,
2772        );
2773        let last_fork1_entry_hash =
2774            fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 3, 2, last_entry_hash);
2775
2776        // Fork 2, ending at slot 4
2777        let last_fork2_entry_hash = fill_blockstore_slot_with_ticks(
2778            &blockstore,
2779            ticks_per_slot,
2780            4,
2781            1,
2782            last_slot1_entry_hash,
2783        );
2784
2785        info!("last_fork1_entry.hash: {:?}", last_fork1_entry_hash);
2786        info!("last_fork2_entry.hash: {:?}", last_fork2_entry_hash);
2787
2788        blockstore.set_roots([0, 1, 4].iter()).unwrap();
2789
2790        let opts = ProcessOptions {
2791            run_verification: true,
2792            accounts_db_test_hash_calculation: true,
2793            ..ProcessOptions::default()
2794        };
2795        let (bank_forks, ..) =
2796            test_process_blockstore(&genesis_config, &blockstore, &opts, Arc::default());
2797        let bank_forks = bank_forks.read().unwrap();
2798
2799        // One fork, other one is ignored b/c not a descendant of the root
2800        assert_eq!(frozen_bank_slots(&bank_forks), vec![4]);
2801
2802        assert!(&bank_forks[4]
2803            .parents()
2804            .iter()
2805            .map(|bank| bank.slot())
2806            .next()
2807            .is_none());
2808
2809        // Ensure bank_forks holds the right banks
2810        verify_fork_infos(&bank_forks);
2811
2812        assert_eq!(bank_forks.root(), 4);
2813    }
2814
2815    #[test]
2816    fn test_process_blockstore_with_two_forks() {
2817        clone_solana_logger::setup();
2818
2819        let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
2820        let ticks_per_slot = genesis_config.ticks_per_slot;
2821
2822        // Create a new ledger with slot 0 full of ticks
2823        let (ledger_path, blockhash) = create_new_tmp_ledger_auto_delete!(&genesis_config);
2824        debug!("ledger_path: {:?}", ledger_path);
2825        let mut last_entry_hash = blockhash;
2826
2827        /*
2828            Build a blockstore in the ledger with the following fork structure:
2829
2830                 slot 0
2831                   |
2832                 slot 1  <-- set_root(true)
2833                 /   \
2834            slot 2   |
2835               /     |
2836            slot 3   |
2837                     |
2838                   slot 4
2839
2840        */
2841        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
2842
2843        // Fork 1, ending at slot 3
2844        let last_slot1_entry_hash =
2845            fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 1, 0, last_entry_hash);
2846        last_entry_hash = fill_blockstore_slot_with_ticks(
2847            &blockstore,
2848            ticks_per_slot,
2849            2,
2850            1,
2851            last_slot1_entry_hash,
2852        );
2853        let last_fork1_entry_hash =
2854            fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 3, 2, last_entry_hash);
2855
2856        // Fork 2, ending at slot 4
2857        let last_fork2_entry_hash = fill_blockstore_slot_with_ticks(
2858            &blockstore,
2859            ticks_per_slot,
2860            4,
2861            1,
2862            last_slot1_entry_hash,
2863        );
2864
2865        info!("last_fork1_entry.hash: {:?}", last_fork1_entry_hash);
2866        info!("last_fork2_entry.hash: {:?}", last_fork2_entry_hash);
2867
2868        blockstore.set_roots([0, 1].iter()).unwrap();
2869
2870        let opts = ProcessOptions {
2871            run_verification: true,
2872            accounts_db_test_hash_calculation: true,
2873            ..ProcessOptions::default()
2874        };
2875        let (bank_forks, ..) =
2876            test_process_blockstore(&genesis_config, &blockstore, &opts, Arc::default());
2877        let bank_forks = bank_forks.read().unwrap();
2878
2879        assert_eq!(frozen_bank_slots(&bank_forks), vec![1, 2, 3, 4]);
2880        assert_eq!(bank_forks.working_bank().slot(), 4);
2881        assert_eq!(bank_forks.root(), 1);
2882
2883        assert_eq!(
2884            &bank_forks[3]
2885                .parents()
2886                .iter()
2887                .map(|bank| bank.slot())
2888                .collect::<Vec<_>>(),
2889            &[2, 1]
2890        );
2891        assert_eq!(
2892            &bank_forks[4]
2893                .parents()
2894                .iter()
2895                .map(|bank| bank.slot())
2896                .collect::<Vec<_>>(),
2897            &[1]
2898        );
2899
2900        assert_eq!(bank_forks.root(), 1);
2901
2902        // Ensure bank_forks holds the right banks
2903        verify_fork_infos(&bank_forks);
2904    }
2905
2906    #[test]
2907    fn test_process_blockstore_with_dead_slot() {
2908        clone_solana_logger::setup();
2909
2910        let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
2911        let ticks_per_slot = genesis_config.ticks_per_slot;
2912        let (ledger_path, blockhash) = create_new_tmp_ledger_auto_delete!(&genesis_config);
2913        debug!("ledger_path: {:?}", ledger_path);
2914
2915        /*
2916                   slot 0
2917                     |
2918                   slot 1
2919                  /     \
2920                 /       \
2921           slot 2 (dead)  \
2922                           \
2923                        slot 3
2924        */
2925        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
2926        let slot1_blockhash =
2927            fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 1, 0, blockhash);
2928        fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 2, 1, slot1_blockhash);
2929        blockstore.set_dead_slot(2).unwrap();
2930        fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 3, 1, slot1_blockhash);
2931
2932        let (bank_forks, ..) = test_process_blockstore(
2933            &genesis_config,
2934            &blockstore,
2935            &ProcessOptions::default(),
2936            Arc::default(),
2937        );
2938        let bank_forks = bank_forks.read().unwrap();
2939
2940        assert_eq!(frozen_bank_slots(&bank_forks), vec![0, 1, 3]);
2941        assert_eq!(bank_forks.working_bank().slot(), 3);
2942        assert_eq!(
2943            &bank_forks[3]
2944                .parents()
2945                .iter()
2946                .map(|bank| bank.slot())
2947                .collect::<Vec<_>>(),
2948            &[1, 0]
2949        );
2950        verify_fork_infos(&bank_forks);
2951    }
2952
2953    #[test]
2954    fn test_process_blockstore_with_dead_child() {
2955        clone_solana_logger::setup();
2956
2957        let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
2958        let ticks_per_slot = genesis_config.ticks_per_slot;
2959        let (ledger_path, blockhash) = create_new_tmp_ledger_auto_delete!(&genesis_config);
2960        debug!("ledger_path: {:?}", ledger_path);
2961
2962        /*
2963                   slot 0
2964                     |
2965                   slot 1
2966                  /     \
2967                 /       \
2968              slot 2      \
2969               /           \
2970           slot 4 (dead)   slot 3
2971        */
2972        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
2973        let slot1_blockhash =
2974            fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 1, 0, blockhash);
2975        let slot2_blockhash =
2976            fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 2, 1, slot1_blockhash);
2977        fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 4, 2, slot2_blockhash);
2978        blockstore.set_dead_slot(4).unwrap();
2979        fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 3, 1, slot1_blockhash);
2980
2981        let (bank_forks, ..) = test_process_blockstore(
2982            &genesis_config,
2983            &blockstore,
2984            &ProcessOptions::default(),
2985            Arc::default(),
2986        );
2987        let bank_forks = bank_forks.read().unwrap();
2988
2989        // Should see the parent of the dead child
2990        assert_eq!(frozen_bank_slots(&bank_forks), vec![0, 1, 2, 3]);
2991        assert_eq!(bank_forks.working_bank().slot(), 3);
2992
2993        assert_eq!(
2994            &bank_forks[3]
2995                .parents()
2996                .iter()
2997                .map(|bank| bank.slot())
2998                .collect::<Vec<_>>(),
2999            &[1, 0]
3000        );
3001        assert_eq!(
3002            &bank_forks[2]
3003                .parents()
3004                .iter()
3005                .map(|bank| bank.slot())
3006                .collect::<Vec<_>>(),
3007            &[1, 0]
3008        );
3009        assert_eq!(bank_forks.working_bank().slot(), 3);
3010        verify_fork_infos(&bank_forks);
3011    }
3012
3013    #[test]
3014    fn test_root_with_all_dead_children() {
3015        clone_solana_logger::setup();
3016
3017        let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
3018        let ticks_per_slot = genesis_config.ticks_per_slot;
3019        let (ledger_path, blockhash) = create_new_tmp_ledger_auto_delete!(&genesis_config);
3020        debug!("ledger_path: {:?}", ledger_path);
3021
3022        /*
3023                   slot 0
3024                 /        \
3025                /          \
3026           slot 1 (dead)  slot 2 (dead)
3027        */
3028        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
3029        fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 1, 0, blockhash);
3030        fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 2, 0, blockhash);
3031        blockstore.set_dead_slot(1).unwrap();
3032        blockstore.set_dead_slot(2).unwrap();
3033        let (bank_forks, ..) = test_process_blockstore(
3034            &genesis_config,
3035            &blockstore,
3036            &ProcessOptions::default(),
3037            Arc::default(),
3038        );
3039        let bank_forks = bank_forks.read().unwrap();
3040
3041        // Should see only the parent of the dead children
3042        assert_eq!(frozen_bank_slots(&bank_forks), vec![0]);
3043        verify_fork_infos(&bank_forks);
3044    }
3045
3046    #[test]
3047    fn test_process_blockstore_epoch_boundary_root() {
3048        clone_solana_logger::setup();
3049
3050        let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
3051        let ticks_per_slot = genesis_config.ticks_per_slot;
3052
3053        // Create a new ledger with slot 0 full of ticks
3054        let (ledger_path, blockhash) = create_new_tmp_ledger_auto_delete!(&genesis_config);
3055        let mut last_entry_hash = blockhash;
3056
3057        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
3058
3059        // Let `last_slot` be the number of slots in the first two epochs
3060        let epoch_schedule = get_epoch_schedule(&genesis_config);
3061        let last_slot = epoch_schedule.get_last_slot_in_epoch(1);
3062
3063        // Create a single chain of slots with all indexes in the range [0, v + 1]
3064        for i in 1..=last_slot + 1 {
3065            last_entry_hash = fill_blockstore_slot_with_ticks(
3066                &blockstore,
3067                ticks_per_slot,
3068                i,
3069                i - 1,
3070                last_entry_hash,
3071            );
3072        }
3073
3074        // Set a root on the last slot of the last confirmed epoch
3075        let rooted_slots: Vec<Slot> = (0..=last_slot).collect();
3076        blockstore.set_roots(rooted_slots.iter()).unwrap();
3077
3078        // Set a root on the next slot of the confirmed epoch
3079        blockstore
3080            .set_roots(std::iter::once(&(last_slot + 1)))
3081            .unwrap();
3082
3083        // Check that we can properly restart the ledger / leader scheduler doesn't fail
3084        let opts = ProcessOptions {
3085            run_verification: true,
3086            accounts_db_test_hash_calculation: true,
3087            ..ProcessOptions::default()
3088        };
3089        let (bank_forks, ..) =
3090            test_process_blockstore(&genesis_config, &blockstore, &opts, Arc::default());
3091        let bank_forks = bank_forks.read().unwrap();
3092
3093        // There is one fork, head is last_slot + 1
3094        assert_eq!(frozen_bank_slots(&bank_forks), vec![last_slot + 1]);
3095
3096        // The latest root should have purged all its parents
3097        assert!(&bank_forks[last_slot + 1]
3098            .parents()
3099            .iter()
3100            .map(|bank| bank.slot())
3101            .next()
3102            .is_none());
3103    }
3104
3105    #[test]
3106    fn test_first_err() {
3107        assert_eq!(first_err(&[Ok(())]), Ok(()));
3108        assert_eq!(
3109            first_err(&[Ok(()), Err(TransactionError::AlreadyProcessed)]),
3110            Err(TransactionError::AlreadyProcessed)
3111        );
3112        assert_eq!(
3113            first_err(&[
3114                Ok(()),
3115                Err(TransactionError::AlreadyProcessed),
3116                Err(TransactionError::AccountInUse)
3117            ]),
3118            Err(TransactionError::AlreadyProcessed)
3119        );
3120        assert_eq!(
3121            first_err(&[
3122                Ok(()),
3123                Err(TransactionError::AccountInUse),
3124                Err(TransactionError::AlreadyProcessed)
3125            ]),
3126            Err(TransactionError::AccountInUse)
3127        );
3128        assert_eq!(
3129            first_err(&[
3130                Err(TransactionError::AccountInUse),
3131                Ok(()),
3132                Err(TransactionError::AlreadyProcessed)
3133            ]),
3134            Err(TransactionError::AccountInUse)
3135        );
3136    }
3137
3138    #[test]
3139    fn test_process_empty_entry_is_registered() {
3140        clone_solana_logger::setup();
3141
3142        let GenesisConfigInfo {
3143            genesis_config,
3144            mint_keypair,
3145            ..
3146        } = create_genesis_config(2);
3147        let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
3148        let keypair = Keypair::new();
3149        let slot_entries = create_ticks(genesis_config.ticks_per_slot, 1, genesis_config.hash());
3150        let tx = system_transaction::transfer(
3151            &mint_keypair,
3152            &keypair.pubkey(),
3153            1,
3154            slot_entries.last().unwrap().hash,
3155        );
3156
3157        // First, ensure the TX is rejected because of the unregistered last ID
3158        assert_eq!(
3159            bank.process_transaction(&tx),
3160            Err(TransactionError::BlockhashNotFound)
3161        );
3162
3163        // Now ensure the TX is accepted despite pointing to the ID of an empty entry.
3164        process_entries_for_tests_without_scheduler(&bank, slot_entries).unwrap();
3165        assert_eq!(bank.process_transaction(&tx), Ok(()));
3166    }
3167
3168    #[test]
3169    fn test_process_ledger_simple() {
3170        clone_solana_logger::setup();
3171        let leader_pubkey = clone_solana_pubkey::new_rand();
3172        let mint = 100;
3173        let hashes_per_tick = 10;
3174        let GenesisConfigInfo {
3175            mut genesis_config,
3176            mint_keypair,
3177            ..
3178        } = create_genesis_config_with_leader(mint, &leader_pubkey, 50);
3179        genesis_config.poh_config.hashes_per_tick = Some(hashes_per_tick);
3180        let (ledger_path, mut last_entry_hash) =
3181            create_new_tmp_ledger_auto_delete!(&genesis_config);
3182        debug!("ledger_path: {:?}", ledger_path);
3183
3184        let deducted_from_mint = 3;
3185        let mut entries = vec![];
3186        let blockhash = genesis_config.hash();
3187        for _ in 0..deducted_from_mint {
3188            // Transfer one token from the mint to a random account
3189            let keypair = Keypair::new();
3190            let tx = system_transaction::transfer(&mint_keypair, &keypair.pubkey(), 1, blockhash);
3191            let entry = next_entry_mut(&mut last_entry_hash, 1, vec![tx]);
3192            entries.push(entry);
3193
3194            // Add a second Transaction that will produce a
3195            // InstructionError<0, ResultWithNegativeLamports> error when processed
3196            let keypair2 = Keypair::new();
3197            let tx =
3198                system_transaction::transfer(&mint_keypair, &keypair2.pubkey(), 101, blockhash);
3199            let entry = next_entry_mut(&mut last_entry_hash, 1, vec![tx]);
3200            entries.push(entry);
3201        }
3202
3203        let remaining_hashes = hashes_per_tick - entries.len() as u64;
3204        let tick_entry = next_entry_mut(&mut last_entry_hash, remaining_hashes, vec![]);
3205        entries.push(tick_entry);
3206
3207        // Fill up the rest of slot 1 with ticks
3208        entries.extend(create_ticks(
3209            genesis_config.ticks_per_slot - 1,
3210            genesis_config.poh_config.hashes_per_tick.unwrap(),
3211            last_entry_hash,
3212        ));
3213        let last_blockhash = entries.last().unwrap().hash;
3214
3215        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
3216        blockstore
3217            .write_entries(
3218                1,
3219                0,
3220                0,
3221                genesis_config.ticks_per_slot,
3222                None,
3223                true,
3224                &Arc::new(Keypair::new()),
3225                entries,
3226                0,
3227            )
3228            .unwrap();
3229        let opts = ProcessOptions {
3230            run_verification: true,
3231            accounts_db_test_hash_calculation: true,
3232            ..ProcessOptions::default()
3233        };
3234        let (bank_forks, ..) =
3235            test_process_blockstore(&genesis_config, &blockstore, &opts, Arc::default());
3236        let bank_forks = bank_forks.read().unwrap();
3237
3238        assert_eq!(frozen_bank_slots(&bank_forks), vec![0, 1]);
3239        assert_eq!(bank_forks.root(), 0);
3240        assert_eq!(bank_forks.working_bank().slot(), 1);
3241
3242        let bank = bank_forks[1].clone();
3243        assert_eq!(
3244            bank.get_balance(&mint_keypair.pubkey()),
3245            mint - deducted_from_mint
3246        );
3247        assert_eq!(bank.tick_height(), 2 * genesis_config.ticks_per_slot);
3248        assert_eq!(bank.last_blockhash(), last_blockhash);
3249    }
3250
3251    #[test]
3252    fn test_process_ledger_with_one_tick_per_slot() {
3253        let GenesisConfigInfo {
3254            mut genesis_config, ..
3255        } = create_genesis_config(123);
3256        genesis_config.ticks_per_slot = 1;
3257        let (ledger_path, _blockhash) = create_new_tmp_ledger_auto_delete!(&genesis_config);
3258
3259        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
3260        let opts = ProcessOptions {
3261            run_verification: true,
3262            accounts_db_test_hash_calculation: true,
3263            ..ProcessOptions::default()
3264        };
3265        let (bank_forks, ..) =
3266            test_process_blockstore(&genesis_config, &blockstore, &opts, Arc::default());
3267        let bank_forks = bank_forks.read().unwrap();
3268
3269        assert_eq!(frozen_bank_slots(&bank_forks), vec![0]);
3270        let bank = bank_forks[0].clone();
3271        assert_eq!(bank.tick_height(), 1);
3272    }
3273
3274    #[test]
3275    fn test_process_ledger_options_full_leader_cache() {
3276        let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(123);
3277        let (ledger_path, _blockhash) = create_new_tmp_ledger_auto_delete!(&genesis_config);
3278
3279        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
3280        let opts = ProcessOptions {
3281            full_leader_cache: true,
3282            accounts_db_test_hash_calculation: true,
3283            ..ProcessOptions::default()
3284        };
3285        let (_bank_forks, leader_schedule) =
3286            test_process_blockstore(&genesis_config, &blockstore, &opts, Arc::default());
3287        assert_eq!(leader_schedule.max_schedules(), usize::MAX);
3288    }
3289
3290    #[test]
3291    fn test_process_entries_tick() {
3292        let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(1000);
3293        let bank = Arc::new(Bank::new_for_tests(&genesis_config));
3294
3295        // ensure bank can process a tick
3296        assert_eq!(bank.tick_height(), 0);
3297        let tick = next_entry(&genesis_config.hash(), 1, vec![]);
3298        assert_eq!(
3299            process_entries_for_tests_without_scheduler(&bank, vec![tick]),
3300            Ok(())
3301        );
3302        assert_eq!(bank.tick_height(), 1);
3303    }
3304
3305    #[test]
3306    fn test_process_entries_2_entries_collision() {
3307        let GenesisConfigInfo {
3308            genesis_config,
3309            mint_keypair,
3310            ..
3311        } = create_genesis_config(1000);
3312        let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
3313        let keypair1 = Keypair::new();
3314        let keypair2 = Keypair::new();
3315
3316        let blockhash = bank.last_blockhash();
3317
3318        // ensure bank can process 2 entries that have a common account and no tick is registered
3319        let tx = system_transaction::transfer(
3320            &mint_keypair,
3321            &keypair1.pubkey(),
3322            2,
3323            bank.last_blockhash(),
3324        );
3325        let entry_1 = next_entry(&blockhash, 1, vec![tx]);
3326        let tx = system_transaction::transfer(
3327            &mint_keypair,
3328            &keypair2.pubkey(),
3329            2,
3330            bank.last_blockhash(),
3331        );
3332        let entry_2 = next_entry(&entry_1.hash, 1, vec![tx]);
3333        assert_eq!(
3334            process_entries_for_tests_without_scheduler(&bank, vec![entry_1, entry_2]),
3335            Ok(())
3336        );
3337        assert_eq!(bank.get_balance(&keypair1.pubkey()), 2);
3338        assert_eq!(bank.get_balance(&keypair2.pubkey()), 2);
3339        assert_eq!(bank.last_blockhash(), blockhash);
3340    }
3341
3342    #[test]
3343    fn test_process_entries_2_txes_collision() {
3344        let GenesisConfigInfo {
3345            genesis_config,
3346            mint_keypair,
3347            ..
3348        } = create_genesis_config(1000);
3349        let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
3350        let keypair1 = Keypair::new();
3351        let keypair2 = Keypair::new();
3352        let keypair3 = Keypair::new();
3353
3354        // fund: put 4 in each of 1 and 2
3355        assert_matches!(bank.transfer(4, &mint_keypair, &keypair1.pubkey()), Ok(_));
3356        assert_matches!(bank.transfer(4, &mint_keypair, &keypair2.pubkey()), Ok(_));
3357
3358        // construct an Entry whose 2nd transaction would cause a lock conflict with previous entry
3359        let entry_1_to_mint = next_entry(
3360            &bank.last_blockhash(),
3361            1,
3362            vec![system_transaction::transfer(
3363                &keypair1,
3364                &mint_keypair.pubkey(),
3365                1,
3366                bank.last_blockhash(),
3367            )],
3368        );
3369
3370        let entry_2_to_3_mint_to_1 = next_entry(
3371            &entry_1_to_mint.hash,
3372            1,
3373            vec![
3374                system_transaction::transfer(
3375                    &keypair2,
3376                    &keypair3.pubkey(),
3377                    2,
3378                    bank.last_blockhash(),
3379                ), // should be fine
3380                system_transaction::transfer(
3381                    &keypair1,
3382                    &mint_keypair.pubkey(),
3383                    2,
3384                    bank.last_blockhash(),
3385                ), // will collide
3386            ],
3387        );
3388
3389        assert_eq!(
3390            process_entries_for_tests_without_scheduler(
3391                &bank,
3392                vec![entry_1_to_mint, entry_2_to_3_mint_to_1],
3393            ),
3394            Ok(())
3395        );
3396
3397        assert_eq!(bank.get_balance(&keypair1.pubkey()), 1);
3398        assert_eq!(bank.get_balance(&keypair2.pubkey()), 2);
3399        assert_eq!(bank.get_balance(&keypair3.pubkey()), 2);
3400    }
3401
3402    #[test]
3403    fn test_process_entries_2_txes_collision_and_error() {
3404        let GenesisConfigInfo {
3405            genesis_config,
3406            mint_keypair,
3407            ..
3408        } = create_genesis_config(1000);
3409        let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
3410        let keypair1 = Keypair::new();
3411        let keypair2 = Keypair::new();
3412        let keypair3 = Keypair::new();
3413        let keypair4 = Keypair::new();
3414
3415        // fund: put 4 in each of 1 and 2
3416        assert_matches!(bank.transfer(4, &mint_keypair, &keypair1.pubkey()), Ok(_));
3417        assert_matches!(bank.transfer(4, &mint_keypair, &keypair2.pubkey()), Ok(_));
3418        assert_matches!(bank.transfer(4, &mint_keypair, &keypair4.pubkey()), Ok(_));
3419
3420        let good_tx = system_transaction::transfer(
3421            &keypair1,
3422            &mint_keypair.pubkey(),
3423            1,
3424            bank.last_blockhash(),
3425        );
3426
3427        // construct an Entry whose 2nd transaction would cause a lock conflict with previous entry
3428        let entry_1_to_mint = next_entry(
3429            &bank.last_blockhash(),
3430            1,
3431            vec![
3432                good_tx.clone(),
3433                system_transaction::transfer(
3434                    &keypair4,
3435                    &keypair4.pubkey(),
3436                    1,
3437                    Hash::default(), // Should cause a transaction failure with BlockhashNotFound
3438                ),
3439            ],
3440        );
3441
3442        let entry_2_to_3_mint_to_1 = next_entry(
3443            &entry_1_to_mint.hash,
3444            1,
3445            vec![
3446                system_transaction::transfer(
3447                    &keypair2,
3448                    &keypair3.pubkey(),
3449                    2,
3450                    bank.last_blockhash(),
3451                ), // should be fine
3452                system_transaction::transfer(
3453                    &keypair1,
3454                    &mint_keypair.pubkey(),
3455                    2,
3456                    bank.last_blockhash(),
3457                ), // will collide
3458            ],
3459        );
3460
3461        assert_matches!(
3462            process_entries_for_tests_without_scheduler(
3463                &bank,
3464                vec![entry_1_to_mint.clone(), entry_2_to_3_mint_to_1.clone()],
3465            ),
3466            Err(TransactionError::BlockhashNotFound)
3467        );
3468
3469        // First transaction in first entry was rolled-back, so keypair1 didn't lost 1 lamport
3470        assert_eq!(bank.get_balance(&keypair1.pubkey()), 4);
3471        assert_eq!(bank.get_balance(&keypair2.pubkey()), 4);
3472
3473        // Check all accounts are unlocked
3474        let txs1 = entry_1_to_mint.transactions;
3475        let txs2 = entry_2_to_3_mint_to_1.transactions;
3476        let batch1 = bank.prepare_entry_batch(txs1).unwrap();
3477        for result in batch1.lock_results() {
3478            assert!(result.is_ok());
3479        }
3480        // txs1 and txs2 have accounts that conflict, so we must drop txs1 first
3481        drop(batch1);
3482        let batch2 = bank.prepare_entry_batch(txs2).unwrap();
3483        for result in batch2.lock_results() {
3484            assert!(result.is_ok());
3485        }
3486        drop(batch2);
3487
3488        // ensure good_tx will succeed and was just rolled back above due to other failing tx
3489        let entry_3 = next_entry(&entry_2_to_3_mint_to_1.hash, 1, vec![good_tx]);
3490        assert_matches!(
3491            process_entries_for_tests_without_scheduler(&bank, vec![entry_3]),
3492            Ok(())
3493        );
3494        // First transaction in third entry succeeded, so keypair1 lost 1 lamport
3495        assert_eq!(bank.get_balance(&keypair1.pubkey()), 3);
3496    }
3497
3498    #[test_case(true, true; "rent_collected")]
3499    #[test_case(false, true; "rent_not_collected")]
3500    #[test_case(true, false; "rent_not-collected_part_rent_disabled")]
3501    fn test_transaction_result_does_not_affect_bankhash(
3502        fee_payer_in_rent_partition: bool,
3503        should_run_partitioned_rent_collection: bool,
3504    ) {
3505        clone_solana_logger::setup();
3506        let GenesisConfigInfo {
3507            mut genesis_config,
3508            mint_keypair,
3509            ..
3510        } = if fee_payer_in_rent_partition {
3511            create_genesis_config(1000)
3512        } else {
3513            create_genesis_config_with_mint_keypair(Keypair::from_seed(&[1u8; 32]).unwrap(), 1000)
3514        };
3515
3516        if should_run_partitioned_rent_collection {
3517            genesis_config
3518                .accounts
3519                .remove(&clone_agave_feature_set::disable_partitioned_rent_collection::id());
3520        }
3521
3522        fn get_instruction_errors() -> Vec<InstructionError> {
3523            vec![
3524                InstructionError::GenericError,
3525                InstructionError::InvalidArgument,
3526                InstructionError::InvalidInstructionData,
3527                InstructionError::InvalidAccountData,
3528                InstructionError::AccountDataTooSmall,
3529                InstructionError::InsufficientFunds,
3530                InstructionError::IncorrectProgramId,
3531                InstructionError::MissingRequiredSignature,
3532                InstructionError::AccountAlreadyInitialized,
3533                InstructionError::UninitializedAccount,
3534                InstructionError::UnbalancedInstruction,
3535                InstructionError::ModifiedProgramId,
3536                InstructionError::ExternalAccountLamportSpend,
3537                InstructionError::ExternalAccountDataModified,
3538                InstructionError::ReadonlyLamportChange,
3539                InstructionError::ReadonlyDataModified,
3540                InstructionError::DuplicateAccountIndex,
3541                InstructionError::ExecutableModified,
3542                InstructionError::RentEpochModified,
3543                InstructionError::NotEnoughAccountKeys,
3544                InstructionError::AccountDataSizeChanged,
3545                InstructionError::AccountNotExecutable,
3546                InstructionError::AccountBorrowFailed,
3547                InstructionError::AccountBorrowOutstanding,
3548                InstructionError::DuplicateAccountOutOfSync,
3549                InstructionError::Custom(0),
3550                InstructionError::InvalidError,
3551                InstructionError::ExecutableDataModified,
3552                InstructionError::ExecutableLamportChange,
3553                InstructionError::ExecutableAccountNotRentExempt,
3554                InstructionError::UnsupportedProgramId,
3555                InstructionError::CallDepth,
3556                InstructionError::MissingAccount,
3557                InstructionError::ReentrancyNotAllowed,
3558                InstructionError::MaxSeedLengthExceeded,
3559                InstructionError::InvalidSeeds,
3560                InstructionError::InvalidRealloc,
3561                InstructionError::ComputationalBudgetExceeded,
3562                InstructionError::PrivilegeEscalation,
3563                InstructionError::ProgramEnvironmentSetupFailure,
3564                InstructionError::ProgramFailedToComplete,
3565                InstructionError::ProgramFailedToCompile,
3566                InstructionError::Immutable,
3567                InstructionError::IncorrectAuthority,
3568                InstructionError::BorshIoError("error".to_string()),
3569                InstructionError::AccountNotRentExempt,
3570                InstructionError::InvalidAccountOwner,
3571                InstructionError::ArithmeticOverflow,
3572                InstructionError::UnsupportedSysvar,
3573                InstructionError::IllegalOwner,
3574                InstructionError::MaxAccountsDataAllocationsExceeded,
3575                InstructionError::MaxAccountsExceeded,
3576                InstructionError::MaxInstructionTraceLengthExceeded,
3577                InstructionError::BuiltinProgramsMustConsumeComputeUnits,
3578            ]
3579        }
3580
3581        declare_process_instruction!(MockBuiltinOk, 1, |_invoke_context| {
3582            // Always succeeds
3583            Ok(())
3584        });
3585
3586        let mock_program_id = Pubkey::new_unique();
3587
3588        let (bank, _bank_forks) = Bank::new_with_mockup_builtin_for_tests(
3589            &genesis_config,
3590            mock_program_id,
3591            MockBuiltinOk::vm,
3592        );
3593
3594        let tx = Transaction::new_signed_with_payer(
3595            &[Instruction::new_with_bincode(
3596                mock_program_id,
3597                &10,
3598                Vec::new(),
3599            )],
3600            Some(&mint_keypair.pubkey()),
3601            &[&mint_keypair],
3602            bank.last_blockhash(),
3603        );
3604
3605        let entry = next_entry(&bank.last_blockhash(), 1, vec![tx]);
3606        let result = process_entries_for_tests_without_scheduler(&bank, vec![entry]);
3607        bank.freeze();
3608        let ok_bank_details = SlotDetails::new_from_bank(&bank, true).unwrap();
3609        assert!(result.is_ok());
3610
3611        declare_process_instruction!(MockBuiltinErr, 1, |invoke_context| {
3612            let instruction_errors = get_instruction_errors();
3613
3614            let err = invoke_context
3615                .transaction_context
3616                .get_current_instruction_context()
3617                .expect("Failed to get instruction context")
3618                .get_instruction_data()
3619                .first()
3620                .expect("Failed to get instruction data");
3621            Err(instruction_errors
3622                .get(*err as usize)
3623                .expect("Invalid error index")
3624                .clone())
3625        });
3626
3627        // Store details to compare against subsequent iterations
3628        let mut err_bank_details = None;
3629
3630        (0..get_instruction_errors().len()).for_each(|err| {
3631            let (bank, _bank_forks) = Bank::new_with_mockup_builtin_for_tests(
3632                &genesis_config,
3633                mock_program_id,
3634                MockBuiltinErr::vm,
3635            );
3636
3637            let tx = Transaction::new_signed_with_payer(
3638                &[Instruction::new_with_bincode(
3639                    mock_program_id,
3640                    &(err as u8),
3641                    Vec::new(),
3642                )],
3643                Some(&mint_keypair.pubkey()),
3644                &[&mint_keypair],
3645                bank.last_blockhash(),
3646            );
3647
3648            let entry = next_entry(&bank.last_blockhash(), 1, vec![tx]);
3649            let bank = Arc::new(bank);
3650            let result = process_entries_for_tests_without_scheduler(&bank, vec![entry]);
3651            assert!(result.is_ok()); // No failing transaction error - only instruction errors
3652            bank.freeze();
3653            let bank_details = SlotDetails::new_from_bank(&bank, true).unwrap();
3654
3655            // Transaction success/failure should not affect block hash ...
3656            assert_eq!(
3657                ok_bank_details
3658                    .bank_hash_components
3659                    .as_ref()
3660                    .unwrap()
3661                    .last_blockhash,
3662                bank_details
3663                    .bank_hash_components
3664                    .as_ref()
3665                    .unwrap()
3666                    .last_blockhash
3667            );
3668            // AND should not affect bankhash IF the rent is collected during freeze.
3669            assert_eq!(
3670                ok_bank_details == bank_details,
3671                fee_payer_in_rent_partition && should_run_partitioned_rent_collection
3672            );
3673            // Different types of transaction failure should not affect bank hash
3674            if let Some(prev_bank_details) = &err_bank_details {
3675                assert_eq!(
3676                    *prev_bank_details,
3677                    bank_details,
3678                    "bank hash mismatched for tx error: {:?}",
3679                    get_instruction_errors()[err]
3680                );
3681            } else {
3682                err_bank_details = Some(bank_details);
3683            }
3684        });
3685    }
3686
3687    #[test]
3688    fn test_process_entries_2nd_entry_collision_with_self_and_error() {
3689        clone_solana_logger::setup();
3690
3691        let GenesisConfigInfo {
3692            genesis_config,
3693            mint_keypair,
3694            ..
3695        } = create_genesis_config(1000);
3696        let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
3697        let keypair1 = Keypair::new();
3698        let keypair2 = Keypair::new();
3699        let keypair3 = Keypair::new();
3700
3701        // fund: put some money in each of 1 and 2
3702        assert_matches!(bank.transfer(5, &mint_keypair, &keypair1.pubkey()), Ok(_));
3703        assert_matches!(bank.transfer(4, &mint_keypair, &keypair2.pubkey()), Ok(_));
3704
3705        // 3 entries: first has a transfer, 2nd has a conflict with 1st, 3rd has a conflict with itself
3706        let entry_1_to_mint = next_entry(
3707            &bank.last_blockhash(),
3708            1,
3709            vec![system_transaction::transfer(
3710                &keypair1,
3711                &mint_keypair.pubkey(),
3712                1,
3713                bank.last_blockhash(),
3714            )],
3715        );
3716        // should now be:
3717        // keypair1=4
3718        // keypair2=4
3719        // keypair3=0
3720
3721        let entry_2_to_3_and_1_to_mint = next_entry(
3722            &entry_1_to_mint.hash,
3723            1,
3724            vec![
3725                system_transaction::transfer(
3726                    &keypair2,
3727                    &keypair3.pubkey(),
3728                    2,
3729                    bank.last_blockhash(),
3730                ), // should be fine
3731                system_transaction::transfer(
3732                    &keypair1,
3733                    &mint_keypair.pubkey(),
3734                    2,
3735                    bank.last_blockhash(),
3736                ), // will collide with predecessor
3737            ],
3738        );
3739        // should now be:
3740        // keypair1=2
3741        // keypair2=2
3742        // keypair3=2
3743
3744        let entry_conflict_itself = next_entry(
3745            &entry_2_to_3_and_1_to_mint.hash,
3746            1,
3747            vec![
3748                system_transaction::transfer(
3749                    &keypair1,
3750                    &keypair3.pubkey(),
3751                    1,
3752                    bank.last_blockhash(),
3753                ),
3754                system_transaction::transfer(
3755                    &keypair1,
3756                    &keypair2.pubkey(),
3757                    1,
3758                    bank.last_blockhash(),
3759                ), // should be fine
3760            ],
3761        );
3762        // would now be:
3763        // keypair1=0
3764        // keypair2=3
3765        // keypair3=3
3766
3767        assert!(process_entries_for_tests_without_scheduler(
3768            &bank,
3769            vec![
3770                entry_1_to_mint,
3771                entry_2_to_3_and_1_to_mint,
3772                entry_conflict_itself,
3773            ],
3774        )
3775        .is_err());
3776
3777        // last entry should have been aborted before par_execute_entries
3778        assert_eq!(bank.get_balance(&keypair1.pubkey()), 2);
3779        assert_eq!(bank.get_balance(&keypair2.pubkey()), 2);
3780        assert_eq!(bank.get_balance(&keypair3.pubkey()), 2);
3781    }
3782
3783    #[test]
3784    fn test_process_entries_2_entries_par() {
3785        let GenesisConfigInfo {
3786            genesis_config,
3787            mint_keypair,
3788            ..
3789        } = create_genesis_config(1000);
3790        let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
3791        let keypair1 = Keypair::new();
3792        let keypair2 = Keypair::new();
3793        let keypair3 = Keypair::new();
3794        let keypair4 = Keypair::new();
3795
3796        //load accounts
3797        let tx = system_transaction::transfer(
3798            &mint_keypair,
3799            &keypair1.pubkey(),
3800            1,
3801            bank.last_blockhash(),
3802        );
3803        assert_eq!(bank.process_transaction(&tx), Ok(()));
3804        let tx = system_transaction::transfer(
3805            &mint_keypair,
3806            &keypair2.pubkey(),
3807            1,
3808            bank.last_blockhash(),
3809        );
3810        assert_eq!(bank.process_transaction(&tx), Ok(()));
3811
3812        // ensure bank can process 2 entries that do not have a common account and no tick is registered
3813        let blockhash = bank.last_blockhash();
3814        let tx =
3815            system_transaction::transfer(&keypair1, &keypair3.pubkey(), 1, bank.last_blockhash());
3816        let entry_1 = next_entry(&blockhash, 1, vec![tx]);
3817        let tx =
3818            system_transaction::transfer(&keypair2, &keypair4.pubkey(), 1, bank.last_blockhash());
3819        let entry_2 = next_entry(&entry_1.hash, 1, vec![tx]);
3820        assert_eq!(
3821            process_entries_for_tests_without_scheduler(&bank, vec![entry_1, entry_2]),
3822            Ok(())
3823        );
3824        assert_eq!(bank.get_balance(&keypair3.pubkey()), 1);
3825        assert_eq!(bank.get_balance(&keypair4.pubkey()), 1);
3826        assert_eq!(bank.last_blockhash(), blockhash);
3827    }
3828
3829    #[test]
3830    fn test_process_entry_tx_random_execution_with_error() {
3831        let GenesisConfigInfo {
3832            genesis_config,
3833            mint_keypair,
3834            ..
3835        } = create_genesis_config(1_000_000_000);
3836        let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
3837
3838        const NUM_TRANSFERS_PER_ENTRY: usize = 8;
3839        const NUM_TRANSFERS: usize = NUM_TRANSFERS_PER_ENTRY * 32;
3840        // large enough to scramble locks and results
3841
3842        let keypairs: Vec<_> = (0..NUM_TRANSFERS * 2).map(|_| Keypair::new()).collect();
3843
3844        // give everybody one lamport
3845        for keypair in &keypairs {
3846            bank.transfer(1, &mint_keypair, &keypair.pubkey())
3847                .expect("funding failed");
3848        }
3849        let mut hash = bank.last_blockhash();
3850
3851        let present_account_key = Keypair::new();
3852        let present_account = AccountSharedData::new(1, 10, &Pubkey::default());
3853        bank.store_account(&present_account_key.pubkey(), &present_account);
3854
3855        let entries: Vec<_> = (0..NUM_TRANSFERS)
3856            .step_by(NUM_TRANSFERS_PER_ENTRY)
3857            .map(|i| {
3858                let mut transactions = (0..NUM_TRANSFERS_PER_ENTRY)
3859                    .map(|j| {
3860                        system_transaction::transfer(
3861                            &keypairs[i + j],
3862                            &keypairs[i + j + NUM_TRANSFERS].pubkey(),
3863                            1,
3864                            bank.last_blockhash(),
3865                        )
3866                    })
3867                    .collect::<Vec<_>>();
3868
3869                transactions.push(system_transaction::create_account(
3870                    &mint_keypair,
3871                    &present_account_key, // puts a TX error in results
3872                    bank.last_blockhash(),
3873                    1,
3874                    0,
3875                    &clone_solana_pubkey::new_rand(),
3876                ));
3877
3878                next_entry_mut(&mut hash, 0, transactions)
3879            })
3880            .collect();
3881        assert_eq!(
3882            process_entries_for_tests_without_scheduler(&bank, entries),
3883            Ok(())
3884        );
3885    }
3886
3887    #[test]
3888    fn test_process_entry_tx_random_execution_no_error() {
3889        // entropy multiplier should be big enough to provide sufficient entropy
3890        // but small enough to not take too much time while executing the test.
3891        let entropy_multiplier: usize = 25;
3892        let initial_lamports = 100;
3893
3894        // number of accounts need to be in multiple of 4 for correct
3895        // execution of the test.
3896        let num_accounts = entropy_multiplier * 4;
3897        let GenesisConfigInfo {
3898            genesis_config,
3899            mint_keypair,
3900            ..
3901        } = create_genesis_config((num_accounts + 1) as u64 * initial_lamports);
3902
3903        let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
3904
3905        let mut keypairs: Vec<Keypair> = vec![];
3906
3907        for _ in 0..num_accounts {
3908            let keypair = Keypair::new();
3909            let create_account_tx = system_transaction::transfer(
3910                &mint_keypair,
3911                &keypair.pubkey(),
3912                0,
3913                bank.last_blockhash(),
3914            );
3915            assert_eq!(bank.process_transaction(&create_account_tx), Ok(()));
3916            assert_matches!(
3917                bank.transfer(initial_lamports, &mint_keypair, &keypair.pubkey()),
3918                Ok(_)
3919            );
3920            keypairs.push(keypair);
3921        }
3922
3923        let mut tx_vector: Vec<Transaction> = vec![];
3924
3925        for i in (0..num_accounts).step_by(4) {
3926            tx_vector.append(&mut vec![
3927                system_transaction::transfer(
3928                    &keypairs[i + 1],
3929                    &keypairs[i].pubkey(),
3930                    initial_lamports,
3931                    bank.last_blockhash(),
3932                ),
3933                system_transaction::transfer(
3934                    &keypairs[i + 3],
3935                    &keypairs[i + 2].pubkey(),
3936                    initial_lamports,
3937                    bank.last_blockhash(),
3938                ),
3939            ]);
3940        }
3941
3942        // Transfer lamports to each other
3943        let entry = next_entry(&bank.last_blockhash(), 1, tx_vector);
3944        assert_eq!(
3945            process_entries_for_tests_without_scheduler(&bank, vec![entry]),
3946            Ok(())
3947        );
3948        bank.squash();
3949
3950        // Even number keypair should have balance of 2 * initial_lamports and
3951        // odd number keypair should have balance of 0, which proves
3952        // that even in case of random order of execution, overall state remains
3953        // consistent.
3954        for (i, keypair) in keypairs.iter().enumerate() {
3955            if i % 2 == 0 {
3956                assert_eq!(bank.get_balance(&keypair.pubkey()), 2 * initial_lamports);
3957            } else {
3958                assert_eq!(bank.get_balance(&keypair.pubkey()), 0);
3959            }
3960        }
3961    }
3962
3963    #[test]
3964    fn test_process_entries_2_entries_tick() {
3965        let GenesisConfigInfo {
3966            genesis_config,
3967            mint_keypair,
3968            ..
3969        } = create_genesis_config(1000);
3970        let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
3971        let keypair1 = Keypair::new();
3972        let keypair2 = Keypair::new();
3973        let keypair3 = Keypair::new();
3974        let keypair4 = Keypair::new();
3975
3976        //load accounts
3977        let tx = system_transaction::transfer(
3978            &mint_keypair,
3979            &keypair1.pubkey(),
3980            1,
3981            bank.last_blockhash(),
3982        );
3983        assert_eq!(bank.process_transaction(&tx), Ok(()));
3984        let tx = system_transaction::transfer(
3985            &mint_keypair,
3986            &keypair2.pubkey(),
3987            1,
3988            bank.last_blockhash(),
3989        );
3990        assert_eq!(bank.process_transaction(&tx), Ok(()));
3991
3992        let blockhash = bank.last_blockhash();
3993        while blockhash == bank.last_blockhash() {
3994            bank.register_default_tick_for_test();
3995        }
3996
3997        // ensure bank can process 2 entries that do not have a common account and tick is registered
3998        let tx = system_transaction::transfer(&keypair2, &keypair3.pubkey(), 1, blockhash);
3999        let entry_1 = next_entry(&blockhash, 1, vec![tx]);
4000        let tick = next_entry(&entry_1.hash, 1, vec![]);
4001        let tx =
4002            system_transaction::transfer(&keypair1, &keypair4.pubkey(), 1, bank.last_blockhash());
4003        let entry_2 = next_entry(&tick.hash, 1, vec![tx]);
4004        assert_eq!(
4005            process_entries_for_tests_without_scheduler(
4006                &bank,
4007                vec![entry_1, tick, entry_2.clone()],
4008            ),
4009            Ok(())
4010        );
4011        assert_eq!(bank.get_balance(&keypair3.pubkey()), 1);
4012        assert_eq!(bank.get_balance(&keypair4.pubkey()), 1);
4013
4014        // ensure that an error is returned for an empty account (keypair2)
4015        let tx =
4016            system_transaction::transfer(&keypair2, &keypair3.pubkey(), 1, bank.last_blockhash());
4017        let entry_3 = next_entry(&entry_2.hash, 1, vec![tx]);
4018        assert_eq!(
4019            process_entries_for_tests_without_scheduler(&bank, vec![entry_3]),
4020            Err(TransactionError::AccountNotFound)
4021        );
4022    }
4023
4024    #[test]
4025    fn test_update_transaction_statuses() {
4026        let GenesisConfigInfo {
4027            genesis_config,
4028            mint_keypair,
4029            ..
4030        } = create_genesis_config(11_000);
4031        let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
4032
4033        // Make sure instruction errors still update the signature cache
4034        let pubkey = clone_solana_pubkey::new_rand();
4035        bank.transfer(1_000, &mint_keypair, &pubkey).unwrap();
4036        assert_eq!(bank.transaction_count(), 1);
4037        assert_eq!(bank.get_balance(&pubkey), 1_000);
4038        assert_eq!(
4039            bank.transfer(10_001, &mint_keypair, &pubkey),
4040            Err(TransactionError::InstructionError(
4041                0,
4042                SystemError::ResultWithNegativeLamports.into(),
4043            ))
4044        );
4045        assert_eq!(
4046            bank.transfer(10_001, &mint_keypair, &pubkey),
4047            Err(TransactionError::AlreadyProcessed)
4048        );
4049
4050        // Make sure fees-only transactions still update the signature cache
4051        let missing_program_id = Pubkey::new_unique();
4052        let tx = Transaction::new_signed_with_payer(
4053            &[Instruction::new_with_bincode(
4054                missing_program_id,
4055                &10,
4056                Vec::new(),
4057            )],
4058            Some(&mint_keypair.pubkey()),
4059            &[&mint_keypair],
4060            bank.last_blockhash(),
4061        );
4062        // First process attempt will fail but still update status cache
4063        assert_eq!(
4064            bank.process_transaction(&tx),
4065            Err(TransactionError::ProgramAccountNotFound)
4066        );
4067        // Second attempt will be rejected since tx was already in status cache
4068        assert_eq!(
4069            bank.process_transaction(&tx),
4070            Err(TransactionError::AlreadyProcessed)
4071        );
4072
4073        // Make sure other errors don't update the signature cache
4074        let tx = system_transaction::transfer(&mint_keypair, &pubkey, 1000, Hash::default());
4075        let signature = tx.signatures[0];
4076
4077        // Should fail with blockhash not found
4078        assert_eq!(
4079            bank.process_transaction(&tx).map(|_| signature),
4080            Err(TransactionError::BlockhashNotFound)
4081        );
4082
4083        // Should fail again with blockhash not found
4084        assert_eq!(
4085            bank.process_transaction(&tx).map(|_| signature),
4086            Err(TransactionError::BlockhashNotFound)
4087        );
4088    }
4089
4090    #[test]
4091    fn test_update_transaction_statuses_fail() {
4092        let GenesisConfigInfo {
4093            genesis_config,
4094            mint_keypair,
4095            ..
4096        } = create_genesis_config(11_000);
4097        let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
4098        let keypair1 = Keypair::new();
4099        let keypair2 = Keypair::new();
4100        let success_tx = system_transaction::transfer(
4101            &mint_keypair,
4102            &keypair1.pubkey(),
4103            1,
4104            bank.last_blockhash(),
4105        );
4106        let fail_tx = system_transaction::transfer(
4107            &mint_keypair,
4108            &keypair2.pubkey(),
4109            2,
4110            bank.last_blockhash(),
4111        );
4112
4113        let entry_1_to_mint = next_entry(
4114            &bank.last_blockhash(),
4115            1,
4116            vec![
4117                success_tx,
4118                fail_tx.clone(), // will collide
4119            ],
4120        );
4121
4122        assert_eq!(
4123            process_entries_for_tests_without_scheduler(&bank, vec![entry_1_to_mint]),
4124            Err(TransactionError::AccountInUse)
4125        );
4126
4127        // Should not see duplicate signature error
4128        assert_eq!(bank.process_transaction(&fail_tx), Ok(()));
4129    }
4130
4131    #[test]
4132    fn test_halt_at_slot_starting_snapshot_root() {
4133        let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(123);
4134
4135        // Create roots at slots 0, 1
4136        let forks = tr(0) / tr(1);
4137        let ledger_path = get_tmp_ledger_path_auto_delete!();
4138        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
4139        blockstore.add_tree(
4140            forks,
4141            false,
4142            true,
4143            genesis_config.ticks_per_slot,
4144            genesis_config.hash(),
4145        );
4146        blockstore.set_roots([0, 1].iter()).unwrap();
4147
4148        // Specify halting at slot 0
4149        let opts = ProcessOptions {
4150            run_verification: true,
4151            halt_at_slot: Some(0),
4152            accounts_db_test_hash_calculation: true,
4153            ..ProcessOptions::default()
4154        };
4155        let (bank_forks, ..) =
4156            test_process_blockstore(&genesis_config, &blockstore, &opts, Arc::default());
4157        let bank_forks = bank_forks.read().unwrap();
4158
4159        // Should be able to fetch slot 0 because we specified halting at slot 0, even
4160        // if there is a greater root at slot 1.
4161        assert!(bank_forks.get(0).is_some());
4162    }
4163
4164    #[test]
4165    fn test_process_blockstore_from_root() {
4166        let GenesisConfigInfo {
4167            mut genesis_config, ..
4168        } = create_genesis_config(123);
4169
4170        let ticks_per_slot = 1;
4171        genesis_config.ticks_per_slot = ticks_per_slot;
4172        let (ledger_path, blockhash) = create_new_tmp_ledger_auto_delete!(&genesis_config);
4173        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
4174
4175        /*
4176          Build a blockstore in the ledger with the following fork structure:
4177
4178               slot 0 (all ticks)
4179                 |
4180               slot 1 (all ticks)
4181                 |
4182               slot 2 (all ticks)
4183                 |
4184               slot 3 (all ticks) -> root
4185                 |
4186               slot 4 (all ticks)
4187                 |
4188               slot 5 (all ticks) -> root
4189                 |
4190               slot 6 (all ticks)
4191        */
4192
4193        let mut last_hash = blockhash;
4194        for i in 0..6 {
4195            last_hash =
4196                fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, i + 1, i, last_hash);
4197        }
4198        blockstore.set_roots([3, 5].iter()).unwrap();
4199
4200        // Set up bank1
4201        let bank_forks = BankForks::new_rw_arc(Bank::new_for_tests(&genesis_config));
4202        let bank0 = bank_forks.read().unwrap().get_with_scheduler(0).unwrap();
4203        let opts = ProcessOptions {
4204            run_verification: true,
4205            accounts_db_test_hash_calculation: true,
4206            ..ProcessOptions::default()
4207        };
4208        let recyclers = VerifyRecyclers::default();
4209        let replay_tx_thread_pool = create_thread_pool(1);
4210        process_bank_0(
4211            &bank0,
4212            &blockstore,
4213            &replay_tx_thread_pool,
4214            &opts,
4215            &recyclers,
4216            None,
4217            None,
4218        );
4219        let bank0_last_blockhash = bank0.last_blockhash();
4220        let bank1 = bank_forks.write().unwrap().insert(Bank::new_from_parent(
4221            bank0.clone_without_scheduler(),
4222            &Pubkey::default(),
4223            1,
4224        ));
4225        confirm_full_slot(
4226            &blockstore,
4227            &bank1,
4228            &replay_tx_thread_pool,
4229            &opts,
4230            &recyclers,
4231            &mut ConfirmationProgress::new(bank0_last_blockhash),
4232            None,
4233            None,
4234            None,
4235            &mut ExecuteTimings::default(),
4236        )
4237        .unwrap();
4238        bank_forks
4239            .write()
4240            .unwrap()
4241            .set_root(
4242                1,
4243                &clone_solana_runtime::accounts_background_service::AbsRequestSender::default(),
4244                None,
4245            )
4246            .unwrap();
4247
4248        let leader_schedule_cache = LeaderScheduleCache::new_from_bank(&bank1);
4249
4250        // Test process_blockstore_from_root() from slot 1 onwards
4251        process_blockstore_from_root(
4252            &blockstore,
4253            &bank_forks,
4254            &leader_schedule_cache,
4255            &opts,
4256            None,
4257            None,
4258            None,
4259            &AbsRequestSender::default(),
4260        )
4261        .unwrap();
4262
4263        let bank_forks = bank_forks.read().unwrap();
4264
4265        assert_eq!(frozen_bank_slots(&bank_forks), vec![5, 6]);
4266        assert_eq!(bank_forks.working_bank().slot(), 6);
4267        assert_eq!(bank_forks.root(), 5);
4268
4269        // Verify the parents of the head of the fork
4270        assert_eq!(
4271            &bank_forks[6]
4272                .parents()
4273                .iter()
4274                .map(|bank| bank.slot())
4275                .collect::<Vec<_>>(),
4276            &[5]
4277        );
4278
4279        // Check that bank forks has the correct banks
4280        verify_fork_infos(&bank_forks);
4281    }
4282
4283    #[test]
4284    #[ignore]
4285    fn test_process_entries_stress() {
4286        // this test throws lots of rayon threads at process_entries()
4287        //  finds bugs in very low-layer stuff
4288        clone_solana_logger::setup();
4289        let GenesisConfigInfo {
4290            genesis_config,
4291            mint_keypair,
4292            ..
4293        } = create_genesis_config(1_000_000_000);
4294        let mut bank = Arc::new(Bank::new_for_tests(&genesis_config));
4295
4296        const NUM_TRANSFERS_PER_ENTRY: usize = 8;
4297        const NUM_TRANSFERS: usize = NUM_TRANSFERS_PER_ENTRY * 32;
4298
4299        let keypairs: Vec<_> = (0..NUM_TRANSFERS * 2).map(|_| Keypair::new()).collect();
4300
4301        // give everybody one lamport
4302        for keypair in &keypairs {
4303            bank.transfer(1, &mint_keypair, &keypair.pubkey())
4304                .expect("funding failed");
4305        }
4306
4307        let present_account_key = Keypair::new();
4308        let present_account = AccountSharedData::new(1, 10, &Pubkey::default());
4309        bank.store_account(&present_account_key.pubkey(), &present_account);
4310
4311        let mut i = 0;
4312        let mut hash = bank.last_blockhash();
4313        let mut root: Option<Arc<Bank>> = None;
4314        loop {
4315            let entries: Vec<_> = (0..NUM_TRANSFERS)
4316                .step_by(NUM_TRANSFERS_PER_ENTRY)
4317                .map(|i| {
4318                    next_entry_mut(&mut hash, 0, {
4319                        let mut transactions = (i..i + NUM_TRANSFERS_PER_ENTRY)
4320                            .map(|i| {
4321                                system_transaction::transfer(
4322                                    &keypairs[i],
4323                                    &keypairs[i + NUM_TRANSFERS].pubkey(),
4324                                    1,
4325                                    bank.last_blockhash(),
4326                                )
4327                            })
4328                            .collect::<Vec<_>>();
4329
4330                        transactions.push(system_transaction::create_account(
4331                            &mint_keypair,
4332                            &present_account_key, // puts a TX error in results
4333                            bank.last_blockhash(),
4334                            100,
4335                            100,
4336                            &clone_solana_pubkey::new_rand(),
4337                        ));
4338                        transactions
4339                    })
4340                })
4341                .collect();
4342            info!("paying iteration {}", i);
4343            process_entries_for_tests_without_scheduler(&bank, entries).expect("paying failed");
4344
4345            let entries: Vec<_> = (0..NUM_TRANSFERS)
4346                .step_by(NUM_TRANSFERS_PER_ENTRY)
4347                .map(|i| {
4348                    next_entry_mut(
4349                        &mut hash,
4350                        0,
4351                        (i..i + NUM_TRANSFERS_PER_ENTRY)
4352                            .map(|i| {
4353                                system_transaction::transfer(
4354                                    &keypairs[i + NUM_TRANSFERS],
4355                                    &keypairs[i].pubkey(),
4356                                    1,
4357                                    bank.last_blockhash(),
4358                                )
4359                            })
4360                            .collect::<Vec<_>>(),
4361                    )
4362                })
4363                .collect();
4364
4365            info!("refunding iteration {}", i);
4366            process_entries_for_tests_without_scheduler(&bank, entries).expect("refunding failed");
4367
4368            // advance to next block
4369            process_entries_for_tests_without_scheduler(
4370                &bank,
4371                (0..bank.ticks_per_slot())
4372                    .map(|_| next_entry_mut(&mut hash, 1, vec![]))
4373                    .collect::<Vec<_>>(),
4374            )
4375            .expect("process ticks failed");
4376
4377            if i % 16 == 0 {
4378                if let Some(old_root) = root {
4379                    old_root.squash();
4380                }
4381                root = Some(bank.clone());
4382            }
4383            i += 1;
4384
4385            let slot = bank.slot() + thread_rng().gen_range(1..3);
4386            bank = Arc::new(Bank::new_from_parent(bank, &Pubkey::default(), slot));
4387        }
4388    }
4389
4390    #[test]
4391    fn test_process_ledger_ticks_ordering() {
4392        let GenesisConfigInfo {
4393            genesis_config,
4394            mint_keypair,
4395            ..
4396        } = create_genesis_config(100);
4397        let (bank0, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
4398        let genesis_hash = genesis_config.hash();
4399        let keypair = Keypair::new();
4400
4401        // Simulate a slot of virtual ticks, creates a new blockhash
4402        let mut entries = create_ticks(genesis_config.ticks_per_slot, 1, genesis_hash);
4403
4404        // The new blockhash is going to be the hash of the last tick in the block
4405        let new_blockhash = entries.last().unwrap().hash;
4406        // Create an transaction that references the new blockhash, should still
4407        // be able to find the blockhash if we process transactions all in the same
4408        // batch
4409        let tx = system_transaction::transfer(&mint_keypair, &keypair.pubkey(), 1, new_blockhash);
4410        let entry = next_entry(&new_blockhash, 1, vec![tx]);
4411        entries.push(entry);
4412
4413        process_entries_for_tests_without_scheduler(&bank0, entries).unwrap();
4414        assert_eq!(bank0.get_balance(&keypair.pubkey()), 1)
4415    }
4416
4417    fn get_epoch_schedule(genesis_config: &GenesisConfig) -> EpochSchedule {
4418        let bank = Bank::new_for_tests(genesis_config);
4419        bank.epoch_schedule().clone()
4420    }
4421
4422    fn frozen_bank_slots(bank_forks: &BankForks) -> Vec<Slot> {
4423        let mut slots: Vec<_> = bank_forks.frozen_banks().keys().cloned().collect();
4424        slots.sort_unstable();
4425        slots
4426    }
4427
4428    // Check that `bank_forks` contains all the ancestors and banks for each fork identified in
4429    // `bank_forks_info`
4430    fn verify_fork_infos(bank_forks: &BankForks) {
4431        for slot in frozen_bank_slots(bank_forks) {
4432            let head_bank = &bank_forks[slot];
4433            let mut parents = head_bank.parents();
4434            parents.push(head_bank.clone());
4435
4436            // Ensure the tip of each fork and all its parents are in the given bank_forks
4437            for parent in parents {
4438                let parent_bank = &bank_forks[parent.slot()];
4439                assert_eq!(parent_bank.slot(), parent.slot());
4440                assert!(parent_bank.is_frozen());
4441            }
4442        }
4443    }
4444
4445    #[test]
4446    fn test_get_first_error() {
4447        let GenesisConfigInfo {
4448            genesis_config,
4449            mint_keypair,
4450            ..
4451        } = create_genesis_config(1_000_000_000);
4452        let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
4453
4454        let present_account_key = Keypair::new();
4455        let present_account = AccountSharedData::new(1, 10, &Pubkey::default());
4456        bank.store_account(&present_account_key.pubkey(), &present_account);
4457
4458        let keypair = Keypair::new();
4459
4460        // Create array of two transactions which throw different errors
4461        let account_not_found_tx = system_transaction::transfer(
4462            &keypair,
4463            &clone_solana_pubkey::new_rand(),
4464            42,
4465            bank.last_blockhash(),
4466        );
4467        let account_not_found_sig = account_not_found_tx.signatures[0];
4468        let invalid_blockhash_tx = system_transaction::transfer(
4469            &mint_keypair,
4470            &clone_solana_pubkey::new_rand(),
4471            42,
4472            Hash::default(),
4473        );
4474        let txs = vec![account_not_found_tx, invalid_blockhash_tx];
4475        let batch = bank.prepare_batch_for_tests(txs);
4476        let (commit_results, _) = batch.bank().load_execute_and_commit_transactions(
4477            &batch,
4478            MAX_PROCESSING_AGE,
4479            false,
4480            ExecutionRecordingConfig::new_single_setting(false),
4481            &mut ExecuteTimings::default(),
4482            None,
4483        );
4484        let (err, signature) = do_get_first_error(&batch, &commit_results).unwrap();
4485        assert_eq!(err.unwrap_err(), TransactionError::AccountNotFound);
4486        assert_eq!(signature, account_not_found_sig);
4487    }
4488
4489    #[test]
4490    fn test_replay_vote_sender() {
4491        let validator_keypairs: Vec<_> =
4492            (0..10).map(|_| ValidatorVoteKeypairs::new_rand()).collect();
4493        let GenesisConfigInfo {
4494            genesis_config,
4495            voting_keypair: _,
4496            ..
4497        } = create_genesis_config_with_vote_accounts(
4498            1_000_000_000,
4499            &validator_keypairs,
4500            vec![100; validator_keypairs.len()],
4501        );
4502        let (bank0, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
4503        bank0.freeze();
4504
4505        let bank1 = bank_forks
4506            .write()
4507            .unwrap()
4508            .insert(Bank::new_from_parent(
4509                bank0.clone(),
4510                &clone_solana_pubkey::new_rand(),
4511                1,
4512            ))
4513            .clone_without_scheduler();
4514
4515        // The new blockhash is going to be the hash of the last tick in the block
4516        let bank_1_blockhash = bank1.last_blockhash();
4517
4518        // Create an transaction that references the new blockhash, should still
4519        // be able to find the blockhash if we process transactions all in the same
4520        // batch
4521        let mut expected_successful_voter_pubkeys = BTreeSet::new();
4522        let vote_txs: Vec<_> = validator_keypairs
4523            .iter()
4524            .enumerate()
4525            .map(|(i, validator_keypairs)| {
4526                let tower_sync = TowerSync::new_from_slots(vec![0], bank0.hash(), None);
4527                if i % 3 == 0 {
4528                    // These votes are correct
4529                    expected_successful_voter_pubkeys
4530                        .insert(validator_keypairs.vote_keypair.pubkey());
4531                    vote_transaction::new_tower_sync_transaction(
4532                        tower_sync,
4533                        bank_1_blockhash,
4534                        &validator_keypairs.node_keypair,
4535                        &validator_keypairs.vote_keypair,
4536                        &validator_keypairs.vote_keypair,
4537                        None,
4538                    )
4539                } else if i % 3 == 1 {
4540                    // These have the wrong authorized voter
4541                    vote_transaction::new_tower_sync_transaction(
4542                        tower_sync,
4543                        bank_1_blockhash,
4544                        &validator_keypairs.node_keypair,
4545                        &validator_keypairs.vote_keypair,
4546                        &Keypair::new(),
4547                        None,
4548                    )
4549                } else {
4550                    // These have an invalid vote for non-existent bank 2
4551                    vote_transaction::new_tower_sync_transaction(
4552                        TowerSync::from(vec![(bank1.slot() + 1, 1)]),
4553                        bank_1_blockhash,
4554                        &validator_keypairs.node_keypair,
4555                        &validator_keypairs.vote_keypair,
4556                        &validator_keypairs.vote_keypair,
4557                        None,
4558                    )
4559                }
4560            })
4561            .collect();
4562        let entry = next_entry(&bank_1_blockhash, 1, vote_txs);
4563        let (replay_vote_sender, replay_vote_receiver) = crossbeam_channel::unbounded();
4564        let _ = process_entries_for_tests(
4565            &BankWithScheduler::new_without_scheduler(bank1),
4566            vec![entry],
4567            None,
4568            Some(&replay_vote_sender),
4569        );
4570        let successes: BTreeSet<Pubkey> = replay_vote_receiver
4571            .try_iter()
4572            .map(|(vote_pubkey, ..)| vote_pubkey)
4573            .collect();
4574        assert_eq!(successes, expected_successful_voter_pubkeys);
4575    }
4576
4577    fn make_slot_with_vote_tx(
4578        blockstore: &Blockstore,
4579        ticks_per_slot: u64,
4580        tx_landed_slot: Slot,
4581        parent_slot: Slot,
4582        parent_blockhash: &Hash,
4583        vote_tx: Transaction,
4584        slot_leader_keypair: &Arc<Keypair>,
4585    ) {
4586        // Add votes to `last_slot` so that `root` will be confirmed
4587        let vote_entry = next_entry(parent_blockhash, 1, vec![vote_tx]);
4588        let mut entries = create_ticks(ticks_per_slot, 0, vote_entry.hash);
4589        entries.insert(0, vote_entry);
4590        blockstore
4591            .write_entries(
4592                tx_landed_slot,
4593                0,
4594                0,
4595                ticks_per_slot,
4596                Some(parent_slot),
4597                true,
4598                slot_leader_keypair,
4599                entries,
4600                0,
4601            )
4602            .unwrap();
4603    }
4604
4605    fn run_test_process_blockstore_with_supermajority_root(
4606        blockstore_root: Option<Slot>,
4607        blockstore_access_type: AccessType,
4608    ) {
4609        clone_solana_logger::setup();
4610        /*
4611            Build fork structure:
4612                 slot 0
4613                   |
4614                 slot 1 <- (blockstore root)
4615                 /    \
4616            slot 2    |
4617               |      |
4618            slot 4    |
4619                    slot 5
4620                      |
4621                `expected_root_slot`
4622                     /    \
4623                  ...    minor fork
4624                  /
4625            `last_slot`
4626                 |
4627            `really_last_slot`
4628        */
4629        let starting_fork_slot = 5;
4630        let mut main_fork = tr(starting_fork_slot);
4631        let mut main_fork_ref = main_fork.root_mut().get_mut();
4632
4633        // Make enough slots to make a root slot > blockstore_root
4634        let expected_root_slot = starting_fork_slot + blockstore_root.unwrap_or(0);
4635        let really_expected_root_slot = expected_root_slot + 1;
4636        let last_main_fork_slot = expected_root_slot + MAX_LOCKOUT_HISTORY as u64 + 1;
4637        let really_last_main_fork_slot = last_main_fork_slot + 1;
4638
4639        // Make `minor_fork`
4640        let last_minor_fork_slot = really_last_main_fork_slot + 1;
4641        let minor_fork = tr(last_minor_fork_slot);
4642
4643        // Make 'main_fork`
4644        for slot in starting_fork_slot + 1..last_main_fork_slot {
4645            if slot - 1 == expected_root_slot {
4646                main_fork_ref.push_front(minor_fork.clone());
4647            }
4648            main_fork_ref.push_front(tr(slot));
4649            main_fork_ref = main_fork_ref.front_mut().unwrap().get_mut();
4650        }
4651        let forks = tr(0) / (tr(1) / (tr(2) / (tr(4))) / main_fork);
4652        let validator_keypairs = ValidatorVoteKeypairs::new_rand();
4653        let GenesisConfigInfo { genesis_config, .. } =
4654            genesis_utils::create_genesis_config_with_vote_accounts(
4655                10_000,
4656                &[&validator_keypairs],
4657                vec![100],
4658            );
4659        let ticks_per_slot = genesis_config.ticks_per_slot();
4660        let ledger_path = get_tmp_ledger_path_auto_delete!();
4661        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
4662        blockstore.add_tree(forks, false, true, ticks_per_slot, genesis_config.hash());
4663
4664        if let Some(blockstore_root) = blockstore_root {
4665            blockstore
4666                .set_roots(std::iter::once(&blockstore_root))
4667                .unwrap();
4668        }
4669
4670        let opts = ProcessOptions {
4671            run_verification: true,
4672            accounts_db_test_hash_calculation: true,
4673            ..ProcessOptions::default()
4674        };
4675
4676        let (bank_forks, ..) = test_process_blockstore_with_custom_options(
4677            &genesis_config,
4678            &blockstore,
4679            &opts,
4680            blockstore_access_type.clone(),
4681        );
4682        let bank_forks = bank_forks.read().unwrap();
4683
4684        // prepare to add votes
4685        let last_vote_bank_hash = bank_forks.get(last_main_fork_slot - 1).unwrap().hash();
4686        let last_vote_blockhash = bank_forks
4687            .get(last_main_fork_slot - 1)
4688            .unwrap()
4689            .last_blockhash();
4690        let tower_sync = TowerSync::new_from_slot(last_main_fork_slot - 1, last_vote_bank_hash);
4691        let vote_tx = vote_transaction::new_tower_sync_transaction(
4692            tower_sync,
4693            last_vote_blockhash,
4694            &validator_keypairs.node_keypair,
4695            &validator_keypairs.vote_keypair,
4696            &validator_keypairs.vote_keypair,
4697            None,
4698        );
4699
4700        // Add votes to `last_slot` so that `root` will be confirmed
4701        let leader_keypair = Arc::new(validator_keypairs.node_keypair);
4702        make_slot_with_vote_tx(
4703            &blockstore,
4704            ticks_per_slot,
4705            last_main_fork_slot,
4706            last_main_fork_slot - 1,
4707            &last_vote_blockhash,
4708            vote_tx,
4709            &leader_keypair,
4710        );
4711
4712        let (bank_forks, ..) = test_process_blockstore_with_custom_options(
4713            &genesis_config,
4714            &blockstore,
4715            &opts,
4716            blockstore_access_type.clone(),
4717        );
4718        let bank_forks = bank_forks.read().unwrap();
4719
4720        assert_eq!(bank_forks.root(), expected_root_slot);
4721        assert_eq!(
4722            bank_forks.frozen_banks().len() as u64,
4723            last_minor_fork_slot - really_expected_root_slot + 1
4724        );
4725
4726        // Minor fork at `last_main_fork_slot + 1` was above the `expected_root_slot`
4727        // so should not have been purged
4728        //
4729        // Fork at slot 2 was purged because it was below the `expected_root_slot`
4730        for slot in 0..=last_minor_fork_slot {
4731            // this slot will be created below
4732            if slot == really_last_main_fork_slot {
4733                continue;
4734            }
4735            if slot >= expected_root_slot {
4736                let bank = bank_forks.get(slot).unwrap();
4737                assert_eq!(bank.slot(), slot);
4738                assert!(bank.is_frozen());
4739            } else {
4740                assert!(bank_forks.get(slot).is_none());
4741            }
4742        }
4743
4744        // really prepare to add votes
4745        let last_vote_bank_hash = bank_forks.get(last_main_fork_slot).unwrap().hash();
4746        let last_vote_blockhash = bank_forks
4747            .get(last_main_fork_slot)
4748            .unwrap()
4749            .last_blockhash();
4750        let tower_sync = TowerSync::new_from_slot(last_main_fork_slot, last_vote_bank_hash);
4751        let vote_tx = vote_transaction::new_tower_sync_transaction(
4752            tower_sync,
4753            last_vote_blockhash,
4754            &leader_keypair,
4755            &validator_keypairs.vote_keypair,
4756            &validator_keypairs.vote_keypair,
4757            None,
4758        );
4759
4760        // Add votes to `really_last_slot` so that `root` will be confirmed again
4761        make_slot_with_vote_tx(
4762            &blockstore,
4763            ticks_per_slot,
4764            really_last_main_fork_slot,
4765            last_main_fork_slot,
4766            &last_vote_blockhash,
4767            vote_tx,
4768            &leader_keypair,
4769        );
4770
4771        let (bank_forks, ..) = test_process_blockstore_with_custom_options(
4772            &genesis_config,
4773            &blockstore,
4774            &opts,
4775            blockstore_access_type,
4776        );
4777        let bank_forks = bank_forks.read().unwrap();
4778
4779        assert_eq!(bank_forks.root(), really_expected_root_slot);
4780    }
4781
4782    #[test]
4783    fn test_process_blockstore_with_supermajority_root_without_blockstore_root() {
4784        run_test_process_blockstore_with_supermajority_root(None, AccessType::Primary);
4785    }
4786
4787    #[test]
4788    fn test_process_blockstore_with_supermajority_root_without_blockstore_root_secondary_access() {
4789        run_test_process_blockstore_with_supermajority_root(None, AccessType::Secondary);
4790    }
4791
4792    #[test]
4793    fn test_process_blockstore_with_supermajority_root_with_blockstore_root() {
4794        run_test_process_blockstore_with_supermajority_root(Some(1), AccessType::Primary)
4795    }
4796
4797    #[test]
4798    #[allow(clippy::field_reassign_with_default)]
4799    fn test_supermajority_root_from_vote_accounts() {
4800        let convert_to_vote_accounts = |roots_stakes: Vec<(Slot, u64)>| -> VoteAccountsHashMap {
4801            roots_stakes
4802                .into_iter()
4803                .map(|(root, stake)| {
4804                    let mut vote_state = VoteState::default();
4805                    vote_state.root_slot = Some(root);
4806                    let mut vote_account =
4807                        AccountSharedData::new(1, VoteState::size_of(), &clone_solana_vote_program::id());
4808                    let versioned = VoteStateVersions::new_current(vote_state);
4809                    VoteState::serialize(&versioned, vote_account.data_as_mut_slice()).unwrap();
4810                    (
4811                        clone_solana_pubkey::new_rand(),
4812                        (stake, VoteAccount::try_from(vote_account).unwrap()),
4813                    )
4814                })
4815                .collect()
4816        };
4817
4818        let total_stake = 10;
4819
4820        // Supermajority root should be None
4821        assert!(supermajority_root_from_vote_accounts(total_stake, &HashMap::default()).is_none());
4822
4823        // Supermajority root should be None
4824        let roots_stakes = vec![(8, 1), (3, 1), (4, 1), (8, 1)];
4825        let accounts = convert_to_vote_accounts(roots_stakes);
4826        assert!(supermajority_root_from_vote_accounts(total_stake, &accounts).is_none());
4827
4828        // Supermajority root should be 4, has 7/10 of the stake
4829        let roots_stakes = vec![(8, 1), (3, 1), (4, 1), (8, 5)];
4830        let accounts = convert_to_vote_accounts(roots_stakes);
4831        assert_eq!(
4832            supermajority_root_from_vote_accounts(total_stake, &accounts).unwrap(),
4833            4
4834        );
4835
4836        // Supermajority root should be 8, it has 7/10 of the stake
4837        let roots_stakes = vec![(8, 1), (3, 1), (4, 1), (8, 6)];
4838        let accounts = convert_to_vote_accounts(roots_stakes);
4839        assert_eq!(
4840            supermajority_root_from_vote_accounts(total_stake, &accounts).unwrap(),
4841            8
4842        );
4843    }
4844
4845    fn confirm_slot_entries_for_tests(
4846        bank: &Arc<Bank>,
4847        slot_entries: Vec<Entry>,
4848        slot_full: bool,
4849        prev_entry_hash: Hash,
4850    ) -> result::Result<(), BlockstoreProcessorError> {
4851        let replay_tx_thread_pool = create_thread_pool(1);
4852        confirm_slot_entries(
4853            &BankWithScheduler::new_without_scheduler(bank.clone()),
4854            &replay_tx_thread_pool,
4855            (slot_entries, 0, slot_full),
4856            &mut ConfirmationTiming::default(),
4857            &mut ConfirmationProgress::new(prev_entry_hash),
4858            false,
4859            None,
4860            None,
4861            None,
4862            &VerifyRecyclers::default(),
4863            None,
4864            &PrioritizationFeeCache::new(0u64),
4865        )
4866    }
4867
4868    fn create_test_transactions(
4869        mint_keypair: &Keypair,
4870        genesis_hash: &Hash,
4871    ) -> Vec<RuntimeTransaction<SanitizedTransaction>> {
4872        let pubkey = clone_solana_pubkey::new_rand();
4873        let keypair2 = Keypair::new();
4874        let pubkey2 = clone_solana_pubkey::new_rand();
4875        let keypair3 = Keypair::new();
4876        let pubkey3 = clone_solana_pubkey::new_rand();
4877
4878        vec![
4879            RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer(
4880                mint_keypair,
4881                &pubkey,
4882                1,
4883                *genesis_hash,
4884            )),
4885            RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer(
4886                &keypair2,
4887                &pubkey2,
4888                1,
4889                *genesis_hash,
4890            )),
4891            RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer(
4892                &keypair3,
4893                &pubkey3,
4894                1,
4895                *genesis_hash,
4896            )),
4897        ]
4898    }
4899
4900    #[test]
4901    fn test_confirm_slot_entries_progress_num_txs_indexes() {
4902        let GenesisConfigInfo {
4903            genesis_config,
4904            mint_keypair,
4905            ..
4906        } = create_genesis_config(100 * LAMPORTS_PER_SOL);
4907        let genesis_hash = genesis_config.hash();
4908        let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
4909        let bank = BankWithScheduler::new_without_scheduler(bank);
4910        let replay_tx_thread_pool = create_thread_pool(1);
4911        let mut timing = ConfirmationTiming::default();
4912        let mut progress = ConfirmationProgress::new(genesis_hash);
4913        let amount = genesis_config.rent.minimum_balance(0);
4914        let keypair1 = Keypair::new();
4915        let keypair2 = Keypair::new();
4916        let keypair3 = Keypair::new();
4917        let keypair4 = Keypair::new();
4918        bank.transfer(LAMPORTS_PER_SOL, &mint_keypair, &keypair1.pubkey())
4919            .unwrap();
4920        bank.transfer(LAMPORTS_PER_SOL, &mint_keypair, &keypair2.pubkey())
4921            .unwrap();
4922
4923        let (transaction_status_sender, transaction_status_receiver) =
4924            crossbeam_channel::unbounded();
4925        let transaction_status_sender = TransactionStatusSender {
4926            sender: transaction_status_sender,
4927        };
4928
4929        let blockhash = bank.last_blockhash();
4930        let tx1 = system_transaction::transfer(
4931            &keypair1,
4932            &keypair3.pubkey(),
4933            amount,
4934            bank.last_blockhash(),
4935        );
4936        let tx2 = system_transaction::transfer(
4937            &keypair2,
4938            &keypair4.pubkey(),
4939            amount,
4940            bank.last_blockhash(),
4941        );
4942        let entry = next_entry(&blockhash, 1, vec![tx1, tx2]);
4943        let new_hash = entry.hash;
4944
4945        confirm_slot_entries(
4946            &bank,
4947            &replay_tx_thread_pool,
4948            (vec![entry], 0, false),
4949            &mut timing,
4950            &mut progress,
4951            false,
4952            Some(&transaction_status_sender),
4953            None,
4954            None,
4955            &VerifyRecyclers::default(),
4956            None,
4957            &PrioritizationFeeCache::new(0u64),
4958        )
4959        .unwrap();
4960        assert_eq!(progress.num_txs, 2);
4961        let batch = transaction_status_receiver.recv().unwrap();
4962        if let TransactionStatusMessage::Batch(batch) = batch {
4963            assert_eq!(batch.transactions.len(), 2);
4964            assert_eq!(batch.transaction_indexes.len(), 2);
4965            assert_eq!(batch.transaction_indexes, [0, 1]);
4966        } else {
4967            panic!("batch should have been sent");
4968        }
4969
4970        let tx1 = system_transaction::transfer(
4971            &keypair1,
4972            &keypair3.pubkey(),
4973            amount + 1,
4974            bank.last_blockhash(),
4975        );
4976        let tx2 = system_transaction::transfer(
4977            &keypair2,
4978            &keypair4.pubkey(),
4979            amount + 1,
4980            bank.last_blockhash(),
4981        );
4982        let tx3 = system_transaction::transfer(
4983            &mint_keypair,
4984            &Pubkey::new_unique(),
4985            amount,
4986            bank.last_blockhash(),
4987        );
4988        let entry = next_entry(&new_hash, 1, vec![tx1, tx2, tx3]);
4989
4990        confirm_slot_entries(
4991            &bank,
4992            &replay_tx_thread_pool,
4993            (vec![entry], 0, false),
4994            &mut timing,
4995            &mut progress,
4996            false,
4997            Some(&transaction_status_sender),
4998            None,
4999            None,
5000            &VerifyRecyclers::default(),
5001            None,
5002            &PrioritizationFeeCache::new(0u64),
5003        )
5004        .unwrap();
5005        assert_eq!(progress.num_txs, 5);
5006        let batch = transaction_status_receiver.recv().unwrap();
5007        if let TransactionStatusMessage::Batch(batch) = batch {
5008            assert_eq!(batch.transactions.len(), 3);
5009            assert_eq!(batch.transaction_indexes.len(), 3);
5010            assert_eq!(batch.transaction_indexes, [2, 3, 4]);
5011        } else {
5012            panic!("batch should have been sent");
5013        }
5014    }
5015
5016    #[test]
5017    fn test_rebatch_transactions() {
5018        let dummy_leader_pubkey = clone_solana_pubkey::new_rand();
5019        let GenesisConfigInfo {
5020            genesis_config,
5021            mint_keypair,
5022            ..
5023        } = create_genesis_config_with_leader(500, &dummy_leader_pubkey, 100);
5024        let bank = Arc::new(Bank::new_for_tests(&genesis_config));
5025        let txs = create_test_transactions(&mint_keypair, &genesis_config.hash());
5026        let lock_results = bank.try_lock_accounts(&txs);
5027        assert!(lock_results.iter().all(Result::is_ok));
5028
5029        let transaction_indexes = vec![42, 43, 44];
5030
5031        let batch = rebatch_transactions(&lock_results, &bank, &txs, 0..1, &transaction_indexes);
5032        assert!(batch.batch.needs_unlock());
5033        assert_eq!(batch.transaction_indexes, vec![42]);
5034
5035        let batch2 = rebatch_transactions(&lock_results, &bank, &txs, 1..3, &transaction_indexes);
5036        assert!(batch2.batch.needs_unlock());
5037        assert_eq!(batch2.transaction_indexes, vec![43, 44]);
5038    }
5039
5040    fn do_test_schedule_batches_for_execution(should_succeed: bool) {
5041        clone_solana_logger::setup();
5042        let dummy_leader_pubkey = clone_solana_pubkey::new_rand();
5043        let GenesisConfigInfo {
5044            genesis_config,
5045            mint_keypair,
5046            ..
5047        } = create_genesis_config_with_leader(500, &dummy_leader_pubkey, 100);
5048        let bank = Arc::new(Bank::new_for_tests(&genesis_config));
5049        let context = SchedulingContext::new(bank.clone());
5050
5051        let txs = create_test_transactions(&mint_keypair, &genesis_config.hash());
5052
5053        let mut mocked_scheduler = MockInstalledScheduler::new();
5054        let seq = Arc::new(Mutex::new(mockall::Sequence::new()));
5055        let seq_cloned = seq.clone();
5056        mocked_scheduler
5057            .expect_context()
5058            .times(1)
5059            .in_sequence(&mut seq.lock().unwrap())
5060            .return_const(context);
5061        if should_succeed {
5062            mocked_scheduler
5063                .expect_schedule_execution()
5064                .times(txs.len())
5065                .returning(|_, _| Ok(()));
5066        } else {
5067            // mocked_scheduler isn't async; so short-circuiting behavior is quite visible in that
5068            // .times(1) is called instead of .times(txs.len()), not like the succeeding case
5069            mocked_scheduler
5070                .expect_schedule_execution()
5071                .times(1)
5072                .returning(|_, _| Err(SchedulerAborted));
5073            mocked_scheduler
5074                .expect_recover_error_after_abort()
5075                .times(1)
5076                .returning(|| TransactionError::InsufficientFundsForFee);
5077        }
5078        mocked_scheduler
5079            .expect_wait_for_termination()
5080            .with(mockall::predicate::eq(true))
5081            .times(1)
5082            .in_sequence(&mut seq.lock().unwrap())
5083            .returning(move |_| {
5084                let mut mocked_uninstalled_scheduler = MockUninstalledScheduler::new();
5085                mocked_uninstalled_scheduler
5086                    .expect_return_to_pool()
5087                    .times(1)
5088                    .in_sequence(&mut seq_cloned.lock().unwrap())
5089                    .returning(|| ());
5090                (
5091                    (Ok(()), ExecuteTimings::default()),
5092                    Box::new(mocked_uninstalled_scheduler),
5093                )
5094            });
5095        let bank = BankWithScheduler::new(bank, Some(Box::new(mocked_scheduler)));
5096
5097        let locked_entry = LockedTransactionsWithIndexes {
5098            lock_results: bank.try_lock_accounts(&txs),
5099            transactions: txs,
5100            starting_index: 0,
5101        };
5102
5103        let replay_tx_thread_pool = create_thread_pool(1);
5104        let mut batch_execution_timing = BatchExecutionTiming::default();
5105        let ignored_prioritization_fee_cache = PrioritizationFeeCache::new(0u64);
5106        let result = process_batches(
5107            &bank,
5108            &replay_tx_thread_pool,
5109            [locked_entry].into_iter(),
5110            None,
5111            None,
5112            &mut batch_execution_timing,
5113            None,
5114            &ignored_prioritization_fee_cache,
5115        );
5116        if should_succeed {
5117            assert_matches!(result, Ok(()));
5118        } else {
5119            assert_matches!(result, Err(TransactionError::InsufficientFundsForFee));
5120        }
5121    }
5122
5123    #[test]
5124    fn test_schedule_batches_for_execution_success() {
5125        do_test_schedule_batches_for_execution(true);
5126    }
5127
5128    #[test]
5129    fn test_schedule_batches_for_execution_failure() {
5130        do_test_schedule_batches_for_execution(false);
5131    }
5132
5133    enum TxResult {
5134        ExecutedWithSuccess,
5135        ExecutedWithFailure,
5136        NotExecuted,
5137    }
5138
5139    #[test_matrix(
5140        [TxResult::ExecutedWithSuccess, TxResult::ExecutedWithFailure, TxResult::NotExecuted],
5141        [Ok(None), Ok(Some(4)), Err(TransactionError::CommitCancelled)]
5142    )]
5143    fn test_execute_batch_pre_commit_callback(
5144        tx_result: TxResult,
5145        poh_result: Result<Option<usize>>,
5146    ) {
5147        clone_solana_logger::setup();
5148        let dummy_leader_pubkey = clone_solana_sdk::pubkey::new_rand();
5149        let GenesisConfigInfo {
5150            genesis_config,
5151            mint_keypair,
5152            ..
5153        } = create_genesis_config_with_leader(500, &dummy_leader_pubkey, 100);
5154        let bank = Bank::new_for_tests(&genesis_config);
5155        let (bank, _bank_forks) = bank.wrap_with_bank_forks_for_tests();
5156        let bank = Arc::new(bank);
5157        let pubkey = clone_solana_sdk::pubkey::new_rand();
5158        let (tx, expected_tx_result) = match tx_result {
5159            TxResult::ExecutedWithSuccess => (
5160                RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer(
5161                    &mint_keypair,
5162                    &pubkey,
5163                    1,
5164                    genesis_config.hash(),
5165                )),
5166                Ok(()),
5167            ),
5168            TxResult::ExecutedWithFailure => (
5169                RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer(
5170                    &mint_keypair,
5171                    &pubkey,
5172                    100000000,
5173                    genesis_config.hash(),
5174                )),
5175                Ok(()),
5176            ),
5177            TxResult::NotExecuted => (
5178                RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer(
5179                    &mint_keypair,
5180                    &pubkey,
5181                    1,
5182                    Hash::default(),
5183                )),
5184                Err(TransactionError::BlockhashNotFound),
5185            ),
5186        };
5187        let mut batch = TransactionBatch::new(
5188            vec![Ok(()); 1],
5189            &bank,
5190            OwnedOrBorrowed::Borrowed(slice::from_ref(&tx)),
5191        );
5192        batch.set_needs_unlock(false);
5193        let poh_with_index = matches!(&poh_result, Ok(Some(_)));
5194        let batch = TransactionBatchWithIndexes {
5195            batch,
5196            transaction_indexes: vec![],
5197        };
5198        let prioritization_fee_cache = PrioritizationFeeCache::default();
5199        let mut timing = ExecuteTimings::default();
5200        let (sender, receiver) = crossbeam_channel::unbounded();
5201
5202        assert_eq!(bank.transaction_count(), 0);
5203        assert_eq!(bank.transaction_error_count(), 0);
5204        let should_commit = poh_result.is_ok();
5205        let mut is_called = false;
5206        let result = execute_batch(
5207            &batch,
5208            &bank,
5209            Some(&TransactionStatusSender { sender }),
5210            None,
5211            &mut timing,
5212            None,
5213            &prioritization_fee_cache,
5214            Some(|processing_result: &'_ Result<_>| {
5215                is_called = true;
5216                let ok = poh_result?;
5217                if let Err(error) = processing_result {
5218                    Err(error.clone())?;
5219                };
5220                Ok(ok)
5221            }),
5222        );
5223
5224        // pre_commit_callback() should alwasy be called regardless of tx_result
5225        assert!(is_called);
5226
5227        if should_commit {
5228            assert_eq!(result, expected_tx_result);
5229            if expected_tx_result.is_ok() {
5230                assert_eq!(bank.transaction_count(), 1);
5231                if matches!(tx_result, TxResult::ExecutedWithFailure) {
5232                    assert_eq!(bank.transaction_error_count(), 1);
5233                } else {
5234                    assert_eq!(bank.transaction_error_count(), 0);
5235                }
5236            } else {
5237                assert_eq!(bank.transaction_count(), 0);
5238            }
5239        } else {
5240            assert_matches!(result, Err(TransactionError::CommitCancelled));
5241            assert_eq!(bank.transaction_count(), 0);
5242        }
5243        if poh_with_index && expected_tx_result.is_ok() {
5244            assert_matches!(
5245                receiver.try_recv(),
5246                Ok(TransactionStatusMessage::Batch(TransactionStatusBatch{transaction_indexes, ..}))
5247                    if transaction_indexes == vec![4_usize]
5248            );
5249        } else if should_commit && expected_tx_result.is_ok() {
5250            assert_matches!(
5251                receiver.try_recv(),
5252                Ok(TransactionStatusMessage::Batch(TransactionStatusBatch{transaction_indexes, ..}))
5253                    if transaction_indexes.is_empty()
5254            );
5255        } else {
5256            assert_matches!(receiver.try_recv(), Err(_));
5257        }
5258    }
5259
5260    #[test]
5261    fn test_confirm_slot_entries_with_fix() {
5262        const HASHES_PER_TICK: u64 = 10;
5263        const TICKS_PER_SLOT: u64 = 2;
5264
5265        let collector_id = Pubkey::new_unique();
5266
5267        let GenesisConfigInfo {
5268            mut genesis_config,
5269            mint_keypair,
5270            ..
5271        } = create_genesis_config(10_000);
5272        genesis_config.poh_config.hashes_per_tick = Some(HASHES_PER_TICK);
5273        genesis_config.ticks_per_slot = TICKS_PER_SLOT;
5274        let genesis_hash = genesis_config.hash();
5275
5276        let (slot_0_bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
5277        assert_eq!(slot_0_bank.slot(), 0);
5278        assert_eq!(slot_0_bank.tick_height(), 0);
5279        assert_eq!(slot_0_bank.max_tick_height(), 2);
5280        assert_eq!(slot_0_bank.last_blockhash(), genesis_hash);
5281        assert_eq!(slot_0_bank.get_hash_age(&genesis_hash), Some(0));
5282
5283        let slot_0_entries = entry::create_ticks(TICKS_PER_SLOT, HASHES_PER_TICK, genesis_hash);
5284        let slot_0_hash = slot_0_entries.last().unwrap().hash;
5285        confirm_slot_entries_for_tests(&slot_0_bank, slot_0_entries, true, genesis_hash).unwrap();
5286        assert_eq!(slot_0_bank.tick_height(), slot_0_bank.max_tick_height());
5287        assert_eq!(slot_0_bank.last_blockhash(), slot_0_hash);
5288        assert_eq!(slot_0_bank.get_hash_age(&genesis_hash), Some(1));
5289        assert_eq!(slot_0_bank.get_hash_age(&slot_0_hash), Some(0));
5290
5291        let new_bank = Bank::new_from_parent(slot_0_bank, &collector_id, 2);
5292        let slot_2_bank = bank_forks
5293            .write()
5294            .unwrap()
5295            .insert(new_bank)
5296            .clone_without_scheduler();
5297        assert_eq!(slot_2_bank.slot(), 2);
5298        assert_eq!(slot_2_bank.tick_height(), 2);
5299        assert_eq!(slot_2_bank.max_tick_height(), 6);
5300        assert_eq!(slot_2_bank.last_blockhash(), slot_0_hash);
5301
5302        let slot_1_entries = entry::create_ticks(TICKS_PER_SLOT, HASHES_PER_TICK, slot_0_hash);
5303        let slot_1_hash = slot_1_entries.last().unwrap().hash;
5304        confirm_slot_entries_for_tests(&slot_2_bank, slot_1_entries, false, slot_0_hash).unwrap();
5305        assert_eq!(slot_2_bank.tick_height(), 4);
5306        assert_eq!(slot_2_bank.last_blockhash(), slot_0_hash);
5307        assert_eq!(slot_2_bank.get_hash_age(&genesis_hash), Some(1));
5308        assert_eq!(slot_2_bank.get_hash_age(&slot_0_hash), Some(0));
5309
5310        struct TestCase {
5311            recent_blockhash: Hash,
5312            expected_result: result::Result<(), BlockstoreProcessorError>,
5313        }
5314
5315        let test_cases = [
5316            TestCase {
5317                recent_blockhash: slot_1_hash,
5318                expected_result: Err(BlockstoreProcessorError::InvalidTransaction(
5319                    TransactionError::BlockhashNotFound,
5320                )),
5321            },
5322            TestCase {
5323                recent_blockhash: slot_0_hash,
5324                expected_result: Ok(()),
5325            },
5326        ];
5327
5328        // Check that slot 2 transactions can only use hashes for completed blocks.
5329        for TestCase {
5330            recent_blockhash,
5331            expected_result,
5332        } in test_cases
5333        {
5334            let slot_2_entries = {
5335                let to_pubkey = Pubkey::new_unique();
5336                let mut prev_entry_hash = slot_1_hash;
5337                let mut remaining_entry_hashes = HASHES_PER_TICK;
5338
5339                let tx =
5340                    system_transaction::transfer(&mint_keypair, &to_pubkey, 1, recent_blockhash);
5341                remaining_entry_hashes = remaining_entry_hashes.checked_sub(1).unwrap();
5342                let mut entries = vec![next_entry_mut(&mut prev_entry_hash, 1, vec![tx])];
5343
5344                entries.push(next_entry_mut(
5345                    &mut prev_entry_hash,
5346                    remaining_entry_hashes,
5347                    vec![],
5348                ));
5349                entries.push(next_entry_mut(
5350                    &mut prev_entry_hash,
5351                    HASHES_PER_TICK,
5352                    vec![],
5353                ));
5354
5355                entries
5356            };
5357
5358            let slot_2_hash = slot_2_entries.last().unwrap().hash;
5359            let result =
5360                confirm_slot_entries_for_tests(&slot_2_bank, slot_2_entries, true, slot_1_hash);
5361            match (result, expected_result) {
5362                (Ok(()), Ok(())) => {
5363                    assert_eq!(slot_2_bank.tick_height(), slot_2_bank.max_tick_height());
5364                    assert_eq!(slot_2_bank.last_blockhash(), slot_2_hash);
5365                    assert_eq!(slot_2_bank.get_hash_age(&genesis_hash), Some(2));
5366                    assert_eq!(slot_2_bank.get_hash_age(&slot_0_hash), Some(1));
5367                    assert_eq!(slot_2_bank.get_hash_age(&slot_2_hash), Some(0));
5368                }
5369                (
5370                    Err(BlockstoreProcessorError::InvalidTransaction(err)),
5371                    Err(BlockstoreProcessorError::InvalidTransaction(expected_err)),
5372                ) => {
5373                    assert_eq!(err, expected_err);
5374                }
5375                (result, expected_result) => {
5376                    panic!("actual result {result:?} != expected result {expected_result:?}");
5377                }
5378            }
5379        }
5380    }
5381
5382    #[test]
5383    fn test_check_block_cost_limit() {
5384        let dummy_leader_pubkey = clone_solana_pubkey::new_rand();
5385        let GenesisConfigInfo {
5386            genesis_config,
5387            mint_keypair,
5388            ..
5389        } = create_genesis_config_with_leader(500, &dummy_leader_pubkey, 100);
5390        let bank = Bank::new_for_tests(&genesis_config);
5391
5392        let tx = RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer(
5393            &mint_keypair,
5394            &Pubkey::new_unique(),
5395            1,
5396            genesis_config.hash(),
5397        ));
5398        let mut tx_cost = CostModel::calculate_cost(&tx, &bank.feature_set);
5399        let actual_execution_cu = 1;
5400        let actual_loaded_accounts_data_size = 64 * 1024;
5401        let TransactionCost::Transaction(ref mut usage_cost_details) = tx_cost else {
5402            unreachable!("test tx is non-vote tx");
5403        };
5404        usage_cost_details.programs_execution_cost = actual_execution_cu;
5405        usage_cost_details.loaded_accounts_data_size_cost =
5406            CostModel::calculate_loaded_accounts_data_size_cost(
5407                actual_loaded_accounts_data_size,
5408                &bank.feature_set,
5409            );
5410        // set block-limit to be able to just have one transaction
5411        let block_limit = tx_cost.sum();
5412
5413        bank.write_cost_tracker()
5414            .unwrap()
5415            .set_limits(u64::MAX, block_limit, u64::MAX);
5416        let txs = vec![tx.clone(), tx];
5417        let processing_results = vec![
5418            Ok(ProcessedTransaction::Executed(Box::new(
5419                ExecutedTransaction {
5420                    execution_details: TransactionExecutionDetails {
5421                        status: Ok(()),
5422                        log_messages: None,
5423                        inner_instructions: None,
5424                        return_data: None,
5425                        executed_units: actual_execution_cu,
5426                        accounts_data_len_delta: 0,
5427                    },
5428                    loaded_transaction: LoadedTransaction {
5429                        loaded_accounts_data_size: actual_loaded_accounts_data_size,
5430                        ..LoadedTransaction::default()
5431                    },
5432                    programs_modified_by_tx: HashMap::new(),
5433                },
5434            ))),
5435            Err(TransactionError::AccountNotFound),
5436        ];
5437
5438        assert!(check_block_cost_limits(&bank, &processing_results, &txs).is_ok());
5439        assert_eq!(
5440            Err(TransactionError::WouldExceedMaxBlockCostLimit),
5441            check_block_cost_limits(&bank, &processing_results, &txs)
5442        );
5443    }
5444}