Skip to main content

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        transaction_balances::compile_collected_balances,
9        use_snapshot_archives_at_startup::UseSnapshotArchivesAtStartup,
10    },
11    ExecuteTimingType::{NumExecuteBatches, TotalBatchesLen},
12    agave_votor_messages::{consensus_message::ConsensusMessage, migration::MigrationStatus},
13    chrono_humanize::{Accuracy, HumanTime, Tense},
14    crossbeam_channel::{Receiver, Sender},
15    itertools::Itertools,
16    log::*,
17    rayon::{ThreadPool, prelude::*},
18    scopeguard::defer,
19    solana_accounts_db::{
20        accounts_db::AccountsDbConfig, accounts_update_notifier_interface::AccountsUpdateNotifier,
21    },
22    solana_clock::{BankId, Slot},
23    solana_cost_model::{cost_model::CostModel, transaction_cost::TransactionCost},
24    solana_entry::{
25        block_component::BlockComponent,
26        entry::{self, Entry, EntrySlice, EntryType, create_ticks},
27    },
28    solana_genesis_config::GenesisConfig,
29    solana_hash::Hash,
30    solana_keypair::Keypair,
31    solana_measure::{measure::Measure, measure_us},
32    solana_metrics::datapoint_error,
33    solana_pubkey::Pubkey,
34    solana_runtime::{
35        bank::{Bank, PreCommitResult, TransactionBalancesSet},
36        bank_forks::BankForks,
37        bank_utils,
38        block_component_processor::BlockComponentProcessorError,
39        commitment::VOTE_THRESHOLD_SIZE,
40        dependency_tracker::DependencyTracker,
41        installed_scheduler_pool::BankWithScheduler,
42        leader_schedule_utils::leader_slot_index,
43        prioritization_fee_cache::PrioritizationFeeCache,
44        runtime_config::RuntimeConfig,
45        snapshot_controller::SnapshotController,
46        transaction_batch::{OwnedOrBorrowed, TransactionBatch},
47        vote_sender_types::{ReplayVoteMessage, ReplayVoteSendType, ReplayVoteSender},
48    },
49    solana_runtime_transaction::{
50        runtime_transaction::RuntimeTransaction, transaction_with_meta::TransactionWithMeta,
51    },
52    solana_shred_version::compute_shred_version,
53    solana_signature::Signature,
54    solana_svm::{
55        transaction_commit_result::{TransactionCommitResult, TransactionCommitResultExtensions},
56        transaction_processing_result::ProcessedTransaction,
57        transaction_processor::ExecutionRecordingConfig,
58    },
59    solana_svm_timings::{ExecuteTimingType, ExecuteTimings, report_execute_timings},
60    solana_svm_transaction::{svm_message::SVMMessage, svm_transaction::SVMTransaction},
61    solana_transaction::{
62        TransactionVerificationMode, sanitized::SanitizedTransaction,
63        versioned::VersionedTransaction,
64    },
65    solana_transaction_error::{TransactionError, TransactionResult as Result},
66    solana_transaction_status::token_balances::TransactionTokenBalancesSet,
67    solana_vote::{vote_account::VoteAccountsHashMap, vote_parser::is_valid_vote_only_transaction},
68    std::{
69        borrow::Cow,
70        cmp,
71        collections::{HashMap, HashSet},
72        num::Saturating,
73        ops::Index,
74        path::PathBuf,
75        result,
76        sync::{Arc, Mutex, OnceLock, RwLock, atomic::AtomicBool},
77        time::{Duration, Instant},
78        vec::Drain,
79    },
80    thiserror::Error,
81};
82#[cfg(feature = "dev-context-only-utils")]
83use {qualifier_attr::qualifiers, solana_runtime::bank::HashOverrides};
84
85pub struct TransactionBatchWithIndexes<'a, 'b, Tx: SVMMessage> {
86    pub batch: TransactionBatch<'a, 'b, Tx>,
87    pub transaction_indexes: Vec<usize>,
88}
89
90// `TransactionBatchWithIndexes` but without the `Drop` that prevents
91// us from nicely unwinding these with manual unlocking.
92pub struct LockedTransactionsWithIndexes<Tx: SVMMessage> {
93    lock_results: Vec<Result<()>>,
94    transactions: Vec<RuntimeTransaction<Tx>>,
95    starting_index: usize,
96}
97
98struct ReplayEntry {
99    entry: EntryType<RuntimeTransaction<SanitizedTransaction>>,
100    starting_index: usize,
101}
102
103fn first_err(results: &[Result<()>]) -> Result<()> {
104    for r in results {
105        if r.is_err() {
106            return r.clone();
107        }
108    }
109    Ok(())
110}
111
112/// Result of checking a child slot's chained block ID against its parent.
113pub enum ChainedBlockIdCheck {
114    /// Feature not active; no validation performed.
115    Inactive,
116    /// Chained block ID matches (or parent has no block ID to compare).
117    Pass,
118    /// Definitive mismatch between child's chained merkle root and parent's block ID.
119    Mismatch,
120    /// Data shred 0 not received yet; cannot determine chained block ID.
121    Unavailable,
122}
123
124// Includes transaction signature for unit-testing
125fn do_get_first_error<T, Tx: SVMTransaction>(
126    batch: &TransactionBatch<Tx>,
127    results: &[Result<T>],
128) -> Option<(Result<()>, Signature)> {
129    let mut first_err = None;
130    for (result, transaction) in results.iter().zip(batch.sanitized_transactions()) {
131        if let Err(err) = result {
132            if first_err.is_none() {
133                first_err = Some((Err(err.clone()), *transaction.signature()));
134            }
135            warn!("Unexpected validator error: {err:?}, transaction: {transaction:?}");
136            datapoint_error!(
137                "validator_process_entry_error",
138                (
139                    "error",
140                    format!("error: {err:?}, transaction: {transaction:?}"),
141                    String
142                )
143            );
144        }
145    }
146    first_err
147}
148
149fn get_first_error<T, Tx: SVMTransaction>(
150    batch: &TransactionBatch<Tx>,
151    commit_results: &[Result<T>],
152) -> Result<()> {
153    do_get_first_error(batch, commit_results)
154        .map(|(error, _signature)| error)
155        .unwrap_or(Ok(()))
156}
157
158#[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
159fn create_thread_pool(num_threads: usize) -> ThreadPool {
160    rayon::ThreadPoolBuilder::new()
161        .num_threads(num_threads)
162        .stack_size(8 * 1024 * 1024)
163        .thread_name(|i| format!("solReplayTx{i:02}"))
164        .build()
165        .expect("new rayon threadpool")
166}
167
168fn transaction_hash_verify_thread_pool() -> &'static ThreadPool {
169    const TX_HASH_VERIFY_THREAD_POOL_SIZE: usize = 4;
170    static TX_HASH_VERIFY_THREAD_POOL: OnceLock<ThreadPool> = OnceLock::new();
171    TX_HASH_VERIFY_THREAD_POOL.get_or_init(|| {
172        rayon::ThreadPoolBuilder::new()
173            .num_threads(TX_HASH_VERIFY_THREAD_POOL_SIZE)
174            .thread_name(|i| format!("solReplayHash{i:02}"))
175            .build()
176            .expect("new transaction hash verify rayon threadpool")
177    })
178}
179
180pub fn execute_batch<'a>(
181    batch: &'a TransactionBatchWithIndexes<impl TransactionWithMeta>,
182    bank: &'a Arc<Bank>,
183    transaction_status_sender: Option<&'a TransactionStatusSender>,
184    replay_vote_sender: Option<&'a ReplayVoteSender>,
185    replay_vote_send_type: ReplayVoteSendType,
186    timings: &'a mut ExecuteTimings,
187    log_messages_bytes_limit: Option<usize>,
188    prioritization_fee_cache: Option<&'a PrioritizationFeeCache>,
189    extra_pre_commit_callback: Option<
190        impl FnOnce(&Result<ProcessedTransaction>) -> Result<Option<usize>>,
191    >,
192) -> Result<()> {
193    let TransactionBatchWithIndexes {
194        batch,
195        transaction_indexes,
196    } = batch;
197
198    // extra_pre_commit_callback allows for reuse of this function between the
199    // unified scheduler block production path and block verification path(s)
200    //   Some(_) => unified scheduler block production path
201    //   None    => block verification path(s)
202    let block_verification = extra_pre_commit_callback.is_none();
203    let record_transaction_meta = transaction_status_sender.is_some();
204    let mut transaction_indexes = Cow::from(transaction_indexes);
205
206    let pre_commit_callback = |_timings: &mut _, processing_results: &_| -> PreCommitResult {
207        match extra_pre_commit_callback {
208            None => {
209                // We're entering into one of the block-verification methods.
210                get_first_error(batch, processing_results)?;
211                Ok(None)
212            }
213            Some(extra_pre_commit_callback) => {
214                // We're entering into the block-production unified scheduler special case...
215                // `processing_results` should always contain exactly only 1 result in that case.
216                let [result] = processing_results else {
217                    panic!("unexpected result count: {}", processing_results.len());
218                };
219                // transaction_indexes is intended to be populated later; so barely-initialized vec
220                // should be provided.
221                assert!(transaction_indexes.is_empty());
222
223                // From now on, we need to freeze-lock the tpu bank, in order to prevent it from
224                // freezing in the middle of this code-path. Otherwise, the assertion at the start
225                // of commit_transactions() would trigger panic because it's fatal runtime
226                // invariant violation.
227                let freeze_lock = bank.freeze_lock();
228
229                // `result` won't be examined at all here. Rather, `extra_pre_commit_callback` is
230                // responsible for all result handling, including the very basic precondition of
231                // successful execution of transactions as well.
232                let committed_index = extra_pre_commit_callback(result)?;
233
234                // The callback succeeded. Optionally, update transaction_indexes as well.
235                // Refer to TaskHandler::handle()'s transaction_indexes initialization for further
236                // background.
237                if let Some(index) = committed_index {
238                    let transaction_indexes = transaction_indexes.to_mut();
239                    // Adjust the empty new vec with the exact needed capacity. Otherwise, excess
240                    // cap would be reserved on `.push()` in it.
241                    transaction_indexes.reserve_exact(1);
242                    transaction_indexes.push(index);
243                }
244                // At this point, poh should have been succeeded so it's guaranteed that the bank
245                // hasn't been frozen yet and we're still holding the lock. So, it's okay to pass
246                // down freeze_lock without any introspection here to be unconditionally dropped
247                // after commit_transactions(). This reasoning is same as
248                // solana_core::banking_stage::Consumer::execute_and_commit_transactions_locked()
249                Ok(Some(freeze_lock))
250            }
251        }
252    };
253
254    let (commit_results, balance_collector) = batch
255        .bank()
256        .load_execute_and_commit_transactions_with_pre_commit_callback(
257            batch,
258            ExecutionRecordingConfig::new_single_setting(transaction_status_sender.is_some()),
259            timings,
260            log_messages_bytes_limit,
261            pre_commit_callback,
262        )?;
263
264    let mut check_block_costs_elapsed = Measure::start("check_block_costs");
265    let tx_costs = if block_verification {
266        // Block verification (including unified scheduler) case;
267        // collect and check transaction costs
268        let tx_costs = get_transaction_costs(bank, &commit_results, batch.sanitized_transactions());
269        check_block_cost_limits(bank, &tx_costs).map(|_| tx_costs)
270    } else if record_transaction_meta {
271        // Unified scheduler block production case;
272        // the scheduler will track costs elsewhere but costs are recalculated
273        // here so they can be recorded with other transaction metadata
274        Ok(get_transaction_costs(
275            bank,
276            &commit_results,
277            batch.sanitized_transactions(),
278        ))
279    } else {
280        // Unified scheduler block production without metadata recording
281        Ok(vec![])
282    };
283    check_block_costs_elapsed.stop();
284    timings.saturating_add_in_place(
285        ExecuteTimingType::CheckBlockLimitsUs,
286        check_block_costs_elapsed.as_us(),
287    );
288    let tx_costs = tx_costs?;
289
290    bank_utils::find_and_send_votes(
291        batch.sanitized_transactions(),
292        &commit_results,
293        replay_vote_sender,
294        replay_vote_send_type,
295    );
296
297    if let Some(prioritization_fee_cache) = prioritization_fee_cache {
298        let committed_transactions = commit_results
299            .iter()
300            .zip(batch.sanitized_transactions())
301            .filter_map(|(commit_result, tx)| commit_result.was_committed().then_some(tx));
302        prioritization_fee_cache.update(bank, committed_transactions);
303    }
304    if let Some(transaction_status_sender) = transaction_status_sender {
305        let transactions: Vec<SanitizedTransaction> = batch
306            .sanitized_transactions()
307            .iter()
308            .map(|tx| tx.as_sanitized_transaction().into_owned())
309            .collect();
310
311        // There are two cases where balance_collector could be None:
312        // * Balance recording is disabled. If that were the case, there would
313        //   be no TransactionStatusSender, and we would not be in this branch.
314        // * The batch was aborted in its entirety in SVM. In that case, nothing
315        //   would have been committed.
316        // Therefore this should always be true.
317        debug_assert!(balance_collector.is_some());
318
319        let (balances, token_balances) =
320            compile_collected_balances(balance_collector.unwrap_or_default());
321
322        // The length of costs vector needs to be consistent with all other
323        // vectors that are sent over (such as `transactions`). So, replace the
324        // None elements with Some(0)
325        let tx_costs = tx_costs
326            .into_iter()
327            .map(|tx_cost_option| tx_cost_option.map(|tx_cost| tx_cost.sum()).or(Some(0)))
328            .collect();
329
330        transaction_status_sender.send_transaction_status_batch(
331            bank.slot(),
332            transactions,
333            commit_results,
334            balances,
335            token_balances,
336            tx_costs,
337            transaction_indexes.into_owned(),
338        );
339    }
340
341    Ok(())
342}
343
344// Get actual transaction execution costs from transaction commit results
345fn get_transaction_costs<'a, Tx: TransactionWithMeta>(
346    bank: &Bank,
347    commit_results: &[TransactionCommitResult],
348    sanitized_transactions: &'a [Tx],
349) -> Vec<Option<TransactionCost<'a, Tx>>> {
350    assert_eq!(sanitized_transactions.len(), commit_results.len());
351
352    commit_results
353        .iter()
354        .zip(sanitized_transactions)
355        .map(|(commit_result, tx)| {
356            if let Ok(committed_tx) = commit_result {
357                Some(CostModel::calculate_cost_for_executed_transaction(
358                    tx,
359                    committed_tx.executed_units,
360                    committed_tx.loaded_account_stats.loaded_accounts_data_size,
361                    &bank.feature_set,
362                ))
363            } else {
364                None
365            }
366        })
367        .collect()
368}
369
370fn check_block_cost_limits<Tx: TransactionWithMeta>(
371    bank: &Bank,
372    tx_costs: &[Option<TransactionCost<'_, Tx>>],
373) -> Result<()> {
374    let mut cost_tracker = bank.write_cost_tracker().unwrap();
375    for tx_cost in tx_costs.iter().flatten() {
376        cost_tracker
377            .try_add(tx_cost)
378            .map_err(TransactionError::from)?;
379    }
380
381    Ok(())
382}
383
384#[derive(Default)]
385pub struct ExecuteBatchesInternalMetrics {
386    execution_timings_per_thread: HashMap<usize, ThreadExecuteTimings>,
387    total_batches_len: u64,
388    execute_batches_us: u64,
389}
390
391impl ExecuteBatchesInternalMetrics {
392    pub fn new_with_timings_from_all_threads(execute_timings: ExecuteTimings) -> Self {
393        const DUMMY_THREAD_INDEX: usize = 999;
394        let mut new = Self::default();
395        new.execution_timings_per_thread.insert(
396            DUMMY_THREAD_INDEX,
397            ThreadExecuteTimings {
398                execute_timings,
399                ..ThreadExecuteTimings::default()
400            },
401        );
402        new
403    }
404}
405
406fn execute_batches_internal(
407    bank: &Arc<Bank>,
408    replay_tx_thread_pool: &ThreadPool,
409    batches: &[TransactionBatchWithIndexes<RuntimeTransaction<SanitizedTransaction>>],
410    transaction_status_sender: Option<&TransactionStatusSender>,
411    replay_vote_sender: Option<&ReplayVoteSender>,
412    log_messages_bytes_limit: Option<usize>,
413    prioritization_fee_cache: Option<&PrioritizationFeeCache>,
414) -> Result<ExecuteBatchesInternalMetrics> {
415    assert!(!batches.is_empty());
416    let execution_timings_per_thread: Mutex<HashMap<usize, ThreadExecuteTimings>> =
417        Mutex::new(HashMap::new());
418
419    let mut execute_batches_elapsed = Measure::start("execute_batches_elapsed");
420    let results: Vec<Result<()>> = replay_tx_thread_pool.install(|| {
421        batches
422            .into_par_iter()
423            .map(|transaction_batch| {
424                let transaction_count =
425                    transaction_batch.batch.sanitized_transactions().len() as u64;
426                let mut timings = ExecuteTimings::default();
427                let (result, execute_batches_us) = measure_us!(execute_batch(
428                    transaction_batch,
429                    bank,
430                    transaction_status_sender,
431                    replay_vote_sender,
432                    ReplayVoteSendType::Executed {
433                        replay_bank_id: bank.bank_id(),
434                        replay_slot: bank.slot(),
435                    },
436                    &mut timings,
437                    log_messages_bytes_limit,
438                    prioritization_fee_cache,
439                    None::<fn(&_) -> _>,
440                ));
441
442                let thread_index = replay_tx_thread_pool.current_thread_index().unwrap();
443                execution_timings_per_thread
444                    .lock()
445                    .unwrap()
446                    .entry(thread_index)
447                    .and_modify(|thread_execution_time| {
448                        let ThreadExecuteTimings {
449                            total_thread_us,
450                            total_transactions_executed,
451                            execute_timings: total_thread_execute_timings,
452                        } = thread_execution_time;
453                        *total_thread_us += execute_batches_us;
454                        *total_transactions_executed += transaction_count;
455                        total_thread_execute_timings
456                            .saturating_add_in_place(ExecuteTimingType::TotalBatchesLen, 1);
457                        total_thread_execute_timings.accumulate(&timings);
458                    })
459                    .or_insert(ThreadExecuteTimings {
460                        total_thread_us: Saturating(execute_batches_us),
461                        total_transactions_executed: Saturating(transaction_count),
462                        execute_timings: timings,
463                    });
464                result
465            })
466            .collect()
467    });
468    execute_batches_elapsed.stop();
469
470    first_err(&results)?;
471
472    Ok(ExecuteBatchesInternalMetrics {
473        execution_timings_per_thread: execution_timings_per_thread.into_inner().unwrap(),
474        total_batches_len: batches.len() as u64,
475        execute_batches_us: execute_batches_elapsed.as_us(),
476    })
477}
478
479// This fn diverts the code-path into two variants. Both must provide exactly the same set of
480// validations. For this reason, this fn is deliberately inserted into the code path to be called
481// inside process_entries(), so that Bank::prepare_sanitized_batch() has been called on all of
482// batches already, while minimizing code duplication (thus divergent behavior risk) at the cost of
483// acceptable overhead of meaningless buffering of batches for the scheduler variant.
484//
485// Also note that the scheduler variant can't implement the batch-level sanitization naively, due
486// to the nature of individual tx processing. That's another reason of this particular placement of
487// divergent point in the code-path (i.e. not one layer up with its own prepare_sanitized_batch()
488// invocation).
489fn process_batches(
490    bank: &BankWithScheduler,
491    replay_tx_thread_pool: &ThreadPool,
492    locked_entries: impl ExactSizeIterator<Item = LockedTransactionsWithIndexes<SanitizedTransaction>>,
493    transaction_status_sender: Option<&TransactionStatusSender>,
494    replay_vote_sender: Option<&ReplayVoteSender>,
495    batch_execution_timing: &mut BatchExecutionTiming,
496    log_messages_bytes_limit: Option<usize>,
497    prioritization_fee_cache: Option<&PrioritizationFeeCache>,
498) -> Result<()> {
499    if bank.has_installed_scheduler() {
500        debug!(
501            "process_batches()/schedule_batches_for_execution({} batches)",
502            locked_entries.len()
503        );
504        // Scheduling usually succeeds (immediately returns `Ok(())`) here without being blocked on
505        // the actual transaction executions.
506        //
507        // As an exception, this code path could propagate the transaction execution _errors of
508        // previously-scheduled transactions_ to notify the replay stage. Then, the replay stage
509        // will bail out the further processing of the malformed (possibly malicious) block
510        // immediately, not to waste any system resources. Note that this propagation is of early
511        // hints. Even if errors won't be propagated in this way, they are guaranteed to be
512        // propagated eventually via the blocking fn called
513        // BankWithScheduler::wait_for_completed_scheduler().
514        //
515        // To recite, the returned error is completely unrelated to the argument's `locked_entries`
516        // at the hand. While being awkward, the _async_ unified scheduler is abusing this existing
517        // error propagation code path to the replay stage for compatibility and ease of
518        // integration, exploiting the fact that the replay stage doesn't care _which transaction
519        // the returned error is originating from_.
520        //
521        // In the future, more proper error propagation mechanism will be introduced once after we
522        // fully transition to the unified scheduler for the block verification. That one would be
523        // a push based one from the unified scheduler to the replay stage to eliminate the current
524        // overhead: 1 read lock per batch in
525        // `BankWithScheduler::schedule_transaction_executions()`.
526        schedule_batches_for_execution(bank, locked_entries)
527    } else {
528        debug!(
529            "process_batches()/execute_batches({} batches)",
530            locked_entries.len()
531        );
532        execute_batches(
533            bank,
534            replay_tx_thread_pool,
535            locked_entries,
536            transaction_status_sender,
537            replay_vote_sender,
538            batch_execution_timing,
539            log_messages_bytes_limit,
540            prioritization_fee_cache,
541        )
542    }
543}
544
545fn schedule_batches_for_execution(
546    bank: &BankWithScheduler,
547    locked_entries: impl Iterator<Item = LockedTransactionsWithIndexes<SanitizedTransaction>>,
548) -> Result<()> {
549    // Track the first error encountered in the loop below, if any.
550    // This error will be propagated to the replay stage, or Ok(()).
551    let mut first_err = Ok(());
552
553    for LockedTransactionsWithIndexes {
554        lock_results,
555        transactions,
556        starting_index,
557    } in locked_entries
558    {
559        // unlock before sending to scheduler.
560        bank.unlock_accounts(transactions.iter().zip(lock_results.iter()));
561        // give ownership to scheduler. capture the first error, but continue the loop
562        // to unlock.
563        // scheduling is skipped if we have already detected an error in this loop
564        let indexes = starting_index..starting_index + transactions.len();
565        // Widening usize index to OrderedTaskId (= u128) won't ever fail.
566        let task_ids = indexes.map(|i| i.try_into().unwrap());
567        first_err = first_err.and_then(|()| {
568            bank.schedule_transaction_executions(transactions.into_iter().zip_eq(task_ids))
569        });
570    }
571    first_err
572}
573
574fn execute_batches(
575    bank: &Arc<Bank>,
576    replay_tx_thread_pool: &ThreadPool,
577    locked_entries: impl ExactSizeIterator<Item = LockedTransactionsWithIndexes<SanitizedTransaction>>,
578    transaction_status_sender: Option<&TransactionStatusSender>,
579    replay_vote_sender: Option<&ReplayVoteSender>,
580    timing: &mut BatchExecutionTiming,
581    log_messages_bytes_limit: Option<usize>,
582    prioritization_fee_cache: Option<&PrioritizationFeeCache>,
583) -> Result<()> {
584    if locked_entries.len() == 0 {
585        return Ok(());
586    }
587
588    let tx_batches: Vec<_> = locked_entries
589        .into_iter()
590        .map(
591            |LockedTransactionsWithIndexes {
592                 lock_results,
593                 transactions,
594                 starting_index,
595             }| {
596                let ending_index = starting_index + transactions.len();
597                TransactionBatchWithIndexes {
598                    batch: TransactionBatch::new(
599                        lock_results,
600                        bank,
601                        OwnedOrBorrowed::Owned(transactions),
602                    ),
603                    transaction_indexes: (starting_index..ending_index).collect(),
604                }
605            },
606        )
607        .collect();
608
609    let execute_batches_internal_metrics = execute_batches_internal(
610        bank,
611        replay_tx_thread_pool,
612        &tx_batches,
613        transaction_status_sender,
614        replay_vote_sender,
615        log_messages_bytes_limit,
616        prioritization_fee_cache,
617    )?;
618
619    // Pass false because this code-path is never touched by unified scheduler.
620    timing.accumulate(execute_batches_internal_metrics, false);
621    Ok(())
622}
623
624/// Process an ordered list of entries in parallel
625/// 1. In order lock accounts for each entry while the lock succeeds, up to a Tick entry
626/// 2. Process the locked group in parallel
627/// 3. Register the `Tick` if it's available
628/// 4. Update the leader scheduler, goto 1
629///
630/// This method is for use testing against a single Bank, and assumes `Bank::transaction_count()`
631/// represents the number of transactions executed in this Bank
632pub fn process_entries_for_tests(
633    bank: &BankWithScheduler,
634    entries: Vec<Entry>,
635    transaction_status_sender: Option<&TransactionStatusSender>,
636    replay_vote_sender: Option<&ReplayVoteSender>,
637) -> Result<()> {
638    let replay_tx_thread_pool = create_thread_pool(1);
639    let validate_and_hash_transaction = {
640        let bank = bank.clone_with_scheduler();
641        move |versioned_tx: VersionedTransaction,
642              serialized_message: &[u8]|
643              -> Result<RuntimeTransaction<SanitizedTransaction>> {
644            bank.verify_transaction_with_serialized_message(
645                versioned_tx,
646                serialized_message,
647                TransactionVerificationMode::HashOnly,
648            )
649        }
650    };
651
652    let num_txs = entries.iter().map(|entry| entry.transactions.len()).sum();
653    let entry::ValidatedHashedTransactions {
654        entries,
655        unverified_signatures,
656    } = entry::validate_and_hash_transactions(
657        entries,
658        num_txs,
659        &replay_tx_thread_pool,
660        validate_and_hash_transaction,
661    )?;
662    unverified_signatures.verify()?;
663
664    let mut entry_starting_index: usize = bank.transaction_count().try_into().unwrap();
665    let mut batch_timing = BatchExecutionTiming::default();
666    let replay_entries: Vec<_> = entries
667        .into_iter()
668        .map(|entry| {
669            let starting_index = entry_starting_index;
670            if let EntryType::Transactions(ref transactions) = entry {
671                entry_starting_index = entry_starting_index.saturating_add(transactions.len());
672            }
673            ReplayEntry {
674                entry,
675                starting_index,
676            }
677        })
678        .collect();
679
680    let result = process_entries(
681        bank,
682        &replay_tx_thread_pool,
683        replay_entries,
684        transaction_status_sender,
685        replay_vote_sender,
686        &mut batch_timing,
687        None,
688        None,
689    );
690
691    debug!("process_entries: {batch_timing:?}");
692    result
693}
694
695fn process_entries(
696    bank: &BankWithScheduler,
697    replay_tx_thread_pool: &ThreadPool,
698    entries: Vec<ReplayEntry>,
699    transaction_status_sender: Option<&TransactionStatusSender>,
700    replay_vote_sender: Option<&ReplayVoteSender>,
701    batch_timing: &mut BatchExecutionTiming,
702    log_messages_bytes_limit: Option<usize>,
703    prioritization_fee_cache: Option<&PrioritizationFeeCache>,
704) -> Result<()> {
705    // accumulator for entries that can be processed in parallel
706    let mut batches = vec![];
707    let mut tick_hashes = vec![];
708
709    for ReplayEntry {
710        entry,
711        starting_index,
712    } in entries
713    {
714        match entry {
715            EntryType::Tick(hash) => {
716                // If it's a tick, save it for later
717                tick_hashes.push(hash);
718                if bank.is_block_boundary(bank.tick_height() + tick_hashes.len() as u64) {
719                    break;
720                }
721            }
722            EntryType::Transactions(transactions) => {
723                queue_batches_with_lock_retry(
724                    bank,
725                    starting_index,
726                    transactions,
727                    &mut batches,
728                    |batches| {
729                        process_batches(
730                            bank,
731                            replay_tx_thread_pool,
732                            batches,
733                            transaction_status_sender,
734                            replay_vote_sender,
735                            batch_timing,
736                            log_messages_bytes_limit,
737                            prioritization_fee_cache,
738                        )
739                    },
740                )?;
741            }
742        }
743    }
744    process_batches(
745        bank,
746        replay_tx_thread_pool,
747        batches.into_iter(),
748        transaction_status_sender,
749        replay_vote_sender,
750        batch_timing,
751        log_messages_bytes_limit,
752        prioritization_fee_cache,
753    )?;
754    for hash in tick_hashes {
755        bank.register_tick(&hash);
756    }
757    Ok(())
758}
759
760/// If an entry can be locked without failure, the transactions are pushed
761/// as a batch to `batches`. If the lock fails, the transactions are unlocked
762/// and the batches are processed.
763/// The locking process is retried, and if it fails again the block is marked
764/// as dead.
765/// If the lock retry succeeds, then the batch is pushed into `batches`.
766fn queue_batches_with_lock_retry(
767    bank: &Bank,
768    starting_index: usize,
769    transactions: Vec<RuntimeTransaction<SanitizedTransaction>>,
770    batches: &mut Vec<LockedTransactionsWithIndexes<SanitizedTransaction>>,
771    mut process_batches: impl FnMut(
772        Drain<LockedTransactionsWithIndexes<SanitizedTransaction>>,
773    ) -> Result<()>,
774) -> Result<()> {
775    // try to lock the accounts
776    let lock_results = bank.try_lock_accounts(&transactions);
777    let first_lock_err = first_err(&lock_results);
778    if first_lock_err.is_ok() {
779        batches.push(LockedTransactionsWithIndexes {
780            lock_results,
781            transactions,
782            starting_index,
783        });
784        return Ok(());
785    }
786
787    // We need to unlock the transactions that succeeded to lock before the
788    // retry.
789    bank.unlock_accounts(transactions.iter().zip(lock_results.iter()));
790
791    // We failed to lock, there are 2 possible reasons:
792    // 1. A batch already in `batches` holds the lock.
793    // 2. The batch is "self-conflicting" (i.e. the batch has account lock conflicts with itself)
794
795    // Use the callback to process batches, and clear them.
796    // Clearing the batches will `Drop` the batches which will unlock the accounts.
797    process_batches(batches.drain(..))?;
798
799    // Retry the lock
800    let lock_results = bank.try_lock_accounts(&transactions);
801    match first_err(&lock_results) {
802        Ok(()) => {
803            batches.push(LockedTransactionsWithIndexes {
804                lock_results,
805                transactions,
806                starting_index,
807            });
808            Ok(())
809        }
810        Err(err) => {
811            // We still may have succeeded to lock some accounts, unlock them.
812            bank.unlock_accounts(transactions.iter().zip(lock_results.iter()));
813
814            // An entry has account lock conflicts with *itself*, which should not happen
815            // if generated by a properly functioning leader
816            datapoint_error!(
817                "validator_process_entry_error",
818                (
819                    "error",
820                    format!(
821                        "Lock accounts error, entry conflicts with itself, txs: {transactions:?}"
822                    ),
823                    String
824                )
825            );
826            Err(err)
827        }
828    }
829}
830
831#[derive(Error, Debug)]
832pub enum BlockstoreProcessorError {
833    #[error("failed to load entries, error: {0}")]
834    FailedToLoadEntries(#[from] BlockstoreError),
835
836    #[error("failed to load meta")]
837    FailedToLoadMeta,
838
839    #[error("failed to replay bank 0, did you forget to provide a snapshot")]
840    FailedToReplayBank0,
841
842    #[error("invalid block error: {0}")]
843    InvalidBlock(#[from] BlockError),
844
845    #[error("invalid transaction error: {0}")]
846    InvalidTransaction(#[from] TransactionError),
847
848    #[error("no valid forks found")]
849    NoValidForksFound,
850
851    #[error("invalid hard fork slot {0}")]
852    InvalidHardFork(Slot),
853
854    #[error("root bank with mismatched capitalization at {0}")]
855    RootBankWithMismatchedCapitalization(Slot),
856
857    #[error("user transactions found in vote only mode bank at slot {0}")]
858    UserTransactionsInVoteOnlyBank(Slot),
859
860    #[error("invalid parent -> child chained merkle root at slot {0} parent {1}")]
861    ChainedBlockIdFailure(Slot, Slot),
862
863    #[error("block component processor error: {0}")]
864    BlockComponentProcessor(#[from] BlockComponentProcessorError),
865
866    #[error("bank hash mismatch at slot {0}: expected {1}, got {2}")]
867    BankHashMismatch(Slot, Hash, Hash),
868}
869
870/// Callback for accessing bank state after each slot is confirmed while
871/// processing the blockstore
872pub type ProcessSlotCallback = Arc<dyn Fn(&Bank) + Sync + Send>;
873
874#[derive(Default, Clone)]
875pub struct ProcessOptions {
876    /// Run PoH, transaction signature and other transaction verification on the entries.
877    pub run_verification: bool,
878    /// For startup replay / ledger-tool skip checks that verify a block chains to its parent correctly
879    /// For Tower blocks this is validating the chained merkle root, for Alpenglow it is the double merkle root
880    pub skip_inter_slot_verification: bool,
881    pub halt_at_slot: Option<Slot>,
882    pub slot_callback: Option<ProcessSlotCallback>,
883    pub new_hard_forks: Option<Vec<Slot>>,
884    pub debug_keys: Option<Arc<HashSet<Pubkey>>>,
885    pub limit_load_slot_count_from_snapshot: Option<usize>,
886    pub allow_dead_slots: bool,
887    pub accounts_db_skip_shrink: bool,
888    pub accounts_db_force_initial_clean: bool,
889    pub accounts_db_config: AccountsDbConfig,
890    pub verify_index: bool,
891    pub runtime_config: RuntimeConfig,
892    /// true if after processing the contents of the blockstore at startup, we should run an accounts hash calc
893    /// This is useful for debugging.
894    pub run_final_accounts_hash_calc: bool,
895    pub use_snapshot_archives_at_startup: UseSnapshotArchivesAtStartup,
896    #[cfg(feature = "dev-context-only-utils")]
897    pub hash_overrides: Option<HashOverrides>,
898    pub abort_on_invalid_block: bool,
899    pub no_block_cost_limits: bool,
900}
901
902pub(crate) fn process_blockstore_for_bank_0(
903    genesis_config: &GenesisConfig,
904    blockstore: &Blockstore,
905    account_paths: Vec<PathBuf>,
906    opts: &ProcessOptions,
907    transaction_status_sender: Option<&TransactionStatusSender>,
908    entry_notification_sender: Option<&EntryNotifierSender>,
909    accounts_update_notifier: Option<AccountsUpdateNotifier>,
910    exit: Arc<AtomicBool>,
911) -> result::Result<Arc<RwLock<BankForks>>, BlockstoreProcessorError> {
912    // Setup bank for slot 0
913    let bank0 = Bank::new_from_genesis(
914        genesis_config,
915        Arc::new(opts.runtime_config.clone()),
916        account_paths,
917        opts.debug_keys.clone(),
918        opts.accounts_db_config.clone(),
919        accounts_update_notifier,
920        None,
921        exit,
922        None,
923        None,
924    );
925    let bank0_slot = bank0.slot();
926    let hard_forks = bank0.hard_forks();
927    let bank_forks = BankForks::new_rw_arc(bank0);
928
929    info!("Processing ledger for slot 0...");
930    let replay_tx_thread_pool = create_thread_pool(num_cpus::get());
931    process_bank_0(
932        &bank_forks
933            .read()
934            .unwrap()
935            .get_with_scheduler(bank0_slot)
936            .unwrap(),
937        compute_shred_version(&genesis_config.hash(), Some(&hard_forks)),
938        blockstore,
939        &replay_tx_thread_pool,
940        opts,
941        transaction_status_sender,
942        entry_notification_sender,
943        &bank_forks.read().unwrap().migration_status(),
944    )?;
945
946    Ok(bank_forks)
947}
948
949/// Process blockstore from a known root bank
950#[allow(clippy::too_many_arguments)]
951pub fn process_blockstore_from_root(
952    blockstore: &Blockstore,
953    bank_forks: &RwLock<BankForks>,
954    shred_version: u16,
955    leader_schedule_cache: &LeaderScheduleCache,
956    opts: &ProcessOptions,
957    transaction_status_sender: Option<&TransactionStatusSender>,
958    entry_notification_sender: Option<&EntryNotifierSender>,
959    snapshot_controller: Option<&SnapshotController>,
960) -> result::Result<(), BlockstoreProcessorError> {
961    let (start_slot, start_slot_hash) = {
962        // Starting slot must be a root, and thus has no parents
963        assert_eq!(bank_forks.read().unwrap().banks().len(), 1);
964        let bank = bank_forks.read().unwrap().root_bank();
965        #[cfg(feature = "dev-context-only-utils")]
966        if let Some(hash_overrides) = &opts.hash_overrides {
967            info!("Will override following slots' hashes: {hash_overrides:#?}");
968            bank.set_hash_overrides(hash_overrides.clone());
969        }
970        if opts.no_block_cost_limits {
971            warn!("setting block cost limits to MAX");
972            bank.write_cost_tracker().unwrap().set_limits_max();
973        }
974        assert!(bank.parent().is_none());
975        (bank.slot(), bank.hash())
976    };
977
978    info!("Processing ledger from slot {start_slot}...");
979    let now = Instant::now();
980
981    // Ensure start_slot is rooted for correct replay; also ensure start_slot and
982    // qualifying children are marked as connected
983    if blockstore.is_primary_access() {
984        blockstore
985            .mark_slots_as_if_rooted_normally_at_startup(
986                vec![(start_slot, Some(start_slot_hash))],
987                true,
988            )
989            .expect("Couldn't mark start_slot as root in startup");
990        blockstore
991            .set_and_chain_connected_on_root_and_next_slots(start_slot)
992            .expect("Couldn't mark start_slot as connected during startup")
993    } else {
994        info!(
995            "Start slot {start_slot} isn't a root, and won't be updated due to read-only \
996             blockstore access"
997        );
998    }
999
1000    if let Ok(Some(highest_slot)) = blockstore.highest_slot() {
1001        info!("ledger holds data through slot {highest_slot}");
1002    }
1003
1004    let mut timing = ExecuteTimings::default();
1005    let (num_slots_processed, num_new_roots_found) = if let Some(start_slot_meta) = blockstore
1006        .meta(start_slot)
1007        .unwrap_or_else(|_| panic!("Failed to get meta for slot {start_slot}"))
1008    {
1009        let replay_tx_thread_pool = create_thread_pool(num_cpus::get());
1010        load_frozen_forks(
1011            bank_forks,
1012            shred_version,
1013            &start_slot_meta,
1014            blockstore,
1015            &replay_tx_thread_pool,
1016            leader_schedule_cache,
1017            opts,
1018            transaction_status_sender,
1019            entry_notification_sender,
1020            &mut timing,
1021            snapshot_controller,
1022        )?
1023    } else {
1024        // If there's no meta in the blockstore for the input `start_slot`,
1025        // then we started from a snapshot and are unable to process anything.
1026        //
1027        // If the ledger has any data at all, the snapshot was likely taken at
1028        // a slot that is not within the range of ledger min/max slot(s).
1029        warn!("Starting slot {start_slot} is not in Blockstore, unable to process");
1030        (0, 0)
1031    };
1032
1033    let processing_time = now.elapsed();
1034    let num_frozen_banks = bank_forks.read().unwrap().frozen_banks().count();
1035    datapoint_info!(
1036        "process_blockstore_from_root",
1037        ("total_time_us", processing_time.as_micros(), i64),
1038        ("frozen_banks", num_frozen_banks, i64),
1039        ("slot", bank_forks.read().unwrap().root(), i64),
1040        ("num_slots_processed", num_slots_processed, i64),
1041        ("num_new_roots_found", num_new_roots_found, i64),
1042        ("forks", bank_forks.read().unwrap().banks().len(), i64),
1043    );
1044
1045    info!("ledger processing timing: {timing:?}");
1046    {
1047        let bank_forks = bank_forks.read().unwrap();
1048        let mut bank_slots = bank_forks.banks().keys().copied().collect::<Vec<_>>();
1049        bank_slots.sort_unstable();
1050
1051        info!(
1052            "ledger processed in {}. root slot is {}, {} bank{}: {}",
1053            HumanTime::from(chrono::Duration::from_std(processing_time).unwrap())
1054                .to_text_en(Accuracy::Precise, Tense::Present),
1055            bank_forks.root(),
1056            bank_slots.len(),
1057            if bank_slots.len() > 1 { "s" } else { "" },
1058            bank_slots.iter().map(|slot| slot.to_string()).join(", "),
1059        );
1060        assert!(bank_forks.active_bank_slots().is_empty());
1061    }
1062
1063    Ok(())
1064}
1065
1066/// Verify that a segment of entries has the correct number of ticks and hashes
1067fn verify_ticks(
1068    bank: &Bank,
1069    entries: &[Entry],
1070    slot_full: bool,
1071    tick_hash_count: &mut u64,
1072    migration_status: &MigrationStatus,
1073) -> std::result::Result<(), BlockError> {
1074    let next_bank_tick_height = bank.tick_height() + entries.tick_count();
1075    let max_bank_tick_height = bank.max_tick_height();
1076
1077    if next_bank_tick_height > max_bank_tick_height {
1078        warn!("Too many entry ticks found in slot: {}", bank.slot());
1079        return Err(BlockError::TooManyTicks);
1080    }
1081
1082    if next_bank_tick_height < max_bank_tick_height && slot_full {
1083        info!("Too few entry ticks found in slot: {}", bank.slot());
1084        return Err(BlockError::TooFewTicks);
1085    }
1086
1087    if next_bank_tick_height == max_bank_tick_height {
1088        let has_trailing_entry = entries.last().map(|e| !e.is_tick()).unwrap_or_default();
1089        if has_trailing_entry {
1090            warn!("Slot: {} did not end with a tick entry", bank.slot());
1091            return Err(BlockError::TrailingEntry);
1092        }
1093
1094        if !slot_full {
1095            warn!("Slot: {} was not marked full", bank.slot());
1096            return Err(BlockError::InvalidLastTick);
1097        }
1098    }
1099
1100    if migration_status.should_have_alpenglow_ticks(bank.slot()) {
1101        // When alpenglow is active, PoH MUST be in low power mode.
1102        // We require that each block only has 1 tick at the very end
1103        if entries.iter().any(|entry| entry.num_hashes != 1) {
1104            warn!(
1105                "Alpenglow entry with invalid num_hashes found in slot: {}",
1106                bank.slot()
1107            );
1108            return Err(BlockError::InvalidTickHashCount);
1109        }
1110        return Ok(());
1111    }
1112
1113    let hashes_per_tick = bank.hashes_per_tick().unwrap_or(0);
1114    if !entries.verify_tick_hash_count(tick_hash_count, hashes_per_tick) {
1115        warn!(
1116            "Tick with invalid number of hashes found in slot: {}",
1117            bank.slot()
1118        );
1119        return Err(BlockError::InvalidTickHashCount);
1120    }
1121
1122    Ok(())
1123}
1124
1125#[allow(clippy::too_many_arguments)]
1126#[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
1127fn confirm_full_slot(
1128    blockstore: &Blockstore,
1129    bank: &BankWithScheduler,
1130    shred_version: u16,
1131    replay_tx_thread_pool: &ThreadPool,
1132    opts: &ProcessOptions,
1133    progress: &mut ConfirmationProgress,
1134    transaction_status_sender: Option<&TransactionStatusSender>,
1135    entry_notification_sender: Option<&EntryNotifierSender>,
1136    replay_vote_sender: Option<&ReplayVoteSender>,
1137    timing: &mut ExecuteTimings,
1138    migration_status: &MigrationStatus,
1139) -> result::Result<(), BlockstoreProcessorError> {
1140    let mut confirmation_timing = ConfirmationTiming::default();
1141    let skip_verification = !opts.run_verification;
1142    let slot = bank.slot();
1143    let bank_id = bank.bank_id();
1144    defer! {
1145        if let Some(replay_vote_sender) = replay_vote_sender {
1146            let _ = replay_vote_sender.send(ReplayVoteMessage::BankComplete {
1147                replay_bank_id: bank_id,
1148                replay_slot: slot,
1149            });
1150        }
1151    }
1152
1153    confirm_slot(
1154        blockstore,
1155        bank,
1156        shred_version,
1157        replay_tx_thread_pool,
1158        &mut confirmation_timing,
1159        progress,
1160        skip_verification,
1161        transaction_status_sender,
1162        entry_notification_sender,
1163        replay_vote_sender,
1164        None,
1165        opts.allow_dead_slots,
1166        opts.runtime_config.log_messages_bytes_limit,
1167        None,
1168        migration_status,
1169    )?;
1170
1171    timing.accumulate(&confirmation_timing.batch_execute.totals);
1172
1173    if !bank.is_complete() {
1174        return Err(BlockstoreProcessorError::InvalidBlock(
1175            BlockError::Incomplete,
1176        ));
1177    }
1178
1179    if let Some((result, execute_time)) = bank.wait_for_completed_scheduler() {
1180        timing.accumulate(&execute_time);
1181        result?;
1182    }
1183
1184    progress.wait_for_all_verification_results(&mut 0, &mut 0)
1185}
1186
1187/// Measures different parts of the slot confirmation processing pipeline.
1188#[derive(Debug)]
1189pub struct ConfirmationTiming {
1190    /// Moment when the `ConfirmationTiming` instance was created.  Used to track the total wall
1191    /// clock time from the moment the first shard for the slot is received and to the moment the
1192    /// slot is complete.
1193    pub started: Instant,
1194
1195    /// Wall clock time used by the slot confirmation code, including PoH/signature verification,
1196    /// and replay.  As replay can run in parallel with the verification, this value can not be
1197    /// recovered from the `replay_elapsed` and or `{poh,transaction}_verify_elapsed`.  This
1198    /// includes failed cases, when `confirm_slot_entries` exist with an error.  In microseconds.
1199    /// When unified scheduler is enabled, replay excludes the transaction execution, only
1200    /// accounting for task creation and submission to the scheduler.
1201    pub confirmation_elapsed: u64,
1202
1203    /// Wall clock time used by the entry replay code.  Does not include the PoH or the transaction
1204    /// signature/precompiles verification, but can overlap with the PoH and signature verification.
1205    /// In microseconds.
1206    /// When unified scheduler is enabled, replay excludes the transaction execution, only
1207    /// accounting for task creation and submission to the scheduler.
1208    pub replay_elapsed: u64,
1209
1210    /// Wall clock times, used for the PoH verification of entries.  In microseconds.
1211    pub poh_verify_elapsed: u64,
1212
1213    /// Wall clock time, used for the signature verification as well as precompiles verification.
1214    /// In microseconds.
1215    pub transaction_verify_elapsed: u64,
1216
1217    /// Wall clock time spent loading data sets (and entries) from the blockstore.  This does not
1218    /// include the case when the blockstore load failed.  In microseconds.
1219    pub fetch_elapsed: u64,
1220
1221    /// Same as `fetch_elapsed` above, but for the case when the blockstore load fails.  In
1222    /// microseconds.
1223    pub fetch_fail_elapsed: u64,
1224
1225    /// `batch_execute()` measurements.
1226    pub batch_execute: BatchExecutionTiming,
1227
1228    /// Number of times this slot was switched from an alternate location.
1229    pub num_bank_switches: u64,
1230}
1231
1232impl Default for ConfirmationTiming {
1233    fn default() -> Self {
1234        Self {
1235            started: Instant::now(),
1236            confirmation_elapsed: 0,
1237            replay_elapsed: 0,
1238            poh_verify_elapsed: 0,
1239            transaction_verify_elapsed: 0,
1240            fetch_elapsed: 0,
1241            fetch_fail_elapsed: 0,
1242            batch_execute: BatchExecutionTiming::default(),
1243            num_bank_switches: 0,
1244        }
1245    }
1246}
1247
1248/// Measures times related to transaction execution in a slot.
1249#[derive(Debug, Default)]
1250pub struct BatchExecutionTiming {
1251    /// Time used by transaction execution.  Accumulated across multiple threads that are running
1252    /// `execute_batch()`.
1253    pub totals: ExecuteTimings,
1254
1255    /// Wall clock time used by the transaction execution part of pipeline.
1256    /// [`ConfirmationTiming::replay_elapsed`] includes this time.  In microseconds.
1257    wall_clock_us: Saturating<u64>,
1258
1259    /// Time used to execute transactions, via `execute_batch()`, in the thread that consumed the
1260    /// most time (in terms of total_thread_us) among rayon threads. Note that the slowest thread
1261    /// is determined each time a given group of batches is newly processed. So, this is a coarse
1262    /// approximation of wall-time single-threaded linearized metrics, discarding all metrics other
1263    /// than the arbitrary set of batches mixed with various transactions, which replayed slowest
1264    /// as a whole for each rayon processing session.
1265    ///
1266    /// When unified scheduler is enabled, this field isn't maintained, because it's not batched at
1267    /// all.
1268    slowest_thread: ThreadExecuteTimings,
1269}
1270
1271impl BatchExecutionTiming {
1272    pub fn accumulate(
1273        &mut self,
1274        new_batch: ExecuteBatchesInternalMetrics,
1275        is_unified_scheduler_enabled: bool,
1276    ) {
1277        let Self {
1278            totals,
1279            wall_clock_us,
1280            slowest_thread,
1281        } = self;
1282
1283        // These metric fields aren't applicable for the unified scheduler
1284        if !is_unified_scheduler_enabled {
1285            *wall_clock_us += new_batch.execute_batches_us;
1286
1287            totals.saturating_add_in_place(TotalBatchesLen, new_batch.total_batches_len);
1288            totals.saturating_add_in_place(NumExecuteBatches, 1);
1289        }
1290
1291        for thread_times in new_batch.execution_timings_per_thread.values() {
1292            totals.accumulate(&thread_times.execute_timings);
1293        }
1294
1295        // This whole metric (replay-slot-end-to-end-stats) isn't applicable for the unified
1296        // scheduler.
1297        if !is_unified_scheduler_enabled {
1298            let slowest = new_batch
1299                .execution_timings_per_thread
1300                .values()
1301                .max_by_key(|thread_times| thread_times.total_thread_us);
1302
1303            if let Some(slowest) = slowest {
1304                slowest_thread.accumulate(slowest);
1305                slowest_thread
1306                    .execute_timings
1307                    .saturating_add_in_place(NumExecuteBatches, 1);
1308            };
1309        }
1310    }
1311}
1312
1313#[derive(Debug, Default)]
1314pub struct ThreadExecuteTimings {
1315    pub total_thread_us: Saturating<u64>,
1316    pub total_transactions_executed: Saturating<u64>,
1317    pub execute_timings: ExecuteTimings,
1318}
1319
1320impl ThreadExecuteTimings {
1321    pub fn report_stats(&self, slot: Slot) {
1322        lazy! {
1323            datapoint_info!(
1324                "replay-slot-end-to-end-stats",
1325                ("slot", slot as i64, i64),
1326                ("total_thread_us", self.total_thread_us.0 as i64, i64),
1327                ("total_transactions_executed", self.total_transactions_executed.0 as i64, i64),
1328                // Everything inside the `eager!` block will be eagerly expanded before
1329                // evaluation of the rest of the surrounding macro.
1330                // Pass false because this code-path is never touched by unified scheduler.
1331                eager!{report_execute_timings!(self.execute_timings, false)}
1332            );
1333        };
1334    }
1335
1336    pub fn accumulate(&mut self, other: &ThreadExecuteTimings) {
1337        self.execute_timings.accumulate(&other.execute_timings);
1338        self.total_thread_us += other.total_thread_us;
1339        self.total_transactions_executed += other.total_transactions_executed;
1340    }
1341}
1342
1343#[derive(Default)]
1344pub struct ReplaySlotStats(ConfirmationTiming);
1345impl std::ops::Deref for ReplaySlotStats {
1346    type Target = ConfirmationTiming;
1347    fn deref(&self) -> &Self::Target {
1348        &self.0
1349    }
1350}
1351impl std::ops::DerefMut for ReplaySlotStats {
1352    fn deref_mut(&mut self) -> &mut Self::Target {
1353        &mut self.0
1354    }
1355}
1356
1357impl ReplaySlotStats {
1358    pub fn report_stats(
1359        &self,
1360        slot: Slot,
1361        num_txs: usize,
1362        num_entries: usize,
1363        num_shreds: u64,
1364        bank_complete_time_us: u64,
1365        is_unified_scheduler_enabled: bool,
1366    ) {
1367        let confirmation_elapsed = if is_unified_scheduler_enabled {
1368            "confirmation_without_replay_us"
1369        } else {
1370            "confirmation_time_us"
1371        };
1372        let replay_elapsed = if is_unified_scheduler_enabled {
1373            "task_submission_us"
1374        } else {
1375            "replay_time"
1376        };
1377        let execute_batches_us = if is_unified_scheduler_enabled {
1378            None
1379        } else {
1380            Some(self.batch_execute.wall_clock_us.0 as i64)
1381        };
1382
1383        lazy! {
1384            datapoint_info!(
1385                "replay-slot-stats",
1386                ("slot", slot as i64, i64),
1387                ("fetch_entries_time", self.fetch_elapsed as i64, i64),
1388                (
1389                    "fetch_entries_fail_time",
1390                    self.fetch_fail_elapsed as i64,
1391                    i64
1392                ),
1393                (
1394                    "entry_poh_verification_time",
1395                    self.poh_verify_elapsed as i64,
1396                    i64
1397                ),
1398                (
1399                    "entry_transaction_verification_time",
1400                    self.transaction_verify_elapsed as i64,
1401                    i64
1402                ),
1403                (confirmation_elapsed, self.confirmation_elapsed as i64, i64),
1404                (replay_elapsed, self.replay_elapsed as i64, i64),
1405                ("execute_batches_us", execute_batches_us, Option<i64>),
1406                ("num_bank_switches", self.num_bank_switches as i64, i64),
1407                (
1408                    "replay_total_elapsed",
1409                    self.started.elapsed().as_micros() as i64,
1410                    i64
1411                ),
1412                ("bank_complete_time_us", bank_complete_time_us, i64),
1413                ("total_transactions", num_txs as i64, i64),
1414                ("total_entries", num_entries as i64, i64),
1415                ("total_shreds", num_shreds as i64, i64),
1416                // Everything inside the `eager!` block will be eagerly expanded before
1417                // evaluation of the rest of the surrounding macro.
1418                eager!{report_execute_timings!(self.batch_execute.totals, is_unified_scheduler_enabled)}
1419            );
1420        };
1421
1422        // Skip reporting replay-slot-end-to-end-stats entirely if unified scheduler is enabled,
1423        // because the whole metrics itself is only meaningful for rayon-based worker threads.
1424        //
1425        // See slowest_thread doc comment for details.
1426        if !is_unified_scheduler_enabled {
1427            self.batch_execute.slowest_thread.report_stats(slot);
1428        }
1429
1430        // per_program_timings datapoints are only reported at the trace level, and all preparations
1431        // required to generate them can only occur when trace level is enabled.
1432        if log::log_enabled!(log::Level::Trace) {
1433            let mut per_pubkey_timings: Vec<_> = self
1434                .batch_execute
1435                .totals
1436                .details
1437                .per_program_timings
1438                .iter()
1439                .collect();
1440            per_pubkey_timings.sort_by_key(|b| cmp::Reverse(b.1.accumulated_us));
1441            let (total_us, total_units, total_count, total_errored_units, total_errored_count) =
1442                per_pubkey_timings.iter().fold(
1443                    (0, 0, 0, 0, 0),
1444                    |(sum_us, sum_units, sum_count, sum_errored_units, sum_errored_count), a| {
1445                        (
1446                            sum_us + a.1.accumulated_us.0,
1447                            sum_units + a.1.accumulated_units.0,
1448                            sum_count + a.1.count.0,
1449                            sum_errored_units + a.1.total_errored_units.0,
1450                            sum_errored_count + a.1.errored_txs_compute_consumed.len(),
1451                        )
1452                    },
1453                );
1454
1455            for (pubkey, time) in per_pubkey_timings.iter().take(5) {
1456                datapoint_trace!(
1457                    "per_program_timings",
1458                    ("slot", slot as i64, i64),
1459                    ("pubkey", pubkey.to_string(), String),
1460                    ("execute_us", time.accumulated_us.0, i64),
1461                    ("accumulated_units", time.accumulated_units.0, i64),
1462                    ("errored_units", time.total_errored_units.0, i64),
1463                    ("count", time.count.0, i64),
1464                    (
1465                        "errored_count",
1466                        time.errored_txs_compute_consumed.len(),
1467                        i64
1468                    ),
1469                );
1470            }
1471            datapoint_info!(
1472                "per_program_timings",
1473                ("slot", slot as i64, i64),
1474                ("pubkey", "all", String),
1475                ("execute_us", total_us, i64),
1476                ("accumulated_units", total_units, i64),
1477                ("count", total_count, i64),
1478                ("errored_units", total_errored_units, i64),
1479                ("errored_count", total_errored_count, i64)
1480            );
1481        }
1482    }
1483}
1484
1485#[derive(Default)]
1486pub struct ConfirmationProgress {
1487    pub last_entry: Hash,
1488    pub tick_hash_count: u64,
1489    pub num_shreds: u64,
1490    pub num_entries: usize,
1491    pub num_txs: usize,
1492    async_verification: Option<AsyncVerificationProgress>,
1493}
1494
1495impl ConfirmationProgress {
1496    pub fn new(last_entry: Hash) -> Self {
1497        Self {
1498            last_entry,
1499            ..Self::default()
1500        }
1501    }
1502
1503    pub fn new_with_async_verification(
1504        last_entry: Hash,
1505        async_verification: Option<AsyncVerificationProgress>,
1506    ) -> Self {
1507        debug_assert!(
1508            async_verification
1509                .as_ref()
1510                .map(|av| av.pending_jobs == 0 && av.first_error.is_none())
1511                .unwrap_or(true)
1512        );
1513        Self {
1514            last_entry,
1515            async_verification,
1516            ..Self::default()
1517        }
1518    }
1519
1520    fn async_verification(&mut self) -> &mut AsyncVerificationProgress {
1521        self.async_verification
1522            .get_or_insert_with(AsyncVerificationProgress::new)
1523    }
1524
1525    fn collect_available_verification_results(
1526        &mut self,
1527        poh_verify_elapsed: &mut u64,
1528        transaction_verify_elapsed: &mut u64,
1529    ) -> result::Result<(), BlockstoreProcessorError> {
1530        self.async_verification
1531            .as_mut()
1532            .map_or(Ok(()), |async_verification| {
1533                async_verification
1534                    .collect_available_results(poh_verify_elapsed, transaction_verify_elapsed)
1535            })
1536    }
1537
1538    pub fn wait_for_all_verification_results(
1539        &mut self,
1540        poh_verify_elapsed: &mut u64,
1541        transaction_verify_elapsed: &mut u64,
1542    ) -> result::Result<(), BlockstoreProcessorError> {
1543        self.async_verification
1544            .as_mut()
1545            .map_or(Ok(()), |async_verification| {
1546                async_verification
1547                    .wait_for_all_results(poh_verify_elapsed, transaction_verify_elapsed)
1548            })
1549    }
1550
1551    pub fn take_async_verification(&mut self) -> Option<AsyncVerificationProgress> {
1552        debug_assert!(
1553            self.async_verification
1554                .as_ref()
1555                .map(|av| av.pending_jobs == 0 && av.first_error.is_none())
1556                .unwrap_or(true)
1557        );
1558        self.async_verification.take()
1559    }
1560}
1561
1562struct AsyncVerificationResult {
1563    poh_verify_elapsed: u64,
1564    transaction_verify_elapsed: u64,
1565    error: Option<BlockstoreProcessorError>,
1566}
1567
1568pub struct AsyncVerificationProgress {
1569    sender: Sender<AsyncVerificationResult>,
1570    receiver: Receiver<AsyncVerificationResult>,
1571    pending_jobs: usize,
1572    first_error: Option<BlockstoreProcessorError>,
1573}
1574
1575impl Default for AsyncVerificationProgress {
1576    fn default() -> Self {
1577        Self::new()
1578    }
1579}
1580
1581impl AsyncVerificationProgress {
1582    // The capacity of the channel is somewhat arbitrary. At the time of this writing this is 100x
1583    // the number of max entries per slot on mnb. The channel will effectively never fill up, but if
1584    // it does, that condition is handled gracefully in spawn().
1585    const RESULT_CHANNEL_CAPACITY: usize = 100000;
1586
1587    pub fn new() -> Self {
1588        let (sender, receiver) = crossbeam_channel::bounded(Self::RESULT_CHANNEL_CAPACITY);
1589        Self {
1590            sender,
1591            receiver,
1592            pending_jobs: 0,
1593            first_error: None,
1594        }
1595    }
1596
1597    // Spawns the given work on the given thread pool. The result, once
1598    // available, can be collected by calling `collect_available_results()` or
1599    // `wait_for_all_results()`.
1600    fn spawn(
1601        &mut self,
1602        replay_tx_thread_pool: &ThreadPool,
1603        poh_verify_elapsed: &mut u64,
1604        transaction_verify_elapsed: &mut u64,
1605        work: impl FnOnce() -> AsyncVerificationResult + Send + 'static,
1606    ) -> result::Result<(), BlockstoreProcessorError> {
1607        while self.sender.is_full() {
1608            // Note that we spin here if the channel is full. This is fine because it can only be
1609            // full if we are not keeping up, in which case pulling results as fast as possible is
1610            // the right thing to do.
1611            //
1612            // We also really don't want sender.send(result) below to sleep, because that would
1613            // block rayon threads slowing down progress even more.
1614            self.collect_available_results(poh_verify_elapsed, transaction_verify_elapsed)?;
1615        }
1616        self.pending_jobs = self.pending_jobs.saturating_add(1);
1617        let sender = self.sender.clone();
1618        replay_tx_thread_pool.spawn(move || {
1619            let _ = sender.send(work());
1620        });
1621        Ok(())
1622    }
1623
1624    // Collects all available results from the channel.
1625    fn collect_available_results(
1626        &mut self,
1627        poh_verify_elapsed: &mut u64,
1628        transaction_verify_elapsed: &mut u64,
1629    ) -> result::Result<(), BlockstoreProcessorError> {
1630        while let Ok(result) = self.receiver.try_recv() {
1631            self.apply_result(result, poh_verify_elapsed, transaction_verify_elapsed);
1632        }
1633        if let Some(error) = self.first_error.take() {
1634            return Err(error);
1635        }
1636        Ok(())
1637    }
1638
1639    // Waits for all pending jobs to complete and collects their results.
1640    //
1641    // This MUST be called at the end of a slot.
1642    fn wait_for_all_results(
1643        &mut self,
1644        poh_verify_elapsed: &mut u64,
1645        transaction_verify_elapsed: &mut u64,
1646    ) -> result::Result<(), BlockstoreProcessorError> {
1647        while self.pending_jobs > 0 {
1648            let result = self.receiver.recv().map_err(|_| {
1649                BlockstoreProcessorError::InvalidBlock(BlockError::InvalidEntryHash)
1650            })?;
1651            self.apply_result(result, poh_verify_elapsed, transaction_verify_elapsed);
1652        }
1653        if let Some(error) = self.first_error.take() {
1654            return Err(error);
1655        }
1656        Ok(())
1657    }
1658
1659    fn apply_result(
1660        &mut self,
1661        AsyncVerificationResult {
1662            poh_verify_elapsed: poh_us,
1663            transaction_verify_elapsed: tx_verify_us,
1664            error,
1665        }: AsyncVerificationResult,
1666        poh_verify_elapsed: &mut u64,
1667        transaction_verify_elapsed: &mut u64,
1668    ) {
1669        self.pending_jobs = self.pending_jobs.saturating_sub(1);
1670        *poh_verify_elapsed = poh_verify_elapsed.saturating_add(poh_us);
1671        *transaction_verify_elapsed = transaction_verify_elapsed.saturating_add(tx_verify_us);
1672        if self.first_error.is_none() {
1673            self.first_error = error;
1674        }
1675    }
1676}
1677
1678#[allow(clippy::too_many_arguments)]
1679pub fn confirm_slot(
1680    blockstore: &Blockstore,
1681    bank: &BankWithScheduler,
1682    shred_version: u16,
1683    replay_tx_thread_pool: &ThreadPool,
1684    timing: &mut ConfirmationTiming,
1685    progress: &mut ConfirmationProgress,
1686    skip_verification: bool,
1687    transaction_status_sender: Option<&TransactionStatusSender>,
1688    entry_notification_sender: Option<&EntryNotifierSender>,
1689    replay_vote_sender: Option<&ReplayVoteSender>,
1690    finalization_cert_sender: Option<&Sender<ConsensusMessage>>,
1691    allow_dead_slots: bool,
1692    log_messages_bytes_limit: Option<usize>,
1693    prioritization_fee_cache: Option<&PrioritizationFeeCache>,
1694    migration_status: &MigrationStatus,
1695) -> result::Result<(), BlockstoreProcessorError> {
1696    let slot = bank.slot();
1697
1698    let (slot_components, completed_ranges, slot_full) = {
1699        let mut load_elapsed = Measure::start("load_elapsed");
1700        let load_result = blockstore
1701            .get_slot_components_with_shred_info(slot, progress.num_shreds, allow_dead_slots)
1702            .map_err(BlockstoreProcessorError::FailedToLoadEntries);
1703        load_elapsed.stop();
1704        if load_result.is_err() {
1705            timing.fetch_fail_elapsed += load_elapsed.as_us();
1706        } else {
1707            timing.fetch_elapsed += load_elapsed.as_us();
1708        }
1709        load_result
1710    }?;
1711
1712    // Process block components for Alpenglow slots. Note that we don't need to run migration checks
1713    // for BlockMarkers here, despite BlockMarkers only being active post-Alpenglow. Here's why:
1714    //
1715    // Post-Alpenglow migration - validators that have Alpenglow enabled can parse BlockComponents.
1716    // Things just work.
1717    //
1718    // Pre-Alpenglow migration, suppose a validator receives a BlockMarker:
1719    //
1720    // (1) validators *incapable* of processing BlockMarkers will mark the slot as dead on shred
1721    //     ingest in blockstore.
1722    //
1723    // (2) validators *capable* of processing BlockMarkers will store the BlockMarkers in shred
1724    //     ingest, run through this verifying code here, and then error out when processing a
1725    //     BlockMarker, resulting in the slot being marked as dead.
1726    // Only replay that starts at the persisted UpdateParent FEC set may accept
1727    // UpdateParent as its first parent marker. From-shred-zero replay still
1728    // requires a block header before UpdateParent.
1729    let replay_starts_at_update_parent = bank.feature_set.snapshot().alpenglow_fast_leader_handover
1730        && migration_status.should_allow_block_markers(slot)
1731        && leader_slot_index(slot) == 0
1732        && blockstore
1733            .meta(slot)
1734            .expect("Blockstore operations must succeed")
1735            .is_some_and(|meta| {
1736                meta.has_update_parent()
1737                    && progress.num_shreds == u64::from(meta.replay_fec_set_index)
1738            });
1739    let mut processor = bank.block_component_processor.write().unwrap();
1740
1741    // Find the index of the last EntryBatch in slot_components
1742    let last_entry_batch_index = slot_components
1743        .iter()
1744        .rposition(|bc| matches!(bc, BlockComponent::EntryBatch(_)));
1745
1746    for (ix, (completed_range, component)) in
1747        completed_ranges.iter().zip(slot_components).enumerate()
1748    {
1749        let num_shreds = completed_range.end - completed_range.start;
1750        let is_final = slot_full && ix == completed_ranges.len() - 1;
1751
1752        match component {
1753            BlockComponent::EntryBatch(entries) => {
1754                let slot_full = slot_full && ix == last_entry_batch_index.unwrap();
1755
1756                // Skip block component validation for genesis block. Slot 0 is handled specially,
1757                // since it won't have the required block markers.
1758                if slot != 0 {
1759                    processor
1760                        .on_entry_batch(migration_status, slot)
1761                        .inspect_err(|err| {
1762                            warn!(
1763                                "BlockComponentProcessor::on_entry_batch() for slot {slot} failed \
1764                                 with {err}"
1765                            );
1766                        })?;
1767                }
1768
1769                confirm_slot_entries(
1770                    bank,
1771                    replay_tx_thread_pool,
1772                    (entries, num_shreds as u64, slot_full),
1773                    timing,
1774                    progress,
1775                    skip_verification,
1776                    transaction_status_sender,
1777                    entry_notification_sender,
1778                    replay_vote_sender,
1779                    log_messages_bytes_limit,
1780                    prioritization_fee_cache,
1781                    migration_status,
1782                )?;
1783            }
1784            BlockComponent::BlockMarker(marker) => {
1785                if marker.is_footer() {
1786                    // The footer path mutates vote accounts directly to pay rewards.
1787                    // All prior transactions must finish first so vote account view is deterministic.
1788                    if let Some((result, execute_time)) = bank.wait_for_completed_scheduler() {
1789                        timing.batch_execute.totals.accumulate(&execute_time);
1790                        result?;
1791                    }
1792                }
1793                if let Some(parent_bank) = bank.parent() {
1794                    let allow_initial_update_parent =
1795                        replay_starts_at_update_parent && marker.is_update_parent();
1796                    processor
1797                        .on_marker(
1798                            bank.clone_without_scheduler(),
1799                            parent_bank,
1800                            shred_version,
1801                            marker,
1802                            allow_initial_update_parent,
1803                            finalization_cert_sender,
1804                            migration_status,
1805                        )
1806                        .inspect_err(|err| {
1807                            warn!(
1808                                "BlockComponentProcessor::on_marker() for slot {slot} failed with \
1809                                 {err}"
1810                            );
1811                        })?;
1812                }
1813                progress.num_shreds += num_shreds as u64;
1814            }
1815        }
1816
1817        // Skip block component validation for genesis block. Slot 0 is handled specially,
1818        // since it won't have the required block markers.
1819        if is_final && slot != 0 {
1820            processor.on_final(migration_status, slot)?;
1821        }
1822    }
1823
1824    Ok(())
1825}
1826
1827#[allow(clippy::too_many_arguments)]
1828#[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
1829fn confirm_slot_entries(
1830    bank: &BankWithScheduler,
1831    replay_tx_thread_pool: &ThreadPool,
1832    slot_entries_load_result: (Vec<Entry>, u64, bool),
1833    timing: &mut ConfirmationTiming,
1834    progress: &mut ConfirmationProgress,
1835    skip_verification: bool,
1836    transaction_status_sender: Option<&TransactionStatusSender>,
1837    entry_notification_sender: Option<&EntryNotifierSender>,
1838    replay_vote_sender: Option<&ReplayVoteSender>,
1839    log_messages_bytes_limit: Option<usize>,
1840    prioritization_fee_cache: Option<&PrioritizationFeeCache>,
1841    migration_status: &MigrationStatus,
1842) -> result::Result<(), BlockstoreProcessorError> {
1843    let ConfirmationTiming {
1844        confirmation_elapsed,
1845        replay_elapsed,
1846        poh_verify_elapsed,
1847        transaction_verify_elapsed,
1848        batch_execute: batch_execute_timing,
1849        ..
1850    } = timing;
1851
1852    let confirmation_elapsed_timer = Measure::start("confirmation_elapsed");
1853    defer! {
1854        *confirmation_elapsed += confirmation_elapsed_timer.end_as_us();
1855    };
1856
1857    let slot = bank.slot();
1858    let (entries, num_shreds, slot_full) = slot_entries_load_result;
1859    let num_entries = entries.len();
1860    let mut entry_tx_starting_indexes = Vec::with_capacity(num_entries);
1861    let mut entry_tx_starting_index = progress.num_txs;
1862    let num_txs = entries
1863        .iter()
1864        .enumerate()
1865        .map(|(i, entry)| {
1866            if let Some(entry_notification_sender) = entry_notification_sender {
1867                let entry_index = progress.num_entries.saturating_add(i);
1868                if let Err(err) = entry_notification_sender.send(EntryNotification {
1869                    slot,
1870                    index: entry_index,
1871                    entry: entry.into(),
1872                    starting_transaction_index: entry_tx_starting_index,
1873                }) {
1874                    warn!(
1875                        "Slot {slot}, entry {entry_index} entry_notification_sender send failed: \
1876                         {err:?}"
1877                    );
1878                }
1879            }
1880            let num_txs = entry.transactions.len();
1881            let next_tx_starting_index = entry_tx_starting_index.saturating_add(num_txs);
1882            entry_tx_starting_indexes.push(entry_tx_starting_index);
1883            entry_tx_starting_index = next_tx_starting_index;
1884            num_txs
1885        })
1886        .sum::<usize>();
1887    trace!(
1888        "Fetched entries for slot {slot}, num_entries: {num_entries}, num_shreds: {num_shreds}, \
1889         num_txs: {num_txs}, slot_full: {slot_full}",
1890    );
1891
1892    if !skip_verification {
1893        let tick_hash_count = &mut progress.tick_hash_count;
1894        verify_ticks(bank, &entries, slot_full, tick_hash_count, migration_status).map_err(
1895            |err| {
1896                warn!(
1897                    "{:#?}, slot: {}, entry len: {}, tick_height: {}, last entry: {}, \
1898                     last_blockhash: {}, shred_index: {}, slot_full: {}",
1899                    err,
1900                    slot,
1901                    num_entries,
1902                    bank.tick_height(),
1903                    progress.last_entry,
1904                    bank.last_blockhash(),
1905                    num_shreds,
1906                    slot_full,
1907                );
1908                err
1909            },
1910        )?;
1911    }
1912
1913    let last_entry_hash = entries.last().map(|e| e.hash);
1914    if !skip_verification {
1915        let start_hash = progress.last_entry;
1916        let verify_entries = entry::entries_to_verification_data(&entries);
1917        progress.async_verification().spawn(
1918            replay_tx_thread_pool,
1919            poh_verify_elapsed,
1920            transaction_verify_elapsed,
1921            move || {
1922                datapoint_debug!(
1923                    "verify-batch-size",
1924                    ("size", verify_entries.len() as i64, i64)
1925                );
1926                let state = entry::verify_entries_cpu(&verify_entries, &start_hash);
1927                let error = if state.status() {
1928                    None
1929                } else {
1930                    warn!("Ledger proof of history failed at slot: {slot}");
1931                    Some(BlockstoreProcessorError::InvalidBlock(
1932                        BlockError::InvalidEntryHash,
1933                    ))
1934                };
1935                AsyncVerificationResult {
1936                    poh_verify_elapsed: state.poh_duration_us(),
1937                    transaction_verify_elapsed: 0,
1938                    error,
1939                }
1940            },
1941        )?;
1942    }
1943
1944    let validate_and_hash_transaction = {
1945        let bank = bank.clone_with_scheduler();
1946        move |versioned_tx: VersionedTransaction, serialized_message: &[u8]| {
1947            bank.verify_transaction_with_serialized_message(
1948                versioned_tx,
1949                serialized_message,
1950                TransactionVerificationMode::HashOnly,
1951            )
1952        }
1953    };
1954
1955    let entry::ValidatedHashedTransactions {
1956        entries,
1957        unverified_signatures,
1958    } = match entry::validate_and_hash_transactions(
1959        entries,
1960        num_txs,
1961        transaction_hash_verify_thread_pool(),
1962        validate_and_hash_transaction,
1963    ) {
1964        Ok(txs) => txs,
1965        Err(err) => {
1966            warn!(
1967                "Ledger transaction hash verification failed at slot: {}",
1968                bank.slot()
1969            );
1970            return Err(err.into());
1971        }
1972    };
1973    let bank_id = bank.bank_id();
1974    if skip_verification {
1975        if let Some(replay_vote_sender) = replay_vote_sender {
1976            let message_hashes = unverified_signatures.vote_transaction_message_hashes();
1977            if !message_hashes.is_empty() {
1978                let _ = replay_vote_sender.send(ReplayVoteMessage::Verified {
1979                    replay_bank_id: bank_id,
1980                    replay_slot: slot,
1981                    message_hashes,
1982                });
1983            }
1984        }
1985    } else {
1986        let replay_vote_sender = replay_vote_sender.cloned();
1987        progress.async_verification().spawn(
1988            replay_tx_thread_pool,
1989            poh_verify_elapsed,
1990            transaction_verify_elapsed,
1991            move || {
1992                let verification_start = Instant::now();
1993                let error = unverified_signatures
1994                    .verify()
1995                    .map_err(BlockstoreProcessorError::from)
1996                    .err();
1997                if let Some(err) = &error {
1998                    warn!("Ledger transaction signature verification failed at slot {slot}: {err}");
1999                    if let Some(replay_vote_sender) = &replay_vote_sender {
2000                        let _ = replay_vote_sender.send(ReplayVoteMessage::InvalidBank {
2001                            replay_bank_id: bank_id,
2002                            replay_slot: slot,
2003                        });
2004                    }
2005                } else if let Some(replay_vote_sender) = &replay_vote_sender {
2006                    let message_hashes = unverified_signatures.vote_transaction_message_hashes();
2007                    if !message_hashes.is_empty() {
2008                        let _ = replay_vote_sender.send(ReplayVoteMessage::Verified {
2009                            replay_bank_id: bank_id,
2010                            replay_slot: slot,
2011                            message_hashes,
2012                        });
2013                    }
2014                }
2015                AsyncVerificationResult {
2016                    poh_verify_elapsed: 0,
2017                    transaction_verify_elapsed: verification_start.elapsed().as_micros() as u64,
2018                    error,
2019                }
2020            },
2021        )?;
2022    }
2023
2024    let mut replay_timer = Measure::start("replay_elapsed");
2025    let is_vote_only_bank = bank.vote_only_bank();
2026    let replay_entries: Vec<_> = entries
2027        .into_iter()
2028        .zip(entry_tx_starting_indexes)
2029        .map(|(entry, tx_starting_index)| {
2030            if !is_vote_only_bank {
2031                return Ok(ReplayEntry {
2032                    entry,
2033                    starting_index: tx_starting_index,
2034                });
2035            }
2036
2037            // If bank is in vote-only mode, validate that entries contain only vote transactions
2038            if let EntryType::Transactions(ref transactions) = entry
2039                && transactions
2040                    .iter()
2041                    .any(|tx| !is_valid_vote_only_transaction(tx))
2042            {
2043                return Err(BlockstoreProcessorError::UserTransactionsInVoteOnlyBank(
2044                    bank.slot(),
2045                ));
2046            }
2047            Ok(ReplayEntry {
2048                entry,
2049                starting_index: tx_starting_index,
2050            })
2051        })
2052        .collect::<result::Result<Vec<_>, _>>()?;
2053
2054    let process_result = process_entries(
2055        bank,
2056        replay_tx_thread_pool,
2057        replay_entries,
2058        transaction_status_sender,
2059        replay_vote_sender,
2060        batch_execute_timing,
2061        log_messages_bytes_limit,
2062        prioritization_fee_cache,
2063    )
2064    .map_err(BlockstoreProcessorError::from);
2065    replay_timer.stop();
2066    *replay_elapsed += replay_timer.as_us();
2067
2068    process_result?;
2069    progress
2070        .collect_available_verification_results(poh_verify_elapsed, transaction_verify_elapsed)?;
2071
2072    progress.num_shreds += num_shreds;
2073    progress.num_entries += num_entries;
2074    progress.num_txs += num_txs;
2075    if let Some(last_entry_hash) = last_entry_hash {
2076        progress.last_entry = last_entry_hash;
2077    }
2078
2079    Ok(())
2080}
2081
2082// Special handling required for processing the entries in slot 0
2083#[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
2084fn process_bank_0(
2085    bank0: &BankWithScheduler,
2086    shred_version: u16,
2087    blockstore: &Blockstore,
2088    replay_tx_thread_pool: &ThreadPool,
2089    opts: &ProcessOptions,
2090    transaction_status_sender: Option<&TransactionStatusSender>,
2091    entry_notification_sender: Option<&EntryNotifierSender>,
2092    migration_status: &MigrationStatus,
2093) -> result::Result<(), BlockstoreProcessorError> {
2094    assert_eq!(bank0.slot(), 0);
2095    let mut progress = ConfirmationProgress::new(bank0.last_blockhash());
2096    confirm_full_slot(
2097        blockstore,
2098        bank0,
2099        shred_version,
2100        replay_tx_thread_pool,
2101        opts,
2102        &mut progress,
2103        None,
2104        entry_notification_sender,
2105        None,
2106        &mut ExecuteTimings::default(),
2107        migration_status,
2108    )
2109    .map_err(|err| match err {
2110        err @ BlockstoreProcessorError::InvalidTransaction(_) => panic!("{err}"),
2111        _ => BlockstoreProcessorError::FailedToReplayBank0,
2112    })?;
2113    bank0.set_block_id(Some(
2114        blockstore
2115            .get_block_id(bank0.slot(), migration_status)?
2116            .expect("block id for a full slot must exist"),
2117    ));
2118    bank0.freeze();
2119    if blockstore.is_primary_access() {
2120        blockstore.insert_bank_hash(bank0.slot(), bank0.hash(), false);
2121    }
2122
2123    if let Some(transaction_status_sender) = transaction_status_sender {
2124        transaction_status_sender.send_transaction_status_freeze_message(bank0);
2125    }
2126
2127    Ok(())
2128}
2129
2130fn cleanup_outdated_tower_bft_startup_banks(
2131    root_bank: &Bank,
2132    blockstore: &Blockstore,
2133    slots_to_cleanup: &[(Slot, BankId)],
2134) {
2135    root_bank.remove_unrooted_slots(slots_to_cleanup);
2136
2137    for &(slot, _) in slots_to_cleanup {
2138        root_bank.clear_slot_signatures(slot);
2139        root_bank.prune_program_cache_by_deployment_slot(slot);
2140        reset_dead_if_primary_access(blockstore, slot);
2141    }
2142}
2143
2144/// Clean up failed startup slots and restart processing from the given genesis slot
2145///
2146/// `first_alpenglow_bank` and any current `pending_slots` banks are removed from runtime caches,
2147/// and their dead statuses are reset.
2148/// `pending_slots` is the current child blocks left to be processed. We clear and update
2149/// this with the children of `genesis_slot` instead.
2150fn cleanup_and_populate_pending_from_alpenglow_genesis(
2151    first_alpenglow_bank: &BankWithScheduler,
2152    genesis_slot: Slot,
2153    bank_forks: &RwLock<BankForks>,
2154    blockstore: &Blockstore,
2155    leader_schedule_cache: &LeaderScheduleCache,
2156    pending_slots: &mut Vec<(SlotMeta, Bank, Hash)>,
2157    opts: &ProcessOptions,
2158    migration_status: &MigrationStatus,
2159) -> result::Result<(), BlockstoreProcessorError> {
2160    // The frontier is now out of date, as all banks were created as TowerBFT banks.
2161    // Cleanup all the banks in the frontier and recreate them as Alpenglow banks.
2162    let slots_to_cleanup =
2163        std::iter::once((first_alpenglow_bank.slot(), first_alpenglow_bank.bank_id()))
2164            .chain(
2165                pending_slots
2166                    .iter()
2167                    .map(|(_, bank, _)| (bank.slot(), bank.bank_id())),
2168            )
2169            .collect::<Vec<_>>();
2170    let root_bank = bank_forks.read().unwrap().root_bank();
2171    cleanup_outdated_tower_bft_startup_banks(&root_bank, blockstore, &slots_to_cleanup);
2172
2173    let genesis_slot_meta = blockstore
2174        .meta(genesis_slot)
2175        .map_err(|err| {
2176            error!("Failed to load meta for slot {genesis_slot}: {err:?}");
2177            BlockstoreProcessorError::FailedToLoadMeta
2178        })?
2179        .unwrap();
2180
2181    warn!(
2182        "{}: load_frozen_forks() restart processing from {genesis_slot} treating further blocks \
2183         as Alpenglow banks",
2184        migration_status.my_pubkey()
2185    );
2186    // Clear current child bank frontier
2187    pending_slots.clear();
2188    // And queue up children of genesis instead
2189    process_next_slots(
2190        &bank_forks.read().unwrap().get(genesis_slot).unwrap(),
2191        &genesis_slot_meta,
2192        blockstore,
2193        leader_schedule_cache,
2194        pending_slots,
2195        opts,
2196        migration_status,
2197    )?;
2198
2199    Ok(())
2200}
2201
2202// Given a bank, add its children to the pending slots queue if those children slots are
2203// complete
2204fn process_next_slots(
2205    bank: &Arc<Bank>,
2206    meta: &SlotMeta,
2207    blockstore: &Blockstore,
2208    leader_schedule_cache: &LeaderScheduleCache,
2209    pending_slots: &mut Vec<(SlotMeta, Bank, Hash)>,
2210    opts: &ProcessOptions,
2211    migration_status: &MigrationStatus,
2212) -> result::Result<(), BlockstoreProcessorError> {
2213    if meta.next_slots.is_empty() {
2214        return Ok(());
2215    }
2216
2217    // This is a fork point if there are multiple children, create a new child bank for each fork
2218    for next_slot in &meta.next_slots {
2219        if opts
2220            .halt_at_slot
2221            .is_some_and(|halt_at_slot| *next_slot > halt_at_slot)
2222        {
2223            continue;
2224        }
2225        if !opts.allow_dead_slots && blockstore.is_dead(*next_slot) {
2226            continue;
2227        }
2228
2229        let next_meta = blockstore
2230            .meta(*next_slot)
2231            .map_err(|err| {
2232                warn!("Failed to load meta for slot {next_slot}: {err:?}");
2233                BlockstoreProcessorError::FailedToLoadMeta
2234            })?
2235            .unwrap();
2236
2237        // Only process full slots in blockstore_processor, replay_stage
2238        // handles any partials
2239        if next_meta.is_full() {
2240            if !opts.skip_inter_slot_verification {
2241                let parent_block_id = bank.block_id();
2242                if migration_status.should_allow_block_markers(*next_slot)
2243                    && bank.slot() != 0
2244                    && Some(next_meta.parent_block_id) != parent_block_id
2245                {
2246                    warn!(
2247                        "startup replay deferring slot {next_slot}: parent {} has block id {:?}, \
2248                         but SlotMeta expects {:?}",
2249                        bank.slot(),
2250                        parent_block_id,
2251                        next_meta.parent_block_id,
2252                    );
2253                    continue;
2254                }
2255            }
2256
2257            let next_bank = Bank::new_from_parent(
2258                bank.clone(),
2259                leader_schedule_cache
2260                    .slot_leader_at(*next_slot, Some(bank))
2261                    .unwrap(),
2262                *next_slot,
2263            );
2264            set_alpenglow_ticks(&next_bank, migration_status);
2265            trace!(
2266                "New bank for slot {}, parent slot is {}",
2267                next_slot,
2268                bank.slot(),
2269            );
2270            pending_slots.push((next_meta, next_bank, bank.last_blockhash()));
2271        }
2272    }
2273
2274    // Reverse sort by slot, so the next slot to be processed can be popped
2275    pending_slots.sort_by_key(|b| cmp::Reverse(b.1.slot()));
2276    Ok(())
2277}
2278
2279/// Set alpenglow bank tick height.
2280///
2281/// For alpenglow banks the bank tick height is `max_tick_height` - 1, as only the Alpentick
2282/// (fake tick to signal bank completion) will be present.
2283///
2284/// For PoH banks this is 0.
2285pub fn set_alpenglow_ticks(bank: &Bank, migration_status: &MigrationStatus) {
2286    if !migration_status.should_have_alpenglow_ticks(bank.slot()) {
2287        // PoH Bank do not adjust ticks
2288        return;
2289    }
2290
2291    info!(
2292        "Alpenglow: Setting tick height for slot {} to {}",
2293        bank.slot(),
2294        bank.max_tick_height() - 1
2295    );
2296    bank.set_tick_height(bank.max_tick_height() - 1);
2297}
2298
2299/// Starting with the root slot corresponding to `start_slot_meta`, iteratively
2300/// find and process children slots from the blockstore.
2301///
2302/// Returns a tuple (a, b) where a is the number of slots processed and b is
2303/// the number of newly found cluster roots.
2304#[allow(clippy::too_many_arguments)]
2305fn load_frozen_forks(
2306    bank_forks: &RwLock<BankForks>,
2307    shred_version: u16,
2308    start_slot_meta: &SlotMeta,
2309    blockstore: &Blockstore,
2310    replay_tx_thread_pool: &ThreadPool,
2311    leader_schedule_cache: &LeaderScheduleCache,
2312    opts: &ProcessOptions,
2313    transaction_status_sender: Option<&TransactionStatusSender>,
2314    entry_notification_sender: Option<&EntryNotifierSender>,
2315    timing: &mut ExecuteTimings,
2316    snapshot_controller: Option<&SnapshotController>,
2317) -> result::Result<(u64, usize), BlockstoreProcessorError> {
2318    let migration_status = bank_forks.read().unwrap().migration_status();
2319    let blockstore_max_root = blockstore.max_root();
2320    let mut root = bank_forks.read().unwrap().root();
2321    let max_root = std::cmp::max(root, blockstore_max_root);
2322    info!(
2323        "load_frozen_forks() bank forks root: {root}, latest root from blockstore: \
2324         {blockstore_max_root}, max_root: {max_root}",
2325    );
2326
2327    // The total number of slots processed
2328    let mut total_slots_processed = 0;
2329    // The total number of newly identified root slots
2330    let mut total_rooted_slots = 0;
2331
2332    let mut pending_slots = vec![];
2333    process_next_slots(
2334        &bank_forks
2335            .read()
2336            .unwrap()
2337            .get(start_slot_meta.slot)
2338            .unwrap(),
2339        start_slot_meta,
2340        blockstore,
2341        leader_schedule_cache,
2342        &mut pending_slots,
2343        opts,
2344        &migration_status,
2345    )?;
2346
2347    if Some(bank_forks.read().unwrap().root()) != opts.halt_at_slot {
2348        let mut all_banks = HashMap::new();
2349
2350        const STATUS_REPORT_INTERVAL: Duration = Duration::from_secs(2);
2351        let mut last_status_report = Instant::now();
2352        let mut slots_processed = 0;
2353        let mut txs = 0;
2354        let mut set_root_us = 0;
2355        let mut root_retain_us = 0;
2356        let mut process_single_slot_us = 0;
2357        let mut voting_us = 0;
2358
2359        let mut async_verification = None;
2360        while !pending_slots.is_empty() {
2361            timing.details.per_program_timings.clear();
2362            let (meta, bank, last_entry_hash) = pending_slots.pop().unwrap();
2363            let slot = bank.slot();
2364            if last_status_report.elapsed() > STATUS_REPORT_INTERVAL {
2365                let secs = last_status_report.elapsed().as_secs() as f32;
2366                let slots_per_sec = slots_processed as f32 / secs;
2367                let txs_per_sec = txs as f32 / secs;
2368                info!(
2369                    "processing ledger: slot={slot}, root_slot={root} slots={slots_processed}, \
2370                     slots/s={slots_per_sec}, txs/s={txs_per_sec}"
2371                );
2372                debug!(
2373                    "processing ledger timing: set_root_us={set_root_us}, \
2374                     root_retain_us={root_retain_us}, \
2375                     process_single_slot_us:{process_single_slot_us}, voting_us: {voting_us}"
2376                );
2377
2378                last_status_report = Instant::now();
2379                slots_processed = 0;
2380                txs = 0;
2381                set_root_us = 0;
2382                root_retain_us = 0;
2383                process_single_slot_us = 0;
2384                voting_us = 0;
2385            }
2386
2387            let mut progress = ConfirmationProgress::new_with_async_verification(
2388                last_entry_hash,
2389                async_verification.take(),
2390            );
2391            // Live replay restarts UpdateParent slots from the marker's FEC set.
2392            // Startup replay must use the same offset or a restarted validator can
2393            // execute the obsolete optimistic-parent prefix.
2394            if bank.feature_set.snapshot().alpenglow_fast_leader_handover
2395                && migration_status.should_allow_block_markers(slot)
2396                && leader_slot_index(slot) == 0
2397                && meta.has_update_parent()
2398            {
2399                progress.num_shreds = u64::from(meta.replay_fec_set_index);
2400            }
2401            let mut m = Measure::start("process_single_slot");
2402            let bank = bank_forks.write().unwrap().insert_from_ledger(bank);
2403            if let Err(error) = process_single_slot(
2404                blockstore,
2405                &bank,
2406                shred_version,
2407                replay_tx_thread_pool,
2408                opts,
2409                &mut progress,
2410                transaction_status_sender,
2411                entry_notification_sender,
2412                None,
2413                timing,
2414                &migration_status,
2415            ) {
2416                assert!(bank_forks.write().unwrap().remove(bank.slot()).is_some());
2417                if opts.abort_on_invalid_block {
2418                    return Err(error);
2419                }
2420
2421                // If this block was the first alpenglow block and advanced the migration phase, we can enable alpenglow.
2422                //
2423                // This bank must have failed to freeze as it is an Alpenglow block being verified as a TowerBFT one.
2424                // We are safe to cleanly transition to alpenglow here
2425                if migration_status.is_ready_to_enable() {
2426                    let genesis_slot = migration_status.enable_alpenglow_during_startup();
2427
2428                    // We need to clear pending_slots as it might contain Alpenglow blocks initialized as TowerBFT banks.
2429                    // Clear and populate pending slots from alpenglow genesis
2430                    cleanup_and_populate_pending_from_alpenglow_genesis(
2431                        &bank,
2432                        genesis_slot,
2433                        bank_forks,
2434                        blockstore,
2435                        leader_schedule_cache,
2436                        &mut pending_slots,
2437                        opts,
2438                        &migration_status,
2439                    )?;
2440                }
2441
2442                continue;
2443            }
2444            async_verification = progress.take_async_verification();
2445            txs += progress.num_txs;
2446
2447            // Block must be frozen by this point; otherwise,
2448            // process_single_slot() would have errored above.
2449            assert!(bank.is_frozen());
2450            all_banks.insert(bank.slot(), bank.clone_with_scheduler());
2451            m.stop();
2452            process_single_slot_us += m.as_us();
2453
2454            let mut m = Measure::start("voting");
2455            // If we've reached the last known root in blockstore, start looking
2456            // for newer cluster confirmed roots
2457            let new_root_bank = {
2458                if bank_forks.read().unwrap().root() >= max_root {
2459                    supermajority_root_from_vote_accounts(
2460                        bank.total_epoch_stake(),
2461                        &bank.vote_accounts(),
2462                    ).and_then(|supermajority_root| {
2463                        if supermajority_root > root {
2464                            // If there's a cluster confirmed root greater than our last
2465                            // replayed root, then because the cluster confirmed root should
2466                            // be descended from our last root, it must exist in `all_banks`
2467                            let cluster_root_bank = all_banks.get(&supermajority_root).unwrap();
2468
2469                            // cluster root must be a descendant of our root, otherwise something
2470                            // is drastically wrong
2471                            assert!(cluster_root_bank.ancestors.contains_key(&root));
2472                            info!(
2473                                "blockstore processor found new cluster confirmed root: {}, observed in bank: {}",
2474                                cluster_root_bank.slot(), bank.slot()
2475                            );
2476
2477                            // Ensure cluster-confirmed root and parents are set as root in blockstore
2478                            let mut rooted_slots = vec![];
2479                            let mut new_root_bank = cluster_root_bank.clone_without_scheduler();
2480                            loop {
2481                                if new_root_bank.slot() == root { break; } // Found the last root in the chain, yay!
2482                                assert!(new_root_bank.slot() > root);
2483
2484                                rooted_slots.push((new_root_bank.slot(), Some(new_root_bank.hash())));
2485                                // As noted, the cluster confirmed root should be descended from
2486                                // our last root; therefore parent should be set
2487                                new_root_bank = new_root_bank.parent().unwrap();
2488                            }
2489                            total_rooted_slots += rooted_slots.len();
2490                            if blockstore.is_primary_access() {
2491                                blockstore
2492                                    .mark_slots_as_if_rooted_normally_at_startup(rooted_slots, true)
2493                                    .expect("Blockstore::mark_slots_as_if_rooted_normally_at_startup() should succeed");
2494                            }
2495                            Some(cluster_root_bank)
2496                        } else {
2497                            None
2498                        }
2499                    })
2500                } else if blockstore.is_root(slot) {
2501                    Some(&bank)
2502                } else {
2503                    None
2504                }
2505            }.filter(|new_root_bank| {
2506                // In the case that we've restarted while the migrationary period is going on but before alpenglow
2507                // is enabled, don't root blocks past the migration slot
2508                migration_status.should_root_during_startup(new_root_bank.slot())
2509            });
2510            m.stop();
2511            voting_us += m.as_us();
2512
2513            if let Some(new_root_bank) = new_root_bank {
2514                let mut m = Measure::start("set_root");
2515                root = new_root_bank.slot();
2516
2517                leader_schedule_cache.set_root(new_root_bank);
2518                new_root_bank.prune_program_cache(&bank_forks.read().unwrap());
2519                let _ = bank_forks
2520                    .write()
2521                    .unwrap()
2522                    .set_root(root, snapshot_controller, None);
2523                m.stop();
2524                set_root_us += m.as_us();
2525
2526                // Filter out all non descendants of the new root
2527                let mut m = Measure::start("filter pending slots");
2528                pending_slots
2529                    .retain(|(_, pending_bank, _)| pending_bank.ancestors.contains_key(&root));
2530                all_banks.retain(|_, bank| bank.ancestors.contains_key(&root));
2531                m.stop();
2532                root_retain_us += m.as_us();
2533
2534                // If this root bank activated the feature flag, update migration status
2535                if migration_status.is_pre_feature_activation()
2536                    && let Some(slot) = bank_forks
2537                        .read()
2538                        .unwrap()
2539                        .root_bank()
2540                        .feature_set
2541                        .activated_slot(&agave_feature_set::alpenglow::id())
2542                {
2543                    migration_status.record_feature_activation(slot);
2544                }
2545            }
2546
2547            slots_processed += 1;
2548            total_slots_processed += 1;
2549
2550            trace!(
2551                "Bank for {}slot {} is complete",
2552                if root == slot { "root " } else { "" },
2553                slot,
2554            );
2555
2556            let done_processing = opts
2557                .halt_at_slot
2558                .map(|halt_at_slot| slot >= halt_at_slot)
2559                .unwrap_or(false);
2560            if done_processing {
2561                if opts.run_final_accounts_hash_calc {
2562                    bank.run_final_hash_calc();
2563                }
2564                break;
2565            }
2566
2567            process_next_slots(
2568                &bank,
2569                &meta,
2570                blockstore,
2571                leader_schedule_cache,
2572                &mut pending_slots,
2573                opts,
2574                &migration_status,
2575            )?;
2576        }
2577    } else if opts.run_final_accounts_hash_calc {
2578        bank_forks.read().unwrap().root_bank().run_final_hash_calc();
2579    }
2580
2581    Ok((total_slots_processed, total_rooted_slots))
2582}
2583
2584// `roots` is sorted largest to smallest by root slot
2585fn supermajority_root(roots: &[(Slot, u64)], total_epoch_stake: u64) -> Option<Slot> {
2586    if roots.is_empty() {
2587        return None;
2588    }
2589
2590    // Find latest root
2591    let mut total = 0;
2592    let mut prev_root = roots[0].0;
2593    for (root, stake) in roots.iter() {
2594        assert!(*root <= prev_root);
2595        total += stake;
2596        if total as f64 / total_epoch_stake as f64 > VOTE_THRESHOLD_SIZE {
2597            return Some(*root);
2598        }
2599        prev_root = *root;
2600    }
2601
2602    None
2603}
2604
2605fn supermajority_root_from_vote_accounts(
2606    total_epoch_stake: u64,
2607    vote_accounts: &VoteAccountsHashMap,
2608) -> Option<Slot> {
2609    let mut roots_stakes: Vec<(Slot, u64)> = vote_accounts
2610        .values()
2611        .filter_map(|(stake, account)| {
2612            if *stake == 0 {
2613                return None;
2614            }
2615
2616            Some((account.vote_state_view().root_slot()?, *stake))
2617        })
2618        .collect();
2619
2620    // Sort from greatest to smallest slot
2621    roots_stakes.sort_unstable_by_key(|a| cmp::Reverse(a.0));
2622
2623    // Find latest root
2624    supermajority_root(&roots_stakes, total_epoch_stake)
2625}
2626
2627/// Validates the chained block ID for a child slot against its parent.
2628///
2629/// Returns:
2630/// - `Inactive`: feature not active, or alpenglow is active, no validation performed
2631/// - `Pass`: chained block ID matches parent's block ID (or parent has no
2632///   block ID yet)
2633/// - `Mismatch`: definitive mismatch between child's chained merkle root
2634///   and parent's block ID
2635/// - `Unavailable`: data shred 0 not received yet, cannot validate
2636pub fn check_chained_block_id(
2637    blockstore: &Blockstore,
2638    bank: &Bank,
2639    migration_status: &MigrationStatus,
2640) -> ChainedBlockIdCheck {
2641    let slot = bank.slot();
2642    let feature_snapshot = bank.feature_set.snapshot();
2643    if !(feature_snapshot.validate_chained_block_id || feature_snapshot.validate_chained_block_id_2)
2644        || migration_status.should_use_double_merkle_block_id(slot)
2645    {
2646        return ChainedBlockIdCheck::Inactive;
2647    }
2648
2649    let parent_slot = bank.parent_slot();
2650
2651    let Ok(expected_parent_block_id) = blockstore.get_parent_chained_block_id(slot) else {
2652        return ChainedBlockIdCheck::Unavailable;
2653    };
2654
2655    match blockstore
2656        .get_last_shred_merkle_root(parent_slot)
2657        .expect("Blockstore operations must succeed")
2658    {
2659        Some(parent_block_id) => {
2660            if expected_parent_block_id != parent_block_id {
2661                warn!(
2662                    "Chained merkle root mismatch for slot {slot} (parent {parent_slot}): child \
2663                     chains to {expected_parent_block_id}, but parent block ID is \
2664                     {parent_block_id}"
2665                );
2666                ChainedBlockIdCheck::Mismatch
2667            } else {
2668                ChainedBlockIdCheck::Pass
2669            }
2670        }
2671        None => {
2672            warn!(
2673                "{parent_slot} is missing from our blockstore, likely the snapshot slot. Skipping \
2674                 chained block id verification",
2675            );
2676            ChainedBlockIdCheck::Pass
2677        }
2678    }
2679}
2680
2681fn mark_dead_if_primary_access(blockstore: &Blockstore, slot: Slot) {
2682    if blockstore.is_primary_access() {
2683        blockstore
2684            .set_dead_slot(slot)
2685            .expect("Failed to mark slot as dead in blockstore");
2686    } else {
2687        info!("Failed slot {slot} won't be marked dead due to being read-only blockstore access");
2688    }
2689}
2690
2691fn reset_dead_if_primary_access(blockstore: &Blockstore, slot: Slot) {
2692    if !blockstore.is_dead(slot) {
2693        return;
2694    }
2695    if blockstore.is_primary_access() {
2696        blockstore.remove_dead_slot(slot).unwrap();
2697    } else {
2698        info!("slot {slot} won't be cleared from dead due to being read-only blockstore access");
2699    }
2700}
2701
2702// Processes and replays the contents of a single slot, returns Error
2703// if failed to play the slot.
2704//
2705// For use during startup replay, enforces any pre and post replay checks
2706// that occur in ReplayStage.
2707#[allow(clippy::too_many_arguments)]
2708pub fn process_single_slot(
2709    blockstore: &Blockstore,
2710    bank: &BankWithScheduler,
2711    shred_version: u16,
2712    replay_tx_thread_pool: &ThreadPool,
2713    opts: &ProcessOptions,
2714    progress: &mut ConfirmationProgress,
2715    transaction_status_sender: Option<&TransactionStatusSender>,
2716    entry_notification_sender: Option<&EntryNotifierSender>,
2717    replay_vote_sender: Option<&ReplayVoteSender>,
2718    timing: &mut ExecuteTimings,
2719    migration_status: &MigrationStatus,
2720) -> result::Result<(), BlockstoreProcessorError> {
2721    let slot = bank.slot();
2722    if !opts.skip_inter_slot_verification {
2723        match check_chained_block_id(blockstore, bank, migration_status) {
2724            ChainedBlockIdCheck::Inactive | ChainedBlockIdCheck::Pass => (),
2725            ChainedBlockIdCheck::Unavailable => {
2726                // no shreds to replay
2727                return Ok(());
2728            }
2729            ChainedBlockIdCheck::Mismatch => {
2730                // Mismatch, mark dead
2731                mark_dead_if_primary_access(blockstore, slot);
2732                return Err(BlockstoreProcessorError::ChainedBlockIdFailure(
2733                    slot,
2734                    bank.parent_slot(),
2735                ));
2736            }
2737        }
2738    }
2739
2740    // Mark corrupt slots as dead so validators don't replay this slot and
2741    // see AlreadyProcessed errors later in ReplayStage
2742    confirm_full_slot(
2743        blockstore,
2744        bank,
2745        shred_version,
2746        replay_tx_thread_pool,
2747        opts,
2748        progress,
2749        transaction_status_sender,
2750        entry_notification_sender,
2751        replay_vote_sender,
2752        timing,
2753        migration_status,
2754    )
2755    .map_err(|err| {
2756        warn!("slot {slot} failed to verify: {err}");
2757        mark_dead_if_primary_access(blockstore, slot);
2758        err
2759    })?;
2760
2761    let block_id = blockstore
2762        .get_block_id(slot, migration_status)
2763        .expect("Blockstore operations must succeed")
2764        .expect("Full block must have block id");
2765    bank.set_block_id(Some(block_id));
2766    let verify_result = bank.freeze_and_verify_bank_hash(); // all banks handled by this routine are created from complete slots
2767
2768    if let Err((expected_hash, computed_hash)) = verify_result {
2769        warn!(
2770            "slot {slot} failed to freeze, bank hash mismatch expected {expected_hash} computed \
2771             {computed_hash}"
2772        );
2773        mark_dead_if_primary_access(blockstore, slot);
2774        return Err(BlockstoreProcessorError::BankHashMismatch(
2775            slot,
2776            expected_hash,
2777            computed_hash,
2778        ));
2779    }
2780
2781    if let Some(slot_callback) = &opts.slot_callback {
2782        slot_callback(bank);
2783    }
2784
2785    if blockstore.is_primary_access() {
2786        blockstore.insert_bank_hash(bank.slot(), bank.hash(), false);
2787    }
2788
2789    if let Some(transaction_status_sender) = transaction_status_sender {
2790        transaction_status_sender.send_transaction_status_freeze_message(bank);
2791    }
2792
2793    Ok(())
2794}
2795
2796type WorkSequence = u64;
2797
2798#[allow(clippy::large_enum_variant)]
2799#[derive(Debug)]
2800pub enum TransactionStatusMessage {
2801    Batch((TransactionStatusBatch, Option<WorkSequence>)),
2802    Freeze(Arc<Bank>),
2803}
2804
2805#[derive(Debug)]
2806pub struct TransactionStatusBatch {
2807    pub slot: Slot,
2808    pub transactions: Vec<SanitizedTransaction>,
2809    pub commit_results: Vec<TransactionCommitResult>,
2810    pub balances: TransactionBalancesSet,
2811    pub token_balances: TransactionTokenBalancesSet,
2812    pub costs: Vec<Option<u64>>,
2813    pub transaction_indexes: Vec<usize>,
2814}
2815
2816#[derive(Clone, Debug)]
2817pub struct TransactionStatusSender {
2818    pub sender: Sender<TransactionStatusMessage>,
2819    pub dependency_tracker: Option<Arc<DependencyTracker>>,
2820}
2821
2822impl TransactionStatusSender {
2823    pub fn send_transaction_status_batch(
2824        &self,
2825        slot: Slot,
2826        transactions: Vec<SanitizedTransaction>,
2827        commit_results: Vec<TransactionCommitResult>,
2828        balances: TransactionBalancesSet,
2829        token_balances: TransactionTokenBalancesSet,
2830        costs: Vec<Option<u64>>,
2831        transaction_indexes: Vec<usize>,
2832    ) {
2833        let work_sequence = self
2834            .dependency_tracker
2835            .as_ref()
2836            .map(|dependency_tracker| dependency_tracker.declare_work());
2837
2838        if let Err(e) = self.sender.send(TransactionStatusMessage::Batch((
2839            TransactionStatusBatch {
2840                slot,
2841                transactions,
2842                commit_results,
2843                balances,
2844                token_balances,
2845                costs,
2846                transaction_indexes,
2847            },
2848            work_sequence,
2849        ))) {
2850            trace!("Slot {slot} transaction_status send batch failed: {e:?}");
2851        }
2852    }
2853
2854    pub fn send_transaction_status_freeze_message(&self, bank: &Arc<Bank>) {
2855        if let Err(e) = self
2856            .sender
2857            .send(TransactionStatusMessage::Freeze(bank.clone()))
2858        {
2859            let slot = bank.slot();
2860            warn!("Slot {slot} transaction_status send freeze message failed: {e:?}");
2861        }
2862    }
2863}
2864
2865// used for tests only
2866pub fn fill_blockstore_slot_with_ticks(
2867    blockstore: &Blockstore,
2868    ticks_per_slot: u64,
2869    slot: u64,
2870    parent_slot: u64,
2871    last_entry_hash: Hash,
2872) -> Hash {
2873    // Only slot 0 can be equal to the parent_slot
2874    assert!(slot.saturating_sub(1) >= parent_slot);
2875    let num_slots = (slot - parent_slot).max(1);
2876    let entries = create_ticks(num_slots * ticks_per_slot, 0, last_entry_hash);
2877    let last_entry_hash = entries.last().unwrap().hash;
2878
2879    blockstore
2880        .write_entries(
2881            slot,
2882            0,
2883            0,
2884            ticks_per_slot,
2885            Some(parent_slot),
2886            true,
2887            &Arc::new(Keypair::new()),
2888            entries,
2889            0,
2890        )
2891        .unwrap();
2892
2893    last_entry_hash
2894}
2895
2896#[cfg(test)]
2897pub mod tests {
2898    use {
2899        super::*,
2900        crate::{
2901            blockstore_options::{AccessType, BlockstoreOptions},
2902            genesis_utils::{
2903                GenesisConfigInfo, create_genesis_config, create_genesis_config_with_leader,
2904            },
2905            shred::{ProcessShredsStats, ReedSolomonCache, Shred, Shredder},
2906        },
2907        agave_votor_messages::{
2908            certificate::{Certificate, CertificateType},
2909            consensus_message::Block,
2910        },
2911        assert_matches::assert_matches,
2912        crossbeam_channel::bounded,
2913        rand::{Rng, rng},
2914        rayon::ThreadPoolBuilder,
2915        solana_account::{AccountSharedData, WritableAccount},
2916        solana_bls_signatures::{BLS_SIGNATURE_AFFINE_SIZE, Signature as BLSSignature},
2917        solana_cost_model::cost_tracker::CostTrackerLimits,
2918        solana_entry::{
2919            block_component::{BlockComponent, BlockFooterV1, BlockHeaderV1, VersionedBlockMarker},
2920            entry::{create_ticks, next_entry, next_entry_mut},
2921        },
2922        solana_epoch_schedule::EpochSchedule,
2923        solana_hash::Hash,
2924        solana_instruction::{Instruction, error::InstructionError},
2925        solana_keypair::Keypair,
2926        solana_leader_schedule::SlotLeader,
2927        solana_native_token::LAMPORTS_PER_SOL,
2928        solana_program_runtime::{
2929            declare_process_instruction, solana_sbpf::program::BuiltinFunctionDefinition,
2930        },
2931        solana_pubkey::Pubkey,
2932        solana_runtime::{
2933            bank::bank_hash_details::SlotDetails,
2934            genesis_utils::{
2935                self, ValidatorVoteKeypairs, create_genesis_config_with_vote_accounts,
2936            },
2937            installed_scheduler_pool::{
2938                MockInstalledScheduler, MockUninstalledScheduler, SchedulerAborted,
2939                SchedulingContext,
2940            },
2941        },
2942        solana_signer::Signer,
2943        solana_svm::transaction_processor::ExecutionRecordingConfig,
2944        solana_system_interface::error::SystemError,
2945        solana_system_transaction as system_transaction,
2946        solana_transaction::Transaction,
2947        solana_transaction_error::TransactionError,
2948        solana_vote::{vote_account::VoteAccount, vote_transaction},
2949        solana_vote_program::{
2950            self,
2951            vote_state::{MAX_LOCKOUT_HISTORY, TowerSync, VoteStateV4, VoteStateVersions},
2952        },
2953        std::{
2954            collections::BTreeSet,
2955            slice,
2956            sync::{Arc, Barrier, RwLock, atomic::Ordering},
2957            thread,
2958        },
2959        test_case::test_matrix,
2960        trees::tr,
2961    };
2962
2963    /// Generate a dummy alpenglow genesis certificate
2964    fn genesis_certificate(genesis_block: Block) -> Arc<Certificate> {
2965        Arc::new(Certificate {
2966            cert_type: CertificateType::Genesis(genesis_block),
2967            signature: BLSSignature([0; BLS_SIGNATURE_AFFINE_SIZE]),
2968            bitmap: vec![],
2969        })
2970    }
2971
2972    /// Generate a dummy `MigrationStatus` in the `ReadyToEnable` phase
2973    fn ready_to_enable_migration_status(genesis_block: Block) -> MigrationStatus {
2974        let migration_status = MigrationStatus::default();
2975        let migration_slot = migration_status.record_feature_activation(0);
2976        assert!(genesis_block.slot < migration_slot);
2977        migration_status.set_genesis_block(genesis_block);
2978        migration_status.set_genesis_certificate(genesis_certificate(genesis_block));
2979        assert!(migration_status.is_ready_to_enable());
2980        migration_status
2981    }
2982
2983    #[test]
2984    fn test_startup_replay_enable_waits_for_poh_service_when_started() {
2985        let genesis_block = Block {
2986            slot: 1,
2987            block_id: Hash::new_from_array([7; solana_hash::HASH_BYTES]),
2988        };
2989        let migration_status = Arc::new(ready_to_enable_migration_status(genesis_block));
2990        let poh_service = {
2991            let migration_status = Arc::clone(&migration_status);
2992            migration_status.set_poh_service_started();
2993            thread::spawn(move || {
2994                while !migration_status.shutdown_poh.load(Ordering::Acquire) {
2995                    thread::yield_now();
2996                }
2997                migration_status.poh_service_is_shutting_down();
2998            })
2999        };
3000
3001        assert_eq!(
3002            migration_status.enable_alpenglow_during_startup(),
3003            genesis_block.slot
3004        );
3005        poh_service.join().unwrap();
3006
3007        assert!(migration_status.is_alpenglow_enabled());
3008        assert_eq!(
3009            migration_status.wait_for_migration_or_exit(&AtomicBool::new(false)),
3010            Some(genesis_block)
3011        );
3012    }
3013
3014    fn test_process_blockstore(
3015        genesis_config: &GenesisConfig,
3016        blockstore: &Blockstore,
3017        opts: &ProcessOptions,
3018    ) -> (Arc<RwLock<BankForks>>, LeaderScheduleCache) {
3019        let exit = Arc::default();
3020        let (bank_forks, _) = crate::bank_forks_utils::load_bank_forks_from_genesis(
3021            genesis_config,
3022            blockstore,
3023            Vec::new(),
3024            opts,
3025            None,
3026            None,
3027            None,
3028            exit,
3029        )
3030        .unwrap();
3031
3032        let leader_schedule_cache =
3033            LeaderScheduleCache::new_from_bank(&bank_forks.read().unwrap().root_bank());
3034
3035        process_blockstore_from_root(
3036            blockstore,
3037            &bank_forks,
3038            compute_shred_version(&genesis_config.hash(), None),
3039            &leader_schedule_cache,
3040            opts,
3041            None,
3042            None,
3043            None, // snapshots are disabled
3044        )
3045        .unwrap();
3046
3047        (bank_forks, leader_schedule_cache)
3048    }
3049
3050    // Convenience wrapper to optionally process blockstore with ReadOnly access.
3051    //
3052    // Setting up the ledger for a test requires Primary access as items will need to be inserted.
3053    // However, once a ReadOnly access has been opened, it won't automatically see updates made by
3054    // the Primary access. So, open (and close) the ReadOnly access within this function to ensure
3055    // that "stale" ReadOnly accesses don't propagate.
3056    fn test_process_blockstore_with_custom_options(
3057        genesis_config: &GenesisConfig,
3058        blockstore: &Blockstore,
3059        opts: &ProcessOptions,
3060        access_type: AccessType,
3061    ) -> (Arc<RwLock<BankForks>>, LeaderScheduleCache) {
3062        match access_type {
3063            AccessType::Primary | AccessType::PrimaryForMaintenance => {
3064                // Attempting to open a second Primary access would fail, so
3065                // just pass the original session if it is a Primary variant
3066                test_process_blockstore(genesis_config, blockstore, opts)
3067            }
3068            AccessType::ReadOnly => {
3069                let read_only_blockstore = Blockstore::open_with_options(
3070                    blockstore.ledger_path(),
3071                    BlockstoreOptions {
3072                        access_type,
3073                        ..BlockstoreOptions::default()
3074                    },
3075                )
3076                .expect("Unable to open access to blockstore");
3077                test_process_blockstore(genesis_config, &read_only_blockstore, opts)
3078            }
3079        }
3080    }
3081
3082    fn process_entries_for_tests_without_scheduler(
3083        bank: &Arc<Bank>,
3084        entries: Vec<Entry>,
3085    ) -> Result<()> {
3086        process_entries_for_tests(
3087            &BankWithScheduler::new_without_scheduler(bank.clone()),
3088            entries,
3089            None,
3090            None,
3091        )
3092    }
3093
3094    #[test]
3095    fn test_process_blockstore_with_missing_hashes() {
3096        do_test_process_blockstore_with_missing_hashes(AccessType::Primary);
3097    }
3098
3099    #[test]
3100    fn test_process_blockstore_with_missing_hashes_read_only_access() {
3101        do_test_process_blockstore_with_missing_hashes(AccessType::ReadOnly);
3102    }
3103
3104    // Intentionally make slot 1 faulty and ensure that processing sees it as dead
3105    fn do_test_process_blockstore_with_missing_hashes(blockstore_access_type: AccessType) {
3106        agave_logger::setup();
3107
3108        let hashes_per_tick = 4;
3109        let GenesisConfigInfo {
3110            mut genesis_config, ..
3111        } = create_genesis_config(10_000);
3112        genesis_config.poh_config.hashes_per_tick = Some(hashes_per_tick);
3113        let ticks_per_slot = genesis_config.ticks_per_slot;
3114
3115        let (ledger_path, blockhash) = create_new_tmp_ledger_auto_delete!(&genesis_config);
3116        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
3117
3118        let parent_slot = 0;
3119        let slot = 1;
3120        let entries = create_ticks(ticks_per_slot, 1, blockhash);
3121        assert_matches!(
3122            blockstore.write_entries(
3123                slot,
3124                0,
3125                0,
3126                ticks_per_slot,
3127                Some(parent_slot),
3128                true,
3129                &Arc::new(Keypair::new()),
3130                entries,
3131                0,
3132            ),
3133            Ok(_)
3134        );
3135
3136        let (bank_forks, ..) = test_process_blockstore_with_custom_options(
3137            &genesis_config,
3138            &blockstore,
3139            &ProcessOptions {
3140                run_verification: true,
3141                ..ProcessOptions::default()
3142            },
3143            blockstore_access_type.clone(),
3144        );
3145        assert_eq!(frozen_bank_slots(&bank_forks.read().unwrap()), vec![0]);
3146
3147        let dead_slots: Vec<Slot> = blockstore.dead_slots_iterator(0).unwrap().collect();
3148        match blockstore_access_type {
3149            // In ReadOnly access even though a dead slot
3150            // will be identified, it won't actually be marked dead.
3151            AccessType::ReadOnly => {
3152                assert_eq!(dead_slots.len(), 0);
3153            }
3154            AccessType::Primary | AccessType::PrimaryForMaintenance => {
3155                assert_eq!(&dead_slots, &[1]);
3156            }
3157        }
3158    }
3159
3160    #[test]
3161    fn test_process_blockstore_with_invalid_slot_tick_count() {
3162        agave_logger::setup();
3163
3164        let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
3165        let ticks_per_slot = genesis_config.ticks_per_slot;
3166
3167        // Create a new ledger with slot 0 full of ticks
3168        let (ledger_path, blockhash) = create_new_tmp_ledger_auto_delete!(&genesis_config);
3169        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
3170
3171        // Write slot 1 with one tick missing
3172        let parent_slot = 0;
3173        let slot = 1;
3174        let entries = create_ticks(ticks_per_slot - 1, 0, blockhash);
3175        assert_matches!(
3176            blockstore.write_entries(
3177                slot,
3178                0,
3179                0,
3180                ticks_per_slot,
3181                Some(parent_slot),
3182                true,
3183                &Arc::new(Keypair::new()),
3184                entries,
3185                0,
3186            ),
3187            Ok(_)
3188        );
3189
3190        // Should return slot 0, the last slot on the fork that is valid
3191        let (bank_forks, ..) = test_process_blockstore(
3192            &genesis_config,
3193            &blockstore,
3194            &ProcessOptions {
3195                run_verification: true,
3196                ..ProcessOptions::default()
3197            },
3198        );
3199        assert_eq!(frozen_bank_slots(&bank_forks.read().unwrap()), vec![0]);
3200
3201        // Write slot 2 fully
3202        let _last_slot2_entry_hash =
3203            fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 2, 0, blockhash);
3204
3205        let (bank_forks, ..) = test_process_blockstore(
3206            &genesis_config,
3207            &blockstore,
3208            &ProcessOptions {
3209                run_verification: true,
3210                ..ProcessOptions::default()
3211            },
3212        );
3213
3214        // One valid fork, one bad fork.  process_blockstore() should only return the valid fork
3215        assert_eq!(frozen_bank_slots(&bank_forks.read().unwrap()), vec![0, 2]);
3216        assert_eq!(bank_forks.read().unwrap().working_bank().slot(), 2);
3217        assert_eq!(bank_forks.read().unwrap().root(), 0);
3218    }
3219
3220    #[test]
3221    fn test_process_blockstore_with_slot_with_trailing_entry() {
3222        agave_logger::setup();
3223
3224        let GenesisConfigInfo {
3225            mint_keypair,
3226            genesis_config,
3227            ..
3228        } = create_genesis_config(10_000);
3229        let ticks_per_slot = genesis_config.ticks_per_slot;
3230
3231        let (ledger_path, blockhash) = create_new_tmp_ledger_auto_delete!(&genesis_config);
3232        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
3233
3234        let mut entries = create_ticks(ticks_per_slot, 0, blockhash);
3235        let trailing_entry = {
3236            let keypair = Keypair::new();
3237            let tx = system_transaction::transfer(&mint_keypair, &keypair.pubkey(), 1, blockhash);
3238            next_entry(&blockhash, 1, vec![tx])
3239        };
3240        entries.push(trailing_entry);
3241
3242        // Tricks blockstore into writing the trailing entry by lying that there is one more tick
3243        // per slot.
3244        let parent_slot = 0;
3245        let slot = 1;
3246        assert_matches!(
3247            blockstore.write_entries(
3248                slot,
3249                0,
3250                0,
3251                ticks_per_slot + 1,
3252                Some(parent_slot),
3253                true,
3254                &Arc::new(Keypair::new()),
3255                entries,
3256                0,
3257            ),
3258            Ok(_)
3259        );
3260
3261        let opts = ProcessOptions {
3262            run_verification: true,
3263            ..ProcessOptions::default()
3264        };
3265        let (bank_forks, ..) = test_process_blockstore(&genesis_config, &blockstore, &opts);
3266        assert_eq!(frozen_bank_slots(&bank_forks.read().unwrap()), vec![0]);
3267    }
3268
3269    #[test]
3270    fn test_process_blockstore_with_incomplete_slot() {
3271        agave_logger::setup();
3272
3273        let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
3274        let ticks_per_slot = genesis_config.ticks_per_slot;
3275
3276        /*
3277          Build a blockstore in the ledger with the following fork structure:
3278
3279               slot 0 (all ticks)
3280                 |
3281               slot 1 (all ticks but one)
3282                 |
3283               slot 2 (all ticks)
3284
3285           where slot 1 is incomplete (missing 1 tick at the end)
3286        */
3287
3288        // Create a new ledger with slot 0 full of ticks
3289        let (ledger_path, mut blockhash) = create_new_tmp_ledger_auto_delete!(&genesis_config);
3290        debug!("ledger_path: {ledger_path:?}");
3291
3292        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
3293
3294        // Write slot 1
3295        // slot 1, points at slot 0.  Missing one tick
3296        {
3297            let parent_slot = 0;
3298            let slot = 1;
3299            let mut entries = create_ticks(ticks_per_slot, 0, blockhash);
3300            blockhash = entries.last().unwrap().hash;
3301
3302            // throw away last one
3303            entries.pop();
3304
3305            assert_matches!(
3306                blockstore.write_entries(
3307                    slot,
3308                    0,
3309                    0,
3310                    ticks_per_slot,
3311                    Some(parent_slot),
3312                    false,
3313                    &Arc::new(Keypair::new()),
3314                    entries,
3315                    0,
3316                ),
3317                Ok(_)
3318            );
3319        }
3320
3321        // slot 2, points at slot 1
3322        fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 2, 1, blockhash);
3323
3324        let opts = ProcessOptions {
3325            run_verification: true,
3326            ..ProcessOptions::default()
3327        };
3328        let (bank_forks, ..) = test_process_blockstore(&genesis_config, &blockstore, &opts);
3329
3330        assert_eq!(frozen_bank_slots(&bank_forks.read().unwrap()), vec![0]); // slot 1 isn't "full", we stop at slot zero
3331
3332        /* Add a complete slot such that the store looks like:
3333
3334                                 slot 0 (all ticks)
3335                               /                  \
3336               slot 1 (all ticks but one)        slot 3 (all ticks)
3337                      |
3338               slot 2 (all ticks)
3339        */
3340        let opts = ProcessOptions {
3341            run_verification: true,
3342            ..ProcessOptions::default()
3343        };
3344        fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 3, 0, blockhash);
3345        // Slot 0 should not show up in the ending bank_forks_info
3346        let (bank_forks, ..) = test_process_blockstore(&genesis_config, &blockstore, &opts);
3347
3348        // slot 1 isn't "full", we stop at slot zero
3349        assert_eq!(frozen_bank_slots(&bank_forks.read().unwrap()), vec![0, 3]);
3350    }
3351
3352    #[test]
3353    fn test_process_blockstore_with_two_forks_and_squash() {
3354        agave_logger::setup();
3355
3356        let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
3357        let ticks_per_slot = genesis_config.ticks_per_slot;
3358
3359        // Create a new ledger with slot 0 full of ticks
3360        let (ledger_path, blockhash) = create_new_tmp_ledger_auto_delete!(&genesis_config);
3361        debug!("ledger_path: {ledger_path:?}");
3362        let mut last_entry_hash = blockhash;
3363
3364        /*
3365            Build a blockstore in the ledger with the following fork structure:
3366
3367                 slot 0
3368                   |
3369                 slot 1
3370                 /   \
3371            slot 2   |
3372               /     |
3373            slot 3   |
3374                     |
3375                   slot 4 <-- set_root(true)
3376
3377        */
3378        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
3379
3380        // Fork 1, ending at slot 3
3381        let last_slot1_entry_hash =
3382            fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 1, 0, last_entry_hash);
3383        last_entry_hash = fill_blockstore_slot_with_ticks(
3384            &blockstore,
3385            ticks_per_slot,
3386            2,
3387            1,
3388            last_slot1_entry_hash,
3389        );
3390        let last_fork1_entry_hash =
3391            fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 3, 2, last_entry_hash);
3392
3393        // Fork 2, ending at slot 4
3394        let last_fork2_entry_hash = fill_blockstore_slot_with_ticks(
3395            &blockstore,
3396            ticks_per_slot,
3397            4,
3398            1,
3399            last_slot1_entry_hash,
3400        );
3401
3402        info!("last_fork1_entry.hash: {last_fork1_entry_hash:?}");
3403        info!("last_fork2_entry.hash: {last_fork2_entry_hash:?}");
3404
3405        blockstore.set_roots([0, 1, 4].iter()).unwrap();
3406
3407        let opts = ProcessOptions {
3408            run_verification: true,
3409            ..ProcessOptions::default()
3410        };
3411        let (bank_forks, ..) = test_process_blockstore(&genesis_config, &blockstore, &opts);
3412        let bank_forks = bank_forks.read().unwrap();
3413
3414        // One fork, other one is ignored b/c not a descendant of the root
3415        assert_eq!(frozen_bank_slots(&bank_forks), vec![4]);
3416
3417        assert!(
3418            &bank_forks[4]
3419                .parents()
3420                .iter()
3421                .map(|bank| bank.slot())
3422                .next()
3423                .is_none()
3424        );
3425
3426        // Ensure bank_forks holds the right banks
3427        verify_fork_infos(&bank_forks);
3428
3429        assert_eq!(bank_forks.root(), 4);
3430    }
3431
3432    #[test]
3433    fn test_process_blockstore_with_two_forks() {
3434        agave_logger::setup();
3435
3436        let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
3437        let ticks_per_slot = genesis_config.ticks_per_slot;
3438
3439        // Create a new ledger with slot 0 full of ticks
3440        let (ledger_path, blockhash) = create_new_tmp_ledger_auto_delete!(&genesis_config);
3441        debug!("ledger_path: {ledger_path:?}");
3442        let mut last_entry_hash = blockhash;
3443
3444        /*
3445            Build a blockstore in the ledger with the following fork structure:
3446
3447                 slot 0
3448                   |
3449                 slot 1  <-- set_root(true)
3450                 /   \
3451            slot 2   |
3452               /     |
3453            slot 3   |
3454                     |
3455                   slot 4
3456
3457        */
3458        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
3459
3460        // Fork 1, ending at slot 3
3461        let last_slot1_entry_hash =
3462            fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 1, 0, last_entry_hash);
3463        last_entry_hash = fill_blockstore_slot_with_ticks(
3464            &blockstore,
3465            ticks_per_slot,
3466            2,
3467            1,
3468            last_slot1_entry_hash,
3469        );
3470        let last_fork1_entry_hash =
3471            fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 3, 2, last_entry_hash);
3472
3473        // Fork 2, ending at slot 4
3474        let last_fork2_entry_hash = fill_blockstore_slot_with_ticks(
3475            &blockstore,
3476            ticks_per_slot,
3477            4,
3478            1,
3479            last_slot1_entry_hash,
3480        );
3481
3482        info!("last_fork1_entry.hash: {last_fork1_entry_hash:?}");
3483        info!("last_fork2_entry.hash: {last_fork2_entry_hash:?}");
3484
3485        blockstore.set_roots([0, 1].iter()).unwrap();
3486
3487        let opts = ProcessOptions {
3488            run_verification: true,
3489            ..ProcessOptions::default()
3490        };
3491        let (bank_forks, ..) = test_process_blockstore(&genesis_config, &blockstore, &opts);
3492        let bank_forks = bank_forks.read().unwrap();
3493
3494        assert_eq!(frozen_bank_slots(&bank_forks), vec![1, 2, 3, 4]);
3495        assert_eq!(bank_forks.working_bank().slot(), 4);
3496        assert_eq!(bank_forks.root(), 1);
3497
3498        assert_eq!(
3499            &bank_forks[3]
3500                .parents()
3501                .iter()
3502                .map(|bank| bank.slot())
3503                .collect::<Vec<_>>(),
3504            &[2, 1]
3505        );
3506        assert_eq!(
3507            &bank_forks[4]
3508                .parents()
3509                .iter()
3510                .map(|bank| bank.slot())
3511                .collect::<Vec<_>>(),
3512            &[1]
3513        );
3514
3515        assert_eq!(bank_forks.root(), 1);
3516
3517        // Ensure bank_forks holds the right banks
3518        verify_fork_infos(&bank_forks);
3519    }
3520
3521    #[test]
3522    fn test_process_blockstore_with_dead_slot() {
3523        agave_logger::setup();
3524
3525        let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
3526        let ticks_per_slot = genesis_config.ticks_per_slot;
3527        let (ledger_path, blockhash) = create_new_tmp_ledger_auto_delete!(&genesis_config);
3528        debug!("ledger_path: {ledger_path:?}");
3529
3530        /*
3531                   slot 0
3532                     |
3533                   slot 1
3534                  /     \
3535                 /       \
3536           slot 2 (dead)  \
3537                           \
3538                        slot 3
3539        */
3540        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
3541        let slot1_blockhash =
3542            fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 1, 0, blockhash);
3543        fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 2, 1, slot1_blockhash);
3544        blockstore.set_dead_slot(2).unwrap();
3545        fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 3, 1, slot1_blockhash);
3546
3547        let (bank_forks, ..) =
3548            test_process_blockstore(&genesis_config, &blockstore, &ProcessOptions::default());
3549        let bank_forks = bank_forks.read().unwrap();
3550
3551        assert_eq!(frozen_bank_slots(&bank_forks), vec![0, 1, 3]);
3552        assert_eq!(bank_forks.working_bank().slot(), 3);
3553        assert_eq!(
3554            &bank_forks[3]
3555                .parents()
3556                .iter()
3557                .map(|bank| bank.slot())
3558                .collect::<Vec<_>>(),
3559            &[1, 0]
3560        );
3561        verify_fork_infos(&bank_forks);
3562    }
3563
3564    #[test]
3565    fn test_process_blockstore_with_dead_child() {
3566        agave_logger::setup();
3567
3568        let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
3569        let ticks_per_slot = genesis_config.ticks_per_slot;
3570        let (ledger_path, blockhash) = create_new_tmp_ledger_auto_delete!(&genesis_config);
3571        debug!("ledger_path: {ledger_path:?}");
3572
3573        /*
3574                   slot 0
3575                     |
3576                   slot 1
3577                  /     \
3578                 /       \
3579              slot 2      \
3580               /           \
3581           slot 4 (dead)   slot 3
3582        */
3583        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
3584        let slot1_blockhash =
3585            fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 1, 0, blockhash);
3586        let slot2_blockhash =
3587            fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 2, 1, slot1_blockhash);
3588        fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 4, 2, slot2_blockhash);
3589        blockstore.set_dead_slot(4).unwrap();
3590        fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 3, 1, slot1_blockhash);
3591
3592        let (bank_forks, ..) =
3593            test_process_blockstore(&genesis_config, &blockstore, &ProcessOptions::default());
3594        let bank_forks = bank_forks.read().unwrap();
3595
3596        // Should see the parent of the dead child
3597        assert_eq!(frozen_bank_slots(&bank_forks), vec![0, 1, 2, 3]);
3598        assert_eq!(bank_forks.working_bank().slot(), 3);
3599
3600        assert_eq!(
3601            &bank_forks[3]
3602                .parents()
3603                .iter()
3604                .map(|bank| bank.slot())
3605                .collect::<Vec<_>>(),
3606            &[1, 0]
3607        );
3608        assert_eq!(
3609            &bank_forks[2]
3610                .parents()
3611                .iter()
3612                .map(|bank| bank.slot())
3613                .collect::<Vec<_>>(),
3614            &[1, 0]
3615        );
3616        assert_eq!(bank_forks.working_bank().slot(), 3);
3617        verify_fork_infos(&bank_forks);
3618    }
3619
3620    #[test]
3621    fn test_root_with_all_dead_children() {
3622        agave_logger::setup();
3623
3624        let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
3625        let ticks_per_slot = genesis_config.ticks_per_slot;
3626        let (ledger_path, blockhash) = create_new_tmp_ledger_auto_delete!(&genesis_config);
3627        debug!("ledger_path: {ledger_path:?}");
3628
3629        /*
3630                   slot 0
3631                 /        \
3632                /          \
3633           slot 1 (dead)  slot 2 (dead)
3634        */
3635        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
3636        fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 1, 0, blockhash);
3637        fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 2, 0, blockhash);
3638        blockstore.set_dead_slot(1).unwrap();
3639        blockstore.set_dead_slot(2).unwrap();
3640        let (bank_forks, ..) =
3641            test_process_blockstore(&genesis_config, &blockstore, &ProcessOptions::default());
3642        let bank_forks = bank_forks.read().unwrap();
3643
3644        // Should see only the parent of the dead children
3645        assert_eq!(frozen_bank_slots(&bank_forks), vec![0]);
3646        verify_fork_infos(&bank_forks);
3647    }
3648
3649    #[test]
3650    fn test_process_blockstore_epoch_boundary_root() {
3651        agave_logger::setup();
3652
3653        let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
3654        let ticks_per_slot = genesis_config.ticks_per_slot;
3655
3656        // Create a new ledger with slot 0 full of ticks
3657        let (ledger_path, blockhash) = create_new_tmp_ledger_auto_delete!(&genesis_config);
3658        let mut last_entry_hash = blockhash;
3659
3660        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
3661
3662        // Let `last_slot` be the number of slots in the first two epochs
3663        let epoch_schedule = get_epoch_schedule(&genesis_config);
3664        let last_slot = epoch_schedule.get_last_slot_in_epoch(1);
3665
3666        // Create a single chain of slots with all indexes in the range [0, v + 1]
3667        for i in 1..=last_slot + 1 {
3668            last_entry_hash = fill_blockstore_slot_with_ticks(
3669                &blockstore,
3670                ticks_per_slot,
3671                i,
3672                i - 1,
3673                last_entry_hash,
3674            );
3675        }
3676
3677        // Set a root on the last slot of the last confirmed epoch
3678        let rooted_slots: Vec<Slot> = (0..=last_slot).collect();
3679        blockstore.set_roots(rooted_slots.iter()).unwrap();
3680
3681        // Set a root on the next slot of the confirmed epoch
3682        blockstore
3683            .set_roots(std::iter::once(&(last_slot + 1)))
3684            .unwrap();
3685
3686        // Check that we can properly restart the ledger / leader scheduler doesn't fail
3687        let opts = ProcessOptions {
3688            run_verification: true,
3689            ..ProcessOptions::default()
3690        };
3691        let (bank_forks, ..) = test_process_blockstore(&genesis_config, &blockstore, &opts);
3692        let bank_forks = bank_forks.read().unwrap();
3693
3694        // There is one fork, head is last_slot + 1
3695        assert_eq!(frozen_bank_slots(&bank_forks), vec![last_slot + 1]);
3696
3697        // The latest root should have purged all its parents
3698        assert!(
3699            &bank_forks[last_slot + 1]
3700                .parents()
3701                .iter()
3702                .map(|bank| bank.slot())
3703                .next()
3704                .is_none()
3705        );
3706    }
3707
3708    #[test]
3709    fn test_first_err() {
3710        assert_eq!(first_err(&[Ok(())]), Ok(()));
3711        assert_eq!(
3712            first_err(&[Ok(()), Err(TransactionError::AlreadyProcessed)]),
3713            Err(TransactionError::AlreadyProcessed)
3714        );
3715        assert_eq!(
3716            first_err(&[
3717                Ok(()),
3718                Err(TransactionError::AlreadyProcessed),
3719                Err(TransactionError::AccountInUse)
3720            ]),
3721            Err(TransactionError::AlreadyProcessed)
3722        );
3723        assert_eq!(
3724            first_err(&[
3725                Ok(()),
3726                Err(TransactionError::AccountInUse),
3727                Err(TransactionError::AlreadyProcessed)
3728            ]),
3729            Err(TransactionError::AccountInUse)
3730        );
3731        assert_eq!(
3732            first_err(&[
3733                Err(TransactionError::AccountInUse),
3734                Ok(()),
3735                Err(TransactionError::AlreadyProcessed)
3736            ]),
3737            Err(TransactionError::AccountInUse)
3738        );
3739    }
3740
3741    #[test]
3742    fn test_process_empty_entry_is_registered() {
3743        agave_logger::setup();
3744
3745        let GenesisConfigInfo {
3746            genesis_config,
3747            mint_keypair,
3748            ..
3749        } = create_genesis_config(2);
3750        let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
3751        let keypair = Keypair::new();
3752        let slot_entries = create_ticks(genesis_config.ticks_per_slot, 1, genesis_config.hash());
3753        let tx = system_transaction::transfer(
3754            &mint_keypair,
3755            &keypair.pubkey(),
3756            1,
3757            slot_entries.last().unwrap().hash,
3758        );
3759
3760        // First, ensure the TX is rejected because of the unregistered last ID
3761        assert_eq!(
3762            bank.process_transaction(&tx),
3763            Err(TransactionError::BlockhashNotFound)
3764        );
3765
3766        // Now ensure the TX is accepted despite pointing to the ID of an empty entry.
3767        process_entries_for_tests_without_scheduler(&bank, slot_entries).unwrap();
3768        assert_eq!(bank.process_transaction(&tx), Ok(()));
3769    }
3770
3771    #[test]
3772    fn test_process_ledger_simple() {
3773        agave_logger::setup();
3774        let leader_pubkey = solana_pubkey::new_rand();
3775        let mint = 100_000;
3776        let hashes_per_tick_genesis = 12;
3777        let GenesisConfigInfo {
3778            mut genesis_config,
3779            mint_keypair,
3780            ..
3781        } = create_genesis_config_with_leader(mint, &leader_pubkey, 50);
3782        genesis_config.poh_config.hashes_per_tick = Some(hashes_per_tick_genesis);
3783        let (ledger_path, mut last_entry_hash) =
3784            create_new_tmp_ledger_auto_delete!(&genesis_config);
3785        debug!("ledger_path: {ledger_path:?}");
3786
3787        let deducted_from_mint = 3;
3788        let invalid_transfer_amount = mint + 1;
3789        let mut entries = vec![];
3790        let blockhash = genesis_config.hash();
3791        for _ in 0..deducted_from_mint {
3792            // Transfer one token from the mint to a random account
3793            let keypair = Keypair::new();
3794            let tx = system_transaction::transfer(&mint_keypair, &keypair.pubkey(), 1, blockhash);
3795            let entry = next_entry_mut(&mut last_entry_hash, 1, vec![tx]);
3796            entries.push(entry);
3797
3798            // Add a second Transaction that will produce a
3799            // InstructionError<0, ResultWithNegativeLamports> error when processed
3800            let keypair2 = Keypair::new();
3801            let tx = system_transaction::transfer(
3802                &mint_keypair,
3803                &keypair2.pubkey(),
3804                invalid_transfer_amount,
3805                blockhash,
3806            );
3807            let entry = next_entry_mut(&mut last_entry_hash, 1, vec![tx]);
3808            entries.push(entry);
3809        }
3810
3811        let hashes_per_tick = genesis_config.poh_config.hashes_per_tick.unwrap_or(0);
3812        let remaining_hashes = hashes_per_tick - entries.len() as u64;
3813        let tick_entry = next_entry_mut(&mut last_entry_hash, remaining_hashes, vec![]);
3814        entries.push(tick_entry);
3815
3816        // Fill up the rest of slot 1 with ticks
3817        entries.extend(create_ticks(
3818            genesis_config.ticks_per_slot - 1,
3819            hashes_per_tick,
3820            last_entry_hash,
3821        ));
3822        let last_blockhash = entries.last().unwrap().hash;
3823
3824        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
3825        blockstore
3826            .write_entries(
3827                1,
3828                0,
3829                0,
3830                genesis_config.ticks_per_slot,
3831                None,
3832                true,
3833                &Arc::new(Keypair::new()),
3834                entries,
3835                0,
3836            )
3837            .unwrap();
3838        let opts = ProcessOptions {
3839            run_verification: true,
3840            ..ProcessOptions::default()
3841        };
3842        let (bank_forks, ..) = test_process_blockstore(&genesis_config, &blockstore, &opts);
3843        let bank_forks = bank_forks.read().unwrap();
3844
3845        assert_eq!(frozen_bank_slots(&bank_forks), vec![0, 1]);
3846        assert_eq!(bank_forks.root(), 0);
3847        assert_eq!(bank_forks.working_bank().slot(), 1);
3848
3849        let bank = bank_forks[1].clone();
3850        let tx_fee = bank.fee_structure().lamports_per_signature;
3851        assert_eq!(
3852            bank.get_balance(&mint_keypair.pubkey()),
3853            mint - deducted_from_mint - 2 * deducted_from_mint * tx_fee
3854        );
3855        assert_eq!(bank.tick_height(), 2 * genesis_config.ticks_per_slot);
3856        assert_eq!(bank.last_blockhash(), last_blockhash);
3857    }
3858
3859    #[test]
3860    fn test_process_ledger_with_one_tick_per_slot() {
3861        let GenesisConfigInfo {
3862            mut genesis_config, ..
3863        } = create_genesis_config(123);
3864        genesis_config.ticks_per_slot = 1;
3865        let (ledger_path, _blockhash) = create_new_tmp_ledger_auto_delete!(&genesis_config);
3866
3867        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
3868        let opts = ProcessOptions {
3869            run_verification: true,
3870            ..ProcessOptions::default()
3871        };
3872        let (bank_forks, ..) = test_process_blockstore(&genesis_config, &blockstore, &opts);
3873        let bank_forks = bank_forks.read().unwrap();
3874
3875        assert_eq!(frozen_bank_slots(&bank_forks), vec![0]);
3876        let bank = bank_forks[0].clone();
3877        assert_eq!(bank.tick_height(), 1);
3878    }
3879
3880    #[test]
3881    fn test_process_entries_tick() {
3882        let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(1000);
3883        let bank = Arc::new(Bank::new_for_tests(&genesis_config));
3884
3885        // ensure bank can process a tick
3886        assert_eq!(bank.tick_height(), 0);
3887        let tick = next_entry(&genesis_config.hash(), 1, vec![]);
3888        assert_eq!(
3889            process_entries_for_tests_without_scheduler(&bank, vec![tick]),
3890            Ok(())
3891        );
3892        assert_eq!(bank.tick_height(), 1);
3893    }
3894
3895    #[test]
3896    fn test_process_entries_2_entries_collision() {
3897        let GenesisConfigInfo {
3898            genesis_config,
3899            mint_keypair,
3900            ..
3901        } = create_genesis_config(1000);
3902        let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
3903        let keypair1 = Keypair::new();
3904        let keypair2 = Keypair::new();
3905
3906        let blockhash = bank.last_blockhash();
3907
3908        // ensure bank can process 2 entries that have a common account and no tick is registered
3909        let tx = system_transaction::transfer(
3910            &mint_keypair,
3911            &keypair1.pubkey(),
3912            2,
3913            bank.last_blockhash(),
3914        );
3915        let entry_1 = next_entry(&blockhash, 1, vec![tx]);
3916        let tx = system_transaction::transfer(
3917            &mint_keypair,
3918            &keypair2.pubkey(),
3919            2,
3920            bank.last_blockhash(),
3921        );
3922        let entry_2 = next_entry(&entry_1.hash, 1, vec![tx]);
3923        assert_eq!(
3924            process_entries_for_tests_without_scheduler(&bank, vec![entry_1, entry_2]),
3925            Ok(())
3926        );
3927        assert_eq!(bank.get_balance(&keypair1.pubkey()), 2);
3928        assert_eq!(bank.get_balance(&keypair2.pubkey()), 2);
3929        assert_eq!(bank.last_blockhash(), blockhash);
3930    }
3931
3932    #[test]
3933    fn test_process_entries_2_txes_collision() {
3934        let GenesisConfigInfo {
3935            genesis_config,
3936            mint_keypair,
3937            ..
3938        } = create_genesis_config(1000);
3939        let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
3940        let keypair1 = Keypair::new();
3941        let keypair2 = Keypair::new();
3942        let keypair3 = Keypair::new();
3943
3944        // fund: put 4 in each of 1 and 2
3945        assert_matches!(bank.transfer(4, &mint_keypair, &keypair1.pubkey()), Ok(_));
3946        assert_matches!(bank.transfer(4, &mint_keypair, &keypair2.pubkey()), Ok(_));
3947
3948        // construct an Entry whose 2nd transaction would cause a lock conflict with previous entry
3949        let entry_1_to_mint = next_entry(
3950            &bank.last_blockhash(),
3951            1,
3952            vec![system_transaction::transfer(
3953                &keypair1,
3954                &mint_keypair.pubkey(),
3955                1,
3956                bank.last_blockhash(),
3957            )],
3958        );
3959
3960        let entry_2_to_3_mint_to_1 = next_entry(
3961            &entry_1_to_mint.hash,
3962            1,
3963            vec![
3964                system_transaction::transfer(
3965                    &keypair2,
3966                    &keypair3.pubkey(),
3967                    2,
3968                    bank.last_blockhash(),
3969                ), // should be fine
3970                system_transaction::transfer(
3971                    &keypair1,
3972                    &mint_keypair.pubkey(),
3973                    2,
3974                    bank.last_blockhash(),
3975                ), // will collide
3976            ],
3977        );
3978
3979        assert_eq!(
3980            process_entries_for_tests_without_scheduler(
3981                &bank,
3982                vec![entry_1_to_mint, entry_2_to_3_mint_to_1],
3983            ),
3984            Ok(())
3985        );
3986
3987        assert_eq!(bank.get_balance(&keypair1.pubkey()), 1);
3988        assert_eq!(bank.get_balance(&keypair2.pubkey()), 2);
3989        assert_eq!(bank.get_balance(&keypair3.pubkey()), 2);
3990    }
3991
3992    #[test]
3993    fn test_process_entries_2_txes_collision_and_error() {
3994        let GenesisConfigInfo {
3995            genesis_config,
3996            mint_keypair,
3997            ..
3998        } = create_genesis_config(1000);
3999        let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
4000        let keypair1 = Keypair::new();
4001        let keypair2 = Keypair::new();
4002        let keypair3 = Keypair::new();
4003        let keypair4 = Keypair::new();
4004
4005        // fund: put 4 in each of 1 and 2
4006        assert_matches!(bank.transfer(4, &mint_keypair, &keypair1.pubkey()), Ok(_));
4007        assert_matches!(bank.transfer(4, &mint_keypair, &keypair2.pubkey()), Ok(_));
4008        assert_matches!(bank.transfer(4, &mint_keypair, &keypair4.pubkey()), Ok(_));
4009
4010        let good_tx = system_transaction::transfer(
4011            &keypair1,
4012            &mint_keypair.pubkey(),
4013            1,
4014            bank.last_blockhash(),
4015        );
4016
4017        // construct an Entry whose 2nd transaction would cause a lock conflict with previous entry
4018        let entry_1_to_mint = next_entry(
4019            &bank.last_blockhash(),
4020            1,
4021            vec![
4022                good_tx.clone(),
4023                system_transaction::transfer(
4024                    &keypair4,
4025                    &keypair4.pubkey(),
4026                    1,
4027                    Hash::default(), // Should cause a transaction failure with BlockhashNotFound
4028                ),
4029            ],
4030        );
4031
4032        let entry_2_to_3_mint_to_1 = next_entry(
4033            &entry_1_to_mint.hash,
4034            1,
4035            vec![
4036                system_transaction::transfer(
4037                    &keypair2,
4038                    &keypair3.pubkey(),
4039                    2,
4040                    bank.last_blockhash(),
4041                ), // should be fine
4042                system_transaction::transfer(
4043                    &keypair1,
4044                    &mint_keypair.pubkey(),
4045                    2,
4046                    bank.last_blockhash(),
4047                ), // will collide
4048            ],
4049        );
4050
4051        assert_matches!(
4052            process_entries_for_tests_without_scheduler(
4053                &bank,
4054                vec![entry_1_to_mint.clone(), entry_2_to_3_mint_to_1.clone()],
4055            ),
4056            Err(TransactionError::BlockhashNotFound)
4057        );
4058
4059        // First transaction in first entry was rolled-back, so keypair1 didn't lost 1 lamport
4060        assert_eq!(bank.get_balance(&keypair1.pubkey()), 4);
4061        assert_eq!(bank.get_balance(&keypair2.pubkey()), 4);
4062
4063        // Check all accounts are unlocked
4064        let txs1 = entry_1_to_mint.transactions;
4065        let txs2 = entry_2_to_3_mint_to_1.transactions;
4066        let batch1 = bank.prepare_entry_batch(txs1).unwrap();
4067        for result in batch1.lock_results() {
4068            assert!(result.is_ok());
4069        }
4070        // txs1 and txs2 have accounts that conflict, so we must drop txs1 first
4071        drop(batch1);
4072        let batch2 = bank.prepare_entry_batch(txs2).unwrap();
4073        for result in batch2.lock_results() {
4074            assert!(result.is_ok());
4075        }
4076        drop(batch2);
4077
4078        // ensure good_tx will succeed and was just rolled back above due to other failing tx
4079        let entry_3 = next_entry(&entry_2_to_3_mint_to_1.hash, 1, vec![good_tx]);
4080        assert_matches!(
4081            process_entries_for_tests_without_scheduler(&bank, vec![entry_3]),
4082            Ok(())
4083        );
4084        // First transaction in third entry succeeded, so keypair1 lost 1 lamport
4085        assert_eq!(bank.get_balance(&keypair1.pubkey()), 3);
4086    }
4087
4088    #[test]
4089    fn test_transaction_result_does_not_affect_bankhash() {
4090        agave_logger::setup();
4091        let GenesisConfigInfo {
4092            genesis_config,
4093            mint_keypair,
4094            ..
4095        } = create_genesis_config(1000);
4096
4097        fn get_instruction_errors() -> Vec<InstructionError> {
4098            vec![
4099                InstructionError::GenericError,
4100                InstructionError::InvalidArgument,
4101                InstructionError::InvalidInstructionData,
4102                InstructionError::InvalidAccountData,
4103                InstructionError::AccountDataTooSmall,
4104                InstructionError::InsufficientFunds,
4105                InstructionError::IncorrectProgramId,
4106                InstructionError::MissingRequiredSignature,
4107                InstructionError::AccountAlreadyInitialized,
4108                InstructionError::UninitializedAccount,
4109                InstructionError::UnbalancedInstruction,
4110                InstructionError::ModifiedProgramId,
4111                InstructionError::ExternalAccountLamportSpend,
4112                InstructionError::ExternalAccountDataModified,
4113                InstructionError::ReadonlyLamportChange,
4114                InstructionError::ReadonlyDataModified,
4115                InstructionError::DuplicateAccountIndex,
4116                InstructionError::ExecutableModified,
4117                InstructionError::RentEpochModified,
4118                #[allow(deprecated)]
4119                InstructionError::NotEnoughAccountKeys,
4120                InstructionError::AccountDataSizeChanged,
4121                InstructionError::AccountNotExecutable,
4122                InstructionError::AccountBorrowFailed,
4123                InstructionError::AccountBorrowOutstanding,
4124                InstructionError::DuplicateAccountOutOfSync,
4125                InstructionError::Custom(0),
4126                InstructionError::InvalidError,
4127                InstructionError::ExecutableDataModified,
4128                InstructionError::ExecutableLamportChange,
4129                InstructionError::ExecutableAccountNotRentExempt,
4130                InstructionError::UnsupportedProgramId,
4131                InstructionError::CallDepth,
4132                InstructionError::MissingAccount,
4133                InstructionError::ReentrancyNotAllowed,
4134                InstructionError::MaxSeedLengthExceeded,
4135                InstructionError::InvalidSeeds,
4136                InstructionError::InvalidRealloc,
4137                InstructionError::ComputationalBudgetExceeded,
4138                InstructionError::PrivilegeEscalation,
4139                InstructionError::ProgramEnvironmentSetupFailure,
4140                InstructionError::ProgramFailedToComplete,
4141                InstructionError::ProgramFailedToCompile,
4142                InstructionError::Immutable,
4143                InstructionError::IncorrectAuthority,
4144                InstructionError::BorshIoError,
4145                InstructionError::AccountNotRentExempt,
4146                InstructionError::InvalidAccountOwner,
4147                InstructionError::ArithmeticOverflow,
4148                InstructionError::UnsupportedSysvar,
4149                InstructionError::IllegalOwner,
4150                InstructionError::MaxAccountsDataAllocationsExceeded,
4151                InstructionError::MaxAccountsExceeded,
4152                InstructionError::MaxInstructionTraceLengthExceeded,
4153                InstructionError::BuiltinProgramsMustConsumeComputeUnits,
4154            ]
4155        }
4156
4157        declare_process_instruction!(MockBuiltinOk, 1, |_invoke_context| {
4158            // Always succeeds
4159            Ok(())
4160        });
4161
4162        let mock_program_id = Pubkey::new_unique();
4163
4164        let (bank, _bank_forks) = Bank::new_with_mockup_builtin_for_tests(
4165            &genesis_config,
4166            mock_program_id,
4167            MockBuiltinOk::register,
4168        );
4169
4170        let tx = Transaction::new_signed_with_payer(
4171            &[Instruction::new_with_wincode(
4172                mock_program_id,
4173                &10,
4174                Vec::new(),
4175            )],
4176            Some(&mint_keypair.pubkey()),
4177            &[&mint_keypair],
4178            bank.last_blockhash(),
4179        );
4180
4181        let entry = next_entry(&bank.last_blockhash(), 1, vec![tx]);
4182        let result = process_entries_for_tests_without_scheduler(&bank, vec![entry]);
4183        bank.freeze();
4184        let ok_bank_details = SlotDetails::new_from_bank(&bank, true).unwrap();
4185        assert!(result.is_ok());
4186
4187        declare_process_instruction!(MockBuiltinErr, 1, |invoke_context| {
4188            let instruction_errors = get_instruction_errors();
4189
4190            let instruction_context = invoke_context
4191                .transaction_context
4192                .get_current_instruction_context()
4193                .expect("Failed to get instruction context");
4194            let err = instruction_context
4195                .get_instruction_data()
4196                .first()
4197                .expect("Failed to get instruction data");
4198            Err(instruction_errors
4199                .get(*err as usize)
4200                .expect("Invalid error index")
4201                .clone())
4202        });
4203
4204        // Store details to compare against subsequent iterations
4205        let mut err_bank_details = None;
4206
4207        (0..get_instruction_errors().len()).for_each(|err| {
4208            let (bank, _bank_forks) = Bank::new_with_mockup_builtin_for_tests(
4209                &genesis_config,
4210                mock_program_id,
4211                MockBuiltinErr::register,
4212            );
4213
4214            let tx = Transaction::new_signed_with_payer(
4215                &[Instruction::new_with_wincode(
4216                    mock_program_id,
4217                    &(err as u8),
4218                    Vec::new(),
4219                )],
4220                Some(&mint_keypair.pubkey()),
4221                &[&mint_keypair],
4222                bank.last_blockhash(),
4223            );
4224
4225            let entry = next_entry(&bank.last_blockhash(), 1, vec![tx]);
4226            let bank = Arc::new(bank);
4227            let result = process_entries_for_tests_without_scheduler(&bank, vec![entry]);
4228            assert!(result.is_ok()); // No failing transaction error - only instruction errors
4229            bank.freeze();
4230            let bank_details = SlotDetails::new_from_bank(&bank, true).unwrap();
4231
4232            // Transaction success/failure should not affect block hash ...
4233            assert_eq!(
4234                ok_bank_details
4235                    .bank_hash_components
4236                    .as_ref()
4237                    .unwrap()
4238                    .last_blockhash,
4239                bank_details
4240                    .bank_hash_components
4241                    .as_ref()
4242                    .unwrap()
4243                    .last_blockhash
4244            );
4245            // Though bankhash is not affected, bank_details should be different.
4246            assert_ne!(ok_bank_details, bank_details);
4247            // Different types of transaction failure should not affect bank hash
4248            if let Some(prev_bank_details) = &err_bank_details {
4249                assert_eq!(
4250                    *prev_bank_details,
4251                    bank_details,
4252                    "bank hash mismatched for tx error: {:?}",
4253                    get_instruction_errors()[err]
4254                );
4255            } else {
4256                err_bank_details = Some(bank_details);
4257            }
4258        });
4259    }
4260
4261    #[test]
4262    fn test_process_entries_2nd_entry_collision_with_self_and_error() {
4263        agave_logger::setup();
4264
4265        let GenesisConfigInfo {
4266            genesis_config,
4267            mint_keypair,
4268            ..
4269        } = create_genesis_config(1000);
4270        let bank = Bank::new_for_tests(&genesis_config);
4271        let (bank, _bank_forks) = bank.wrap_with_bank_forks_for_tests();
4272        let keypair1 = Keypair::new();
4273        let keypair2 = Keypair::new();
4274        let keypair3 = Keypair::new();
4275
4276        // fund: put some money in each of 1 and 2
4277        assert_matches!(bank.transfer(5, &mint_keypair, &keypair1.pubkey()), Ok(_));
4278        assert_matches!(bank.transfer(4, &mint_keypair, &keypair2.pubkey()), Ok(_));
4279
4280        // 3 entries: first has a transfer, 2nd has a conflict with 1st, 3rd has a conflict with itself
4281        let entry_1_to_mint = next_entry(
4282            &bank.last_blockhash(),
4283            1,
4284            vec![system_transaction::transfer(
4285                &keypair1,
4286                &mint_keypair.pubkey(),
4287                1,
4288                bank.last_blockhash(),
4289            )],
4290        );
4291        // should now be:
4292        // keypair1=4
4293        // keypair2=4
4294        // keypair3=0
4295
4296        let entry_2_to_3_and_1_to_mint = next_entry(
4297            &entry_1_to_mint.hash,
4298            1,
4299            vec![
4300                system_transaction::transfer(
4301                    &keypair2,
4302                    &keypair3.pubkey(),
4303                    2,
4304                    bank.last_blockhash(),
4305                ), // should be fine
4306                system_transaction::transfer(
4307                    &keypair1,
4308                    &mint_keypair.pubkey(),
4309                    2,
4310                    bank.last_blockhash(),
4311                ), // will collide with preceding entry
4312            ],
4313        );
4314        // should now be:
4315        // keypair1=2
4316        // keypair2=2
4317        // keypair3=2
4318
4319        let entry_conflict_itself = next_entry(
4320            &entry_2_to_3_and_1_to_mint.hash,
4321            1,
4322            vec![
4323                system_transaction::transfer(
4324                    &keypair1,
4325                    &keypair3.pubkey(),
4326                    1,
4327                    bank.last_blockhash(),
4328                ),
4329                system_transaction::transfer(
4330                    &keypair1,
4331                    &keypair2.pubkey(),
4332                    1,
4333                    bank.last_blockhash(),
4334                ), // will collide with preceding transaction
4335            ],
4336        );
4337        // if successful, becomes:
4338        // keypair1=0
4339        // keypair2=3
4340        // keypair3=3
4341
4342        // succeeds following simd83 locking, fails otherwise
4343        let result = process_entries_for_tests_without_scheduler(
4344            &bank,
4345            vec![
4346                entry_1_to_mint,
4347                entry_2_to_3_and_1_to_mint,
4348                entry_conflict_itself,
4349            ],
4350        );
4351
4352        let balances = [
4353            bank.get_balance(&keypair1.pubkey()),
4354            bank.get_balance(&keypair2.pubkey()),
4355            bank.get_balance(&keypair3.pubkey()),
4356        ];
4357
4358        assert!(result.is_ok());
4359        assert_eq!(balances, [0, 3, 3]);
4360    }
4361
4362    #[test]
4363    fn test_process_entry_duplicate_transaction() {
4364        agave_logger::setup();
4365
4366        let GenesisConfigInfo {
4367            genesis_config,
4368            mint_keypair,
4369            ..
4370        } = create_genesis_config(1000);
4371        let bank = Bank::new_for_tests(&genesis_config);
4372        let (bank, _bank_forks) = bank.wrap_with_bank_forks_for_tests();
4373        let keypair1 = Keypair::new();
4374        let keypair2 = Keypair::new();
4375
4376        // fund: put some money in each of 1 and 2
4377        assert_matches!(bank.transfer(5, &mint_keypair, &keypair1.pubkey()), Ok(_));
4378        assert_matches!(bank.transfer(5, &mint_keypair, &keypair2.pubkey()), Ok(_));
4379
4380        // one entry, two instances of the same transaction. this entry is invalid
4381        // without simd83: due to lock conflicts
4382        // with simd83: due to message hash duplication
4383        let entry_1_to_2_twice = next_entry(
4384            &bank.last_blockhash(),
4385            1,
4386            vec![
4387                system_transaction::transfer(
4388                    &keypair1,
4389                    &keypair2.pubkey(),
4390                    1,
4391                    bank.last_blockhash(),
4392                ),
4393                system_transaction::transfer(
4394                    &keypair1,
4395                    &keypair2.pubkey(),
4396                    1,
4397                    bank.last_blockhash(),
4398                ),
4399            ],
4400        );
4401        // should now be:
4402        // keypair1=5
4403        // keypair2=5
4404
4405        // succeeds following simd83 locking, fails otherwise
4406        let result = process_entries_for_tests_without_scheduler(&bank, vec![entry_1_to_2_twice]);
4407
4408        let balances = [
4409            bank.get_balance(&keypair1.pubkey()),
4410            bank.get_balance(&keypair2.pubkey()),
4411        ];
4412
4413        assert_eq!(balances, [5, 5]);
4414        assert_eq!(result, Err(TransactionError::AlreadyProcessed));
4415    }
4416
4417    #[test]
4418    fn test_process_entries_2_entries_par() {
4419        let GenesisConfigInfo {
4420            genesis_config,
4421            mint_keypair,
4422            ..
4423        } = create_genesis_config(1000);
4424        let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
4425        let keypair1 = Keypair::new();
4426        let keypair2 = Keypair::new();
4427        let keypair3 = Keypair::new();
4428        let keypair4 = Keypair::new();
4429
4430        //load accounts
4431        let tx = system_transaction::transfer(
4432            &mint_keypair,
4433            &keypair1.pubkey(),
4434            1,
4435            bank.last_blockhash(),
4436        );
4437        assert_eq!(bank.process_transaction(&tx), Ok(()));
4438        let tx = system_transaction::transfer(
4439            &mint_keypair,
4440            &keypair2.pubkey(),
4441            1,
4442            bank.last_blockhash(),
4443        );
4444        assert_eq!(bank.process_transaction(&tx), Ok(()));
4445
4446        // ensure bank can process 2 entries that do not have a common account and no tick is registered
4447        let blockhash = bank.last_blockhash();
4448        let tx =
4449            system_transaction::transfer(&keypair1, &keypair3.pubkey(), 1, bank.last_blockhash());
4450        let entry_1 = next_entry(&blockhash, 1, vec![tx]);
4451        let tx =
4452            system_transaction::transfer(&keypair2, &keypair4.pubkey(), 1, bank.last_blockhash());
4453        let entry_2 = next_entry(&entry_1.hash, 1, vec![tx]);
4454        assert_eq!(
4455            process_entries_for_tests_without_scheduler(&bank, vec![entry_1, entry_2]),
4456            Ok(())
4457        );
4458        assert_eq!(bank.get_balance(&keypair3.pubkey()), 1);
4459        assert_eq!(bank.get_balance(&keypair4.pubkey()), 1);
4460        assert_eq!(bank.last_blockhash(), blockhash);
4461    }
4462
4463    #[test]
4464    fn test_process_entry_tx_random_execution_with_error() {
4465        let GenesisConfigInfo {
4466            genesis_config,
4467            mint_keypair,
4468            ..
4469        } = create_genesis_config(1_000_000_000);
4470        let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
4471
4472        const NUM_TRANSFERS_PER_ENTRY: usize = 8;
4473        const NUM_TRANSFERS: usize = NUM_TRANSFERS_PER_ENTRY * 32;
4474        // large enough to scramble locks and results
4475
4476        let keypairs: Vec<_> = (0..NUM_TRANSFERS * 2).map(|_| Keypair::new()).collect();
4477
4478        // give everybody one lamport
4479        for keypair in &keypairs {
4480            bank.transfer(1, &mint_keypair, &keypair.pubkey())
4481                .expect("funding failed");
4482        }
4483        let mut hash = bank.last_blockhash();
4484
4485        let present_account_key = Keypair::new();
4486        let present_account = AccountSharedData::new(1, 10, &Pubkey::default());
4487        bank.store_account(&present_account_key.pubkey(), &present_account);
4488
4489        let entries: Vec<_> = (0..NUM_TRANSFERS)
4490            .step_by(NUM_TRANSFERS_PER_ENTRY)
4491            .map(|i| {
4492                let mut transactions = (0..NUM_TRANSFERS_PER_ENTRY)
4493                    .map(|j| {
4494                        system_transaction::transfer(
4495                            &keypairs[i + j],
4496                            &keypairs[i + j + NUM_TRANSFERS].pubkey(),
4497                            1,
4498                            bank.last_blockhash(),
4499                        )
4500                    })
4501                    .collect::<Vec<_>>();
4502
4503                transactions.push(system_transaction::create_account(
4504                    &mint_keypair,
4505                    &present_account_key, // puts a TX error in results
4506                    bank.last_blockhash(),
4507                    1,
4508                    0,
4509                    &solana_pubkey::new_rand(),
4510                ));
4511
4512                next_entry_mut(&mut hash, 0, transactions)
4513            })
4514            .collect();
4515        assert_eq!(
4516            process_entries_for_tests_without_scheduler(&bank, entries),
4517            Ok(())
4518        );
4519    }
4520
4521    #[test]
4522    fn test_process_entry_tx_random_execution_no_error() {
4523        // entropy multiplier should be big enough to provide sufficient entropy
4524        // but small enough to not take too much time while executing the test.
4525        let entropy_multiplier: usize = 25;
4526        let initial_lamports = 100;
4527
4528        // number of accounts need to be in multiple of 4 for correct
4529        // execution of the test.
4530        let num_accounts = entropy_multiplier * 4;
4531        let GenesisConfigInfo {
4532            genesis_config,
4533            mint_keypair,
4534            ..
4535        } = create_genesis_config((num_accounts + 1) as u64 * initial_lamports);
4536
4537        let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
4538
4539        let mut keypairs: Vec<Keypair> = vec![];
4540
4541        for _ in 0..num_accounts {
4542            let keypair = Keypair::new();
4543            let create_account_tx = system_transaction::transfer(
4544                &mint_keypair,
4545                &keypair.pubkey(),
4546                0,
4547                bank.last_blockhash(),
4548            );
4549            assert_eq!(bank.process_transaction(&create_account_tx), Ok(()));
4550            assert_matches!(
4551                bank.transfer(initial_lamports, &mint_keypair, &keypair.pubkey()),
4552                Ok(_)
4553            );
4554            keypairs.push(keypair);
4555        }
4556
4557        let mut tx_vector: Vec<Transaction> = vec![];
4558
4559        for i in (0..num_accounts).step_by(4) {
4560            tx_vector.append(&mut vec![
4561                system_transaction::transfer(
4562                    &keypairs[i + 1],
4563                    &keypairs[i].pubkey(),
4564                    initial_lamports,
4565                    bank.last_blockhash(),
4566                ),
4567                system_transaction::transfer(
4568                    &keypairs[i + 3],
4569                    &keypairs[i + 2].pubkey(),
4570                    initial_lamports,
4571                    bank.last_blockhash(),
4572                ),
4573            ]);
4574        }
4575
4576        // Transfer lamports to each other
4577        let entry = next_entry(&bank.last_blockhash(), 1, tx_vector);
4578        assert_eq!(
4579            process_entries_for_tests_without_scheduler(&bank, vec![entry]),
4580            Ok(())
4581        );
4582        bank.squash();
4583
4584        // Even number keypair should have balance of 2 * initial_lamports and
4585        // odd number keypair should have balance of 0, which proves
4586        // that even in case of random order of execution, overall state remains
4587        // consistent.
4588        for (i, keypair) in keypairs.iter().enumerate() {
4589            if i % 2 == 0 {
4590                assert_eq!(bank.get_balance(&keypair.pubkey()), 2 * initial_lamports);
4591            } else {
4592                assert_eq!(bank.get_balance(&keypair.pubkey()), 0);
4593            }
4594        }
4595    }
4596
4597    #[test]
4598    fn test_process_entries_2_entries_tick() {
4599        let GenesisConfigInfo {
4600            genesis_config,
4601            mint_keypair,
4602            ..
4603        } = create_genesis_config(1000);
4604        let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
4605        let keypair1 = Keypair::new();
4606        let keypair2 = Keypair::new();
4607        let keypair3 = Keypair::new();
4608        let keypair4 = Keypair::new();
4609
4610        //load accounts
4611        let tx = system_transaction::transfer(
4612            &mint_keypair,
4613            &keypair1.pubkey(),
4614            1,
4615            bank.last_blockhash(),
4616        );
4617        assert_eq!(bank.process_transaction(&tx), Ok(()));
4618        let tx = system_transaction::transfer(
4619            &mint_keypair,
4620            &keypair2.pubkey(),
4621            1,
4622            bank.last_blockhash(),
4623        );
4624        assert_eq!(bank.process_transaction(&tx), Ok(()));
4625
4626        let blockhash = bank.last_blockhash();
4627        while blockhash == bank.last_blockhash() {
4628            bank.register_default_tick_for_test();
4629        }
4630
4631        // ensure bank can process 2 entries that do not have a common account and tick is registered
4632        let tx = system_transaction::transfer(&keypair2, &keypair3.pubkey(), 1, blockhash);
4633        let entry_1 = next_entry(&blockhash, 1, vec![tx]);
4634        let tick = next_entry(&entry_1.hash, 1, vec![]);
4635        let tx =
4636            system_transaction::transfer(&keypair1, &keypair4.pubkey(), 1, bank.last_blockhash());
4637        let entry_2 = next_entry(&tick.hash, 1, vec![tx]);
4638        assert_eq!(
4639            process_entries_for_tests_without_scheduler(
4640                &bank,
4641                vec![entry_1, tick, entry_2.clone()],
4642            ),
4643            Ok(())
4644        );
4645        assert_eq!(bank.get_balance(&keypair3.pubkey()), 1);
4646        assert_eq!(bank.get_balance(&keypair4.pubkey()), 1);
4647
4648        // ensure that an error is returned for an empty account (keypair2)
4649        let tx =
4650            system_transaction::transfer(&keypair2, &keypair3.pubkey(), 1, bank.last_blockhash());
4651        let entry_3 = next_entry(&entry_2.hash, 1, vec![tx]);
4652        assert_eq!(
4653            process_entries_for_tests_without_scheduler(&bank, vec![entry_3]),
4654            Err(TransactionError::AccountNotFound)
4655        );
4656    }
4657
4658    #[test]
4659    fn test_update_transaction_statuses() {
4660        let GenesisConfigInfo {
4661            genesis_config,
4662            mint_keypair,
4663            ..
4664        } = create_genesis_config(11_000);
4665        let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
4666
4667        // Make sure instruction errors still update the signature cache
4668        let pubkey = solana_pubkey::new_rand();
4669        bank.transfer(1_000, &mint_keypair, &pubkey).unwrap();
4670        assert_eq!(bank.transaction_count(), 1);
4671        assert_eq!(bank.get_balance(&pubkey), 1_000);
4672        assert_eq!(
4673            bank.transfer(10_001, &mint_keypair, &pubkey),
4674            Err(TransactionError::InstructionError(
4675                0,
4676                SystemError::ResultWithNegativeLamports.into(),
4677            ))
4678        );
4679        assert_eq!(
4680            bank.transfer(10_001, &mint_keypair, &pubkey),
4681            Err(TransactionError::AlreadyProcessed)
4682        );
4683
4684        // Make sure fees-only transactions still update the signature cache
4685        let missing_program_id = Pubkey::new_unique();
4686        let tx = Transaction::new_signed_with_payer(
4687            &[Instruction::new_with_wincode(
4688                missing_program_id,
4689                &10,
4690                Vec::new(),
4691            )],
4692            Some(&mint_keypair.pubkey()),
4693            &[&mint_keypair],
4694            bank.last_blockhash(),
4695        );
4696        // First process attempt will fail but still update status cache
4697        assert_eq!(
4698            bank.process_transaction(&tx),
4699            Err(TransactionError::ProgramAccountNotFound)
4700        );
4701        // Second attempt will be rejected since tx was already in status cache
4702        assert_eq!(
4703            bank.process_transaction(&tx),
4704            Err(TransactionError::AlreadyProcessed)
4705        );
4706
4707        // Make sure other errors don't update the signature cache
4708        let tx = system_transaction::transfer(&mint_keypair, &pubkey, 1000, Hash::default());
4709        let signature = tx.signatures[0];
4710
4711        // Should fail with blockhash not found
4712        assert_eq!(
4713            bank.process_transaction(&tx).map(|_| signature),
4714            Err(TransactionError::BlockhashNotFound)
4715        );
4716
4717        // Should fail again with blockhash not found
4718        assert_eq!(
4719            bank.process_transaction(&tx).map(|_| signature),
4720            Err(TransactionError::BlockhashNotFound)
4721        );
4722    }
4723
4724    #[test]
4725    fn test_update_transaction_statuses_fail() {
4726        let GenesisConfigInfo {
4727            genesis_config,
4728            mint_keypair,
4729            ..
4730        } = create_genesis_config(11_000);
4731        let bank = Bank::new_for_tests(&genesis_config);
4732        let (bank, _bank_forks) = bank.wrap_with_bank_forks_for_tests();
4733        let keypair1 = Keypair::new();
4734        let keypair2 = Keypair::new();
4735        let success_tx = system_transaction::transfer(
4736            &mint_keypair,
4737            &keypair1.pubkey(),
4738            1,
4739            bank.last_blockhash(),
4740        );
4741        let test_tx = system_transaction::transfer(
4742            &mint_keypair,
4743            &keypair2.pubkey(),
4744            2,
4745            bank.last_blockhash(),
4746        );
4747
4748        let entry_1_to_mint = next_entry(
4749            &bank.last_blockhash(),
4750            1,
4751            vec![
4752                success_tx,
4753                test_tx.clone(), // will collide
4754            ],
4755        );
4756
4757        assert_eq!(
4758            process_entries_for_tests_without_scheduler(&bank, vec![entry_1_to_mint]),
4759            Ok(())
4760        );
4761
4762        assert_eq!(
4763            bank.process_transaction(&test_tx),
4764            Err(TransactionError::AlreadyProcessed)
4765        );
4766    }
4767
4768    #[test]
4769    fn test_halt_at_slot_starting_snapshot_root() {
4770        let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(123);
4771
4772        // Create roots at slots 0, 1
4773        let forks = tr(0) / tr(1);
4774        let ledger_path = get_tmp_ledger_path_auto_delete!();
4775        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
4776        blockstore.add_tree(
4777            forks,
4778            false,
4779            true,
4780            genesis_config.ticks_per_slot,
4781            genesis_config.hash(),
4782        );
4783        blockstore.set_roots([0, 1].iter()).unwrap();
4784
4785        // Specify halting at slot 0
4786        let opts = ProcessOptions {
4787            run_verification: true,
4788            halt_at_slot: Some(0),
4789            ..ProcessOptions::default()
4790        };
4791        let (bank_forks, ..) = test_process_blockstore(&genesis_config, &blockstore, &opts);
4792        let bank_forks = bank_forks.read().unwrap();
4793
4794        // Should be able to fetch slot 0 because we specified halting at slot 0, even
4795        // if there is a greater root at slot 1.
4796        assert!(bank_forks.get(0).is_some());
4797    }
4798
4799    #[test]
4800    fn test_process_blockstore_from_root() {
4801        let GenesisConfigInfo {
4802            mut genesis_config, ..
4803        } = create_genesis_config(123);
4804
4805        let ticks_per_slot = 1;
4806        genesis_config.ticks_per_slot = ticks_per_slot;
4807        let (ledger_path, blockhash) = create_new_tmp_ledger_auto_delete!(&genesis_config);
4808        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
4809
4810        /*
4811          Build a blockstore in the ledger with the following fork structure:
4812
4813               slot 0 (all ticks)
4814                 |
4815               slot 1 (all ticks)
4816                 |
4817               slot 2 (all ticks)
4818                 |
4819               slot 3 (all ticks) -> root
4820                 |
4821               slot 4 (all ticks)
4822                 |
4823               slot 5 (all ticks) -> root
4824                 |
4825               slot 6 (all ticks)
4826        */
4827
4828        let mut last_hash = blockhash;
4829        for i in 0..6 {
4830            last_hash =
4831                fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, i + 1, i, last_hash);
4832        }
4833        blockstore.set_roots([3, 5].iter()).unwrap();
4834
4835        // Set up bank1
4836        let bank_forks = BankForks::new_rw_arc(Bank::new_for_tests(&genesis_config));
4837        let bank0 = bank_forks.read().unwrap().get_with_scheduler(0).unwrap();
4838        let opts = ProcessOptions {
4839            run_verification: true,
4840            ..ProcessOptions::default()
4841        };
4842        let replay_tx_thread_pool = create_thread_pool(1);
4843        process_bank_0(
4844            &bank0,
4845            compute_shred_version(&genesis_config.hash(), None),
4846            &blockstore,
4847            &replay_tx_thread_pool,
4848            &opts,
4849            None,
4850            None,
4851            &MigrationStatus::default(),
4852        )
4853        .unwrap();
4854        let bank0_last_blockhash = bank0.last_blockhash();
4855        let bank1_child =
4856            Bank::new_from_parent(bank0.clone_without_scheduler(), SlotLeader::default(), 1);
4857        let bank1 = bank_forks.write().unwrap().insert(bank1_child);
4858        confirm_full_slot(
4859            &blockstore,
4860            &bank1,
4861            compute_shred_version(&genesis_config.hash(), None),
4862            &replay_tx_thread_pool,
4863            &opts,
4864            &mut ConfirmationProgress::new(bank0_last_blockhash),
4865            None,
4866            None,
4867            None,
4868            &mut ExecuteTimings::default(),
4869            &MigrationStatus::default(),
4870        )
4871        .unwrap();
4872        bank_forks.write().unwrap().set_root(1, None, None);
4873
4874        let leader_schedule_cache = LeaderScheduleCache::new_from_bank(&bank1);
4875
4876        // Test process_blockstore_from_root() from slot 1 onwards
4877        process_blockstore_from_root(
4878            &blockstore,
4879            &bank_forks,
4880            compute_shred_version(&genesis_config.hash(), None),
4881            &leader_schedule_cache,
4882            &opts,
4883            None,
4884            None,
4885            None, // snapshot_controller
4886        )
4887        .unwrap();
4888
4889        let bank_forks = bank_forks.read().unwrap();
4890
4891        assert_eq!(frozen_bank_slots(&bank_forks), vec![5, 6]);
4892        assert_eq!(bank_forks.working_bank().slot(), 6);
4893        assert_eq!(bank_forks.root(), 5);
4894
4895        // Verify the parents of the head of the fork
4896        assert_eq!(
4897            &bank_forks[6]
4898                .parents()
4899                .iter()
4900                .map(|bank| bank.slot())
4901                .collect::<Vec<_>>(),
4902            &[5]
4903        );
4904
4905        // Check that bank forks has the correct banks
4906        verify_fork_infos(&bank_forks);
4907    }
4908
4909    #[test]
4910    #[ignore]
4911    fn test_process_entries_stress() {
4912        // this test throws lots of rayon threads at process_entries()
4913        //  finds bugs in very low-layer stuff
4914        agave_logger::setup();
4915        let GenesisConfigInfo {
4916            genesis_config,
4917            mint_keypair,
4918            ..
4919        } = create_genesis_config(1_000_000_000);
4920        let bank = Bank::new_for_tests(&genesis_config);
4921        let (mut bank, _bank_forks) = bank.wrap_with_bank_forks_for_tests();
4922
4923        const NUM_TRANSFERS_PER_ENTRY: usize = 8;
4924        const NUM_TRANSFERS: usize = NUM_TRANSFERS_PER_ENTRY * 32;
4925
4926        let keypairs: Vec<_> = (0..NUM_TRANSFERS * 2).map(|_| Keypair::new()).collect();
4927
4928        // give everybody one lamport
4929        for keypair in &keypairs {
4930            bank.transfer(1, &mint_keypair, &keypair.pubkey())
4931                .expect("funding failed");
4932        }
4933
4934        let present_account_key = Keypair::new();
4935        let present_account = AccountSharedData::new(1, 10, &Pubkey::default());
4936        bank.store_account(&present_account_key.pubkey(), &present_account);
4937
4938        let mut i = 0;
4939        let mut hash = bank.last_blockhash();
4940        let mut root: Option<Arc<Bank>> = None;
4941        loop {
4942            let entries: Vec<_> = (0..NUM_TRANSFERS)
4943                .step_by(NUM_TRANSFERS_PER_ENTRY)
4944                .map(|i| {
4945                    next_entry_mut(&mut hash, 0, {
4946                        let mut transactions = (i..i + NUM_TRANSFERS_PER_ENTRY)
4947                            .map(|i| {
4948                                system_transaction::transfer(
4949                                    &keypairs[i],
4950                                    &keypairs[i + NUM_TRANSFERS].pubkey(),
4951                                    1,
4952                                    bank.last_blockhash(),
4953                                )
4954                            })
4955                            .collect::<Vec<_>>();
4956
4957                        transactions.push(system_transaction::create_account(
4958                            &mint_keypair,
4959                            &present_account_key, // puts a TX error in results
4960                            bank.last_blockhash(),
4961                            100,
4962                            100,
4963                            &solana_pubkey::new_rand(),
4964                        ));
4965                        transactions
4966                    })
4967                })
4968                .collect();
4969            info!("paying iteration {i}");
4970            process_entries_for_tests_without_scheduler(&bank, entries).expect("paying failed");
4971
4972            let entries: Vec<_> = (0..NUM_TRANSFERS)
4973                .step_by(NUM_TRANSFERS_PER_ENTRY)
4974                .map(|i| {
4975                    next_entry_mut(
4976                        &mut hash,
4977                        0,
4978                        (i..i + NUM_TRANSFERS_PER_ENTRY)
4979                            .map(|i| {
4980                                system_transaction::transfer(
4981                                    &keypairs[i + NUM_TRANSFERS],
4982                                    &keypairs[i].pubkey(),
4983                                    1,
4984                                    bank.last_blockhash(),
4985                                )
4986                            })
4987                            .collect::<Vec<_>>(),
4988                    )
4989                })
4990                .collect();
4991
4992            info!("refunding iteration {i}");
4993            process_entries_for_tests_without_scheduler(&bank, entries).expect("refunding failed");
4994
4995            // advance to next block
4996            process_entries_for_tests_without_scheduler(
4997                &bank,
4998                (0..bank.ticks_per_slot())
4999                    .map(|_| next_entry_mut(&mut hash, 1, vec![]))
5000                    .collect::<Vec<_>>(),
5001            )
5002            .expect("process ticks failed");
5003
5004            if i % 16 == 0 {
5005                if let Some(old_root) = root {
5006                    old_root.squash();
5007                }
5008                root = Some(bank.clone());
5009            }
5010            i += 1;
5011
5012            let slot = bank.slot() + rng().random_range(1..3);
5013            bank = Arc::new(Bank::new_from_parent(bank, SlotLeader::default(), slot));
5014        }
5015    }
5016
5017    fn get_epoch_schedule(genesis_config: &GenesisConfig) -> EpochSchedule {
5018        let bank = Bank::new_for_tests(genesis_config);
5019        bank.epoch_schedule().clone()
5020    }
5021
5022    fn frozen_bank_slots(bank_forks: &BankForks) -> Vec<Slot> {
5023        let mut slots: Vec<_> = bank_forks
5024            .frozen_banks()
5025            .map(|(slot, _bank)| slot)
5026            .collect();
5027        slots.sort_unstable();
5028        slots
5029    }
5030
5031    // Check that `bank_forks` contains all the ancestors and banks for each fork identified in
5032    // `bank_forks_info`
5033    fn verify_fork_infos(bank_forks: &BankForks) {
5034        for slot in frozen_bank_slots(bank_forks) {
5035            let head_bank = &bank_forks[slot];
5036            let mut parents = head_bank.parents();
5037            parents.push(head_bank.clone());
5038
5039            // Ensure the tip of each fork and all its parents are in the given bank_forks
5040            for parent in parents {
5041                let parent_bank = &bank_forks[parent.slot()];
5042                assert_eq!(parent_bank.slot(), parent.slot());
5043                assert!(parent_bank.is_frozen());
5044            }
5045        }
5046    }
5047
5048    #[test]
5049    fn test_get_first_error() {
5050        let GenesisConfigInfo {
5051            genesis_config,
5052            mint_keypair,
5053            ..
5054        } = create_genesis_config(1_000_000_000);
5055        let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
5056
5057        let present_account_key = Keypair::new();
5058        let present_account = AccountSharedData::new(1, 10, &Pubkey::default());
5059        bank.store_account(&present_account_key.pubkey(), &present_account);
5060
5061        let keypair = Keypair::new();
5062
5063        // Create array of two transactions which throw different errors
5064        let account_not_found_tx = system_transaction::transfer(
5065            &keypair,
5066            &solana_pubkey::new_rand(),
5067            42,
5068            bank.last_blockhash(),
5069        );
5070        let account_not_found_sig = account_not_found_tx.signatures[0];
5071        let invalid_blockhash_tx = system_transaction::transfer(
5072            &mint_keypair,
5073            &solana_pubkey::new_rand(),
5074            42,
5075            Hash::default(),
5076        );
5077        let txs = vec![account_not_found_tx, invalid_blockhash_tx];
5078        let batch = bank.prepare_batch_for_tests(txs);
5079        let (commit_results, _) = batch.bank().load_execute_and_commit_transactions(
5080            &batch,
5081            ExecutionRecordingConfig::new_single_setting(false),
5082            &mut ExecuteTimings::default(),
5083            None,
5084        );
5085        let (err, signature) = do_get_first_error(&batch, &commit_results).unwrap();
5086        assert_eq!(err.unwrap_err(), TransactionError::AccountNotFound);
5087        assert_eq!(signature, account_not_found_sig);
5088    }
5089
5090    #[test]
5091    fn test_replay_vote_sender() {
5092        let validator_keypairs: Vec<_> =
5093            (0..10).map(|_| ValidatorVoteKeypairs::new_rand()).collect();
5094        let GenesisConfigInfo { genesis_config, .. } = create_genesis_config_with_vote_accounts(
5095            1_000_000_000,
5096            &validator_keypairs,
5097            vec![100; validator_keypairs.len()],
5098        );
5099        let (bank0, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
5100        bank0.freeze();
5101
5102        let bank1_child = Bank::new_from_parent(bank0.clone(), SlotLeader::new_unique(), 1);
5103        let bank1 = bank_forks
5104            .write()
5105            .unwrap()
5106            .insert(bank1_child)
5107            .clone_without_scheduler();
5108
5109        // The new blockhash is going to be the hash of the last tick in the block
5110        let bank_1_blockhash = bank1.last_blockhash();
5111
5112        // Create an transaction that references the new blockhash, should still
5113        // be able to find the blockhash if we process transactions all in the same
5114        // batch
5115        let mut expected_successful_voter_pubkeys = BTreeSet::new();
5116        let vote_txs: Vec<_> = validator_keypairs
5117            .iter()
5118            .enumerate()
5119            .map(|(i, validator_keypairs)| {
5120                let tower_sync = TowerSync::new_from_slots(vec![0], bank0.hash(), None);
5121                if i % 3 == 0 {
5122                    // These votes are correct
5123                    expected_successful_voter_pubkeys
5124                        .insert(validator_keypairs.vote_keypair.pubkey());
5125                    vote_transaction::new_tower_sync_transaction(
5126                        tower_sync,
5127                        bank_1_blockhash,
5128                        &validator_keypairs.node_keypair,
5129                        &validator_keypairs.vote_keypair,
5130                        &validator_keypairs.vote_keypair,
5131                        None,
5132                    )
5133                } else if i % 3 == 1 {
5134                    // These have the wrong authorized voter
5135                    vote_transaction::new_tower_sync_transaction(
5136                        tower_sync,
5137                        bank_1_blockhash,
5138                        &validator_keypairs.node_keypair,
5139                        &validator_keypairs.vote_keypair,
5140                        &Keypair::new(),
5141                        None,
5142                    )
5143                } else {
5144                    // These have an invalid vote for non-existent bank 2
5145                    vote_transaction::new_tower_sync_transaction(
5146                        TowerSync::from(vec![(bank1.slot() + 1, 1)]),
5147                        bank_1_blockhash,
5148                        &validator_keypairs.node_keypair,
5149                        &validator_keypairs.vote_keypair,
5150                        &validator_keypairs.vote_keypair,
5151                        None,
5152                    )
5153                }
5154            })
5155            .collect();
5156        let entry = next_entry(&bank_1_blockhash, 1, vote_txs);
5157        let (replay_vote_sender, replay_vote_receiver) = bounded(1024);
5158        let _ = process_entries_for_tests(
5159            &BankWithScheduler::new_without_scheduler(bank1),
5160            vec![entry],
5161            None,
5162            Some(&replay_vote_sender),
5163        );
5164        let successes: BTreeSet<Pubkey> = replay_vote_receiver
5165            .try_iter()
5166            .filter_map(|replay_vote| match replay_vote {
5167                ReplayVoteMessage::VerifiedExecuted((vote_pubkey, ..))
5168                | ReplayVoteMessage::Executed {
5169                    parsed_vote: (vote_pubkey, ..),
5170                    ..
5171                } => Some(vote_pubkey),
5172                ReplayVoteMessage::Verified { .. }
5173                | ReplayVoteMessage::InvalidBank { .. }
5174                | ReplayVoteMessage::BankComplete { .. } => None,
5175            })
5176            .collect();
5177        assert_eq!(successes, expected_successful_voter_pubkeys);
5178    }
5179
5180    fn make_slot_with_vote_tx(
5181        blockstore: &Blockstore,
5182        ticks_per_slot: u64,
5183        tx_landed_slot: Slot,
5184        parent_slot: Slot,
5185        parent_blockhash: &Hash,
5186        vote_tx: Transaction,
5187        slot_leader_keypair: &Arc<Keypair>,
5188    ) {
5189        // Add votes to `last_slot` so that `root` will be confirmed
5190        let vote_entry = next_entry(parent_blockhash, 1, vec![vote_tx]);
5191        let mut entries = create_ticks(ticks_per_slot, 0, vote_entry.hash);
5192        entries.insert(0, vote_entry);
5193        blockstore
5194            .write_entries(
5195                tx_landed_slot,
5196                0,
5197                0,
5198                ticks_per_slot,
5199                Some(parent_slot),
5200                true,
5201                slot_leader_keypair,
5202                entries,
5203                0,
5204            )
5205            .unwrap();
5206    }
5207
5208    fn run_test_process_blockstore_with_supermajority_root(
5209        blockstore_root: Option<Slot>,
5210        blockstore_access_type: AccessType,
5211    ) {
5212        agave_logger::setup();
5213        /*
5214            Build fork structure:
5215                 slot 0
5216                   |
5217                 slot 1 <- (blockstore root)
5218                 /    \
5219            slot 2    |
5220               |      |
5221            slot 4    |
5222                    slot 5
5223                      |
5224                `expected_root_slot`
5225                     /    \
5226                  ...    minor fork
5227                  /
5228            `last_slot`
5229                 |
5230            `really_last_slot`
5231        */
5232        let starting_fork_slot = 5;
5233        let mut main_fork = tr(starting_fork_slot);
5234        let mut main_fork_ref = main_fork.root_mut().get_mut();
5235
5236        // Make enough slots to make a root slot > blockstore_root
5237        let expected_root_slot = starting_fork_slot + blockstore_root.unwrap_or(0);
5238        let really_expected_root_slot = expected_root_slot + 1;
5239        let last_main_fork_slot = expected_root_slot + MAX_LOCKOUT_HISTORY as u64 + 1;
5240        let really_last_main_fork_slot = last_main_fork_slot + 1;
5241
5242        // Make `minor_fork`
5243        let last_minor_fork_slot = really_last_main_fork_slot + 1;
5244        let minor_fork = tr(last_minor_fork_slot);
5245
5246        // Make 'main_fork`
5247        for slot in starting_fork_slot + 1..last_main_fork_slot {
5248            if slot - 1 == expected_root_slot {
5249                main_fork_ref.push_front(minor_fork.clone());
5250            }
5251            main_fork_ref.push_front(tr(slot));
5252            main_fork_ref = main_fork_ref.front_mut().unwrap().get_mut();
5253        }
5254        let forks = tr(0) / (tr(1) / (tr(2) / (tr(4))) / main_fork);
5255        let validator_keypairs = ValidatorVoteKeypairs::new_rand();
5256        let GenesisConfigInfo { genesis_config, .. } =
5257            genesis_utils::create_genesis_config_with_vote_accounts(
5258                10_000,
5259                &[&validator_keypairs],
5260                vec![100],
5261            );
5262        let ticks_per_slot = genesis_config.ticks_per_slot();
5263        let ledger_path = get_tmp_ledger_path_auto_delete!();
5264        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
5265        blockstore.add_tree(forks, false, true, ticks_per_slot, genesis_config.hash());
5266
5267        if let Some(blockstore_root) = blockstore_root {
5268            blockstore
5269                .set_roots(std::iter::once(&blockstore_root))
5270                .unwrap();
5271        }
5272
5273        let opts = ProcessOptions {
5274            run_verification: true,
5275            ..ProcessOptions::default()
5276        };
5277
5278        let (bank_forks, ..) = test_process_blockstore_with_custom_options(
5279            &genesis_config,
5280            &blockstore,
5281            &opts,
5282            blockstore_access_type.clone(),
5283        );
5284        let bank_forks = bank_forks.read().unwrap();
5285
5286        // prepare to add votes
5287        let last_vote_bank_hash = bank_forks.get(last_main_fork_slot - 1).unwrap().hash();
5288        let last_vote_blockhash = bank_forks
5289            .get(last_main_fork_slot - 1)
5290            .unwrap()
5291            .last_blockhash();
5292        let tower_sync = TowerSync::new_from_slot(last_main_fork_slot - 1, last_vote_bank_hash);
5293        let vote_tx = vote_transaction::new_tower_sync_transaction(
5294            tower_sync,
5295            last_vote_blockhash,
5296            &validator_keypairs.node_keypair,
5297            &validator_keypairs.vote_keypair,
5298            &validator_keypairs.vote_keypair,
5299            None,
5300        );
5301
5302        // Add votes to `last_slot` so that `root` will be confirmed
5303        let leader_keypair = Arc::new(validator_keypairs.node_keypair);
5304        make_slot_with_vote_tx(
5305            &blockstore,
5306            ticks_per_slot,
5307            last_main_fork_slot,
5308            last_main_fork_slot - 1,
5309            &last_vote_blockhash,
5310            vote_tx,
5311            &leader_keypair,
5312        );
5313
5314        let (bank_forks, ..) = test_process_blockstore_with_custom_options(
5315            &genesis_config,
5316            &blockstore,
5317            &opts,
5318            blockstore_access_type.clone(),
5319        );
5320        let bank_forks = bank_forks.read().unwrap();
5321
5322        assert_eq!(bank_forks.root(), expected_root_slot);
5323        assert_eq!(
5324            bank_forks.frozen_banks().count() as u64,
5325            last_minor_fork_slot - really_expected_root_slot + 1
5326        );
5327
5328        // Minor fork at `last_main_fork_slot + 1` was above the `expected_root_slot`
5329        // so should not have been purged
5330        //
5331        // Fork at slot 2 was purged because it was below the `expected_root_slot`
5332        for slot in 0..=last_minor_fork_slot {
5333            // this slot will be created below
5334            if slot == really_last_main_fork_slot {
5335                continue;
5336            }
5337            if slot >= expected_root_slot {
5338                let bank = bank_forks.get(slot).unwrap();
5339                assert_eq!(bank.slot(), slot);
5340                assert!(bank.is_frozen());
5341            } else {
5342                assert!(bank_forks.get(slot).is_none());
5343            }
5344        }
5345
5346        // really prepare to add votes
5347        let last_vote_bank_hash = bank_forks.get(last_main_fork_slot).unwrap().hash();
5348        let last_vote_blockhash = bank_forks
5349            .get(last_main_fork_slot)
5350            .unwrap()
5351            .last_blockhash();
5352        let tower_sync = TowerSync::new_from_slot(last_main_fork_slot, last_vote_bank_hash);
5353        let vote_tx = vote_transaction::new_tower_sync_transaction(
5354            tower_sync,
5355            last_vote_blockhash,
5356            &leader_keypair,
5357            &validator_keypairs.vote_keypair,
5358            &validator_keypairs.vote_keypair,
5359            None,
5360        );
5361
5362        // Add votes to `really_last_slot` so that `root` will be confirmed again
5363        make_slot_with_vote_tx(
5364            &blockstore,
5365            ticks_per_slot,
5366            really_last_main_fork_slot,
5367            last_main_fork_slot,
5368            &last_vote_blockhash,
5369            vote_tx,
5370            &leader_keypair,
5371        );
5372
5373        let (bank_forks, ..) = test_process_blockstore_with_custom_options(
5374            &genesis_config,
5375            &blockstore,
5376            &opts,
5377            blockstore_access_type,
5378        );
5379        let bank_forks = bank_forks.read().unwrap();
5380
5381        assert_eq!(bank_forks.root(), really_expected_root_slot);
5382    }
5383
5384    #[test]
5385    fn test_process_blockstore_with_supermajority_root_without_blockstore_root() {
5386        run_test_process_blockstore_with_supermajority_root(None, AccessType::Primary);
5387    }
5388
5389    #[test]
5390    fn test_process_blockstore_with_supermajority_root_without_blockstore_root_readonly_access() {
5391        run_test_process_blockstore_with_supermajority_root(None, AccessType::ReadOnly);
5392    }
5393
5394    #[test]
5395    fn test_process_blockstore_with_supermajority_root_with_blockstore_root() {
5396        run_test_process_blockstore_with_supermajority_root(Some(1), AccessType::Primary)
5397    }
5398
5399    #[test]
5400    #[allow(clippy::field_reassign_with_default)]
5401    fn test_supermajority_root_from_vote_accounts() {
5402        let convert_to_vote_accounts = |roots_stakes: Vec<(Slot, u64)>| -> VoteAccountsHashMap {
5403            roots_stakes
5404                .into_iter()
5405                .map(|(root, stake)| {
5406                    let mut vote_state = VoteStateV4::default();
5407                    vote_state.root_slot = Some(root);
5408                    let mut vote_account = AccountSharedData::new(
5409                        1,
5410                        VoteStateV4::size_of(),
5411                        &solana_vote_program::id(),
5412                    );
5413                    let versioned = VoteStateVersions::new_v4(vote_state);
5414                    VoteStateV4::serialize(&versioned, vote_account.data_as_mut_slice()).unwrap();
5415                    (
5416                        solana_pubkey::new_rand(),
5417                        (stake, VoteAccount::try_from(vote_account).unwrap()),
5418                    )
5419                })
5420                .collect()
5421        };
5422
5423        let total_stake = 10;
5424
5425        // Supermajority root should be None
5426        assert!(supermajority_root_from_vote_accounts(total_stake, &HashMap::default()).is_none());
5427
5428        // Supermajority root should be None
5429        let roots_stakes = vec![(8, 1), (3, 1), (4, 1), (8, 1)];
5430        let accounts = convert_to_vote_accounts(roots_stakes);
5431        assert!(supermajority_root_from_vote_accounts(total_stake, &accounts).is_none());
5432
5433        // Supermajority root should be 4, has 7/10 of the stake
5434        let roots_stakes = vec![(8, 1), (3, 1), (4, 1), (8, 5)];
5435        let accounts = convert_to_vote_accounts(roots_stakes);
5436        assert_eq!(
5437            supermajority_root_from_vote_accounts(total_stake, &accounts).unwrap(),
5438            4
5439        );
5440
5441        // Supermajority root should be 8, it has 7/10 of the stake
5442        let roots_stakes = vec![(8, 1), (3, 1), (4, 1), (8, 6)];
5443        let accounts = convert_to_vote_accounts(roots_stakes);
5444        assert_eq!(
5445            supermajority_root_from_vote_accounts(total_stake, &accounts).unwrap(),
5446            8
5447        );
5448    }
5449
5450    fn confirm_slot_entries_for_tests(
5451        bank: &Arc<Bank>,
5452        slot_entries: Vec<Entry>,
5453        slot_full: bool,
5454        prev_entry_hash: Hash,
5455    ) -> result::Result<(), BlockstoreProcessorError> {
5456        let replay_tx_thread_pool = create_thread_pool(1);
5457        let mut progress = ConfirmationProgress::new(prev_entry_hash);
5458        confirm_slot_entries(
5459            &BankWithScheduler::new_without_scheduler(bank.clone()),
5460            &replay_tx_thread_pool,
5461            (slot_entries, 0, slot_full),
5462            &mut ConfirmationTiming::default(),
5463            &mut progress,
5464            false,
5465            None,
5466            None,
5467            None,
5468            None,
5469            None,
5470            &MigrationStatus::default(),
5471        )?;
5472        progress.wait_for_all_verification_results(&mut 0, &mut 0)
5473    }
5474
5475    fn create_test_transactions(
5476        mint_keypair: &Keypair,
5477        genesis_hash: &Hash,
5478    ) -> Vec<RuntimeTransaction<SanitizedTransaction>> {
5479        let pubkey = solana_pubkey::new_rand();
5480        let keypair2 = Keypair::new();
5481        let pubkey2 = solana_pubkey::new_rand();
5482        let keypair3 = Keypair::new();
5483        let pubkey3 = solana_pubkey::new_rand();
5484
5485        vec![
5486            RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer(
5487                mint_keypair,
5488                &pubkey,
5489                1,
5490                *genesis_hash,
5491            )),
5492            RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer(
5493                &keypair2,
5494                &pubkey2,
5495                1,
5496                *genesis_hash,
5497            )),
5498            RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer(
5499                &keypair3,
5500                &pubkey3,
5501                1,
5502                *genesis_hash,
5503            )),
5504        ]
5505    }
5506
5507    #[test]
5508    fn test_confirm_slot_entries_progress_num_txs_indexes() {
5509        let GenesisConfigInfo {
5510            genesis_config,
5511            mint_keypair,
5512            ..
5513        } = create_genesis_config(100 * LAMPORTS_PER_SOL);
5514        let genesis_hash = genesis_config.hash();
5515        let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
5516        let bank = BankWithScheduler::new_without_scheduler(bank);
5517        let replay_tx_thread_pool = create_thread_pool(1);
5518        let mut timing = ConfirmationTiming::default();
5519        let mut progress = ConfirmationProgress::new(genesis_hash);
5520        let amount = genesis_config.rent.minimum_balance(0);
5521        let keypair1 = Keypair::new();
5522        let keypair2 = Keypair::new();
5523        let keypair3 = Keypair::new();
5524        let keypair4 = Keypair::new();
5525        bank.transfer(LAMPORTS_PER_SOL, &mint_keypair, &keypair1.pubkey())
5526            .unwrap();
5527        bank.transfer(LAMPORTS_PER_SOL, &mint_keypair, &keypair2.pubkey())
5528            .unwrap();
5529
5530        let (transaction_status_sender, transaction_status_receiver) = bounded(1024);
5531        let transaction_status_sender = TransactionStatusSender {
5532            sender: transaction_status_sender,
5533            dependency_tracker: None,
5534        };
5535
5536        let blockhash = bank.last_blockhash();
5537        let tx1 = system_transaction::transfer(
5538            &keypair1,
5539            &keypair3.pubkey(),
5540            amount,
5541            bank.last_blockhash(),
5542        );
5543        let tx2 = system_transaction::transfer(
5544            &keypair2,
5545            &keypair4.pubkey(),
5546            amount,
5547            bank.last_blockhash(),
5548        );
5549        let entry = next_entry(&blockhash, 1, vec![tx1, tx2]);
5550        let new_hash = entry.hash;
5551
5552        confirm_slot_entries(
5553            &bank,
5554            &replay_tx_thread_pool,
5555            (vec![entry], 0, false),
5556            &mut timing,
5557            &mut progress,
5558            false,
5559            Some(&transaction_status_sender),
5560            None,
5561            None,
5562            None,
5563            None,
5564            &MigrationStatus::default(),
5565        )
5566        .unwrap();
5567        progress
5568            .wait_for_all_verification_results(&mut 0, &mut 0)
5569            .unwrap();
5570        assert_eq!(progress.num_txs, 2);
5571        let batch = transaction_status_receiver.recv().unwrap();
5572        if let TransactionStatusMessage::Batch((batch, _sequence)) = batch {
5573            assert_eq!(batch.transactions.len(), 2);
5574            assert_eq!(batch.transaction_indexes.len(), 2);
5575            assert_eq!(batch.transaction_indexes, [0, 1]);
5576        } else {
5577            panic!("batch should have been sent");
5578        }
5579
5580        let tx1 = system_transaction::transfer(
5581            &keypair1,
5582            &keypair3.pubkey(),
5583            amount + 1,
5584            bank.last_blockhash(),
5585        );
5586        let tx2 = system_transaction::transfer(
5587            &keypair2,
5588            &keypair4.pubkey(),
5589            amount + 1,
5590            bank.last_blockhash(),
5591        );
5592        let tx3 = system_transaction::transfer(
5593            &mint_keypair,
5594            &Pubkey::new_unique(),
5595            amount,
5596            bank.last_blockhash(),
5597        );
5598        let entry = next_entry(&new_hash, 1, vec![tx1, tx2, tx3]);
5599
5600        confirm_slot_entries(
5601            &bank,
5602            &replay_tx_thread_pool,
5603            (vec![entry], 0, false),
5604            &mut timing,
5605            &mut progress,
5606            false,
5607            Some(&transaction_status_sender),
5608            None,
5609            None,
5610            None,
5611            None,
5612            &MigrationStatus::default(),
5613        )
5614        .unwrap();
5615        progress
5616            .wait_for_all_verification_results(&mut 0, &mut 0)
5617            .unwrap();
5618        assert_eq!(progress.num_txs, 5);
5619        let batch = transaction_status_receiver.recv().unwrap();
5620        if let TransactionStatusMessage::Batch((batch, _sequnce)) = batch {
5621            assert_eq!(batch.transactions.len(), 3);
5622            assert_eq!(batch.transaction_indexes.len(), 3);
5623            assert_eq!(batch.transaction_indexes, [2, 3, 4]);
5624        } else {
5625            panic!("batch should have been sent");
5626        }
5627    }
5628
5629    #[test]
5630    fn test_confirm_slot_entries_async_sigverify_fail() {
5631        let GenesisConfigInfo {
5632            genesis_config,
5633            mint_keypair,
5634            ..
5635        } = create_genesis_config(100 * LAMPORTS_PER_SOL);
5636        let genesis_hash = genesis_config.hash();
5637        let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
5638
5639        let mut tx =
5640            system_transaction::transfer(&mint_keypair, &Pubkey::new_unique(), 1, genesis_hash);
5641        tx.signatures[0] = solana_signature::Signature::default();
5642        let entry = Entry::new(&genesis_hash, 1, vec![tx]);
5643
5644        assert_matches!(
5645            confirm_slot_entries_for_tests(&bank, vec![entry], false, genesis_hash),
5646            Err(BlockstoreProcessorError::InvalidTransaction(
5647                TransactionError::SignatureFailure
5648            ))
5649        );
5650    }
5651
5652    #[test]
5653    fn test_async_verification_progress_drop() {
5654        let exit_barrier = Arc::new(Barrier::new(2));
5655        let drop_barrier = Arc::new(Barrier::new(2));
5656
5657        let pool = ThreadPoolBuilder::new()
5658            .num_threads(1)
5659            .exit_handler({
5660                let exit_barrier = exit_barrier.clone();
5661                move |_| {
5662                    exit_barrier.wait();
5663                }
5664            })
5665            .build()
5666            .unwrap();
5667
5668        let mut progress = AsyncVerificationProgress::new();
5669        progress
5670            .spawn(&pool, &mut 0, &mut 0, {
5671                let drop_barrier = drop_barrier.clone();
5672                move || {
5673                    // wait for the test to drop `progress` so the channel spawn() sends results to
5674                    // gets disconnected
5675                    drop_barrier.wait();
5676                    AsyncVerificationResult {
5677                        poh_verify_elapsed: 0,
5678                        transaction_verify_elapsed: 0,
5679                        error: None,
5680                    }
5681                }
5682            })
5683            .unwrap();
5684
5685        // ensure that in flight or pending tasks don't panic if AsyncVerificationProgress gets
5686        // dropped
5687        drop(progress);
5688        drop_barrier.wait();
5689        drop(pool);
5690        exit_barrier.wait();
5691    }
5692
5693    fn do_test_schedule_batches_for_execution(should_succeed: bool) {
5694        agave_logger::setup();
5695        let dummy_leader_pubkey = solana_pubkey::new_rand();
5696        let GenesisConfigInfo {
5697            genesis_config,
5698            mint_keypair,
5699            ..
5700        } = create_genesis_config_with_leader(500, &dummy_leader_pubkey, 100);
5701        let bank = Arc::new(Bank::new_for_tests(&genesis_config));
5702        let context = SchedulingContext::new(bank.clone());
5703
5704        let txs = create_test_transactions(&mint_keypair, &genesis_config.hash());
5705
5706        let mut mocked_scheduler = MockInstalledScheduler::new();
5707        let seq = Arc::new(Mutex::new(mockall::Sequence::new()));
5708        let seq_cloned = seq.clone();
5709        mocked_scheduler
5710            .expect_context()
5711            .times(1)
5712            .in_sequence(&mut seq.lock().unwrap())
5713            .return_const(context);
5714        if should_succeed {
5715            mocked_scheduler
5716                .expect_schedule_execution()
5717                .times(txs.len())
5718                .returning(|_, _| Ok(()));
5719        } else {
5720            // mocked_scheduler isn't async; so short-circuiting behavior is quite visible in that
5721            // .times(1) is called instead of .times(txs.len()), not like the succeeding case
5722            mocked_scheduler
5723                .expect_schedule_execution()
5724                .times(1)
5725                .returning(|_, _| Err(SchedulerAborted));
5726            mocked_scheduler
5727                .expect_recover_error_after_abort()
5728                .times(1)
5729                .returning(|| TransactionError::InsufficientFundsForFee);
5730        }
5731        mocked_scheduler
5732            .expect_wait_for_termination()
5733            .with(mockall::predicate::eq(true))
5734            .times(1)
5735            .in_sequence(&mut seq.lock().unwrap())
5736            .returning(move |_| {
5737                let mut mocked_uninstalled_scheduler = MockUninstalledScheduler::new();
5738                mocked_uninstalled_scheduler
5739                    .expect_return_to_pool()
5740                    .times(1)
5741                    .in_sequence(&mut seq_cloned.lock().unwrap())
5742                    .returning(|| ());
5743                (
5744                    (Ok(()), ExecuteTimings::default()),
5745                    Box::new(mocked_uninstalled_scheduler),
5746                )
5747            });
5748        let bank = BankWithScheduler::new(bank, Some(Box::new(mocked_scheduler)));
5749
5750        let locked_entry = LockedTransactionsWithIndexes {
5751            lock_results: bank.try_lock_accounts(&txs),
5752            transactions: txs,
5753            starting_index: 0,
5754        };
5755
5756        let replay_tx_thread_pool = create_thread_pool(1);
5757        let mut batch_execution_timing = BatchExecutionTiming::default();
5758        let result = process_batches(
5759            &bank,
5760            &replay_tx_thread_pool,
5761            [locked_entry].into_iter(),
5762            None,
5763            None,
5764            &mut batch_execution_timing,
5765            None,
5766            None,
5767        );
5768        if should_succeed {
5769            assert_matches!(result, Ok(()));
5770        } else {
5771            assert_matches!(result, Err(TransactionError::InsufficientFundsForFee));
5772        }
5773    }
5774
5775    #[test]
5776    fn test_schedule_batches_for_execution_success() {
5777        do_test_schedule_batches_for_execution(true);
5778    }
5779
5780    #[test]
5781    fn test_schedule_batches_for_execution_failure() {
5782        do_test_schedule_batches_for_execution(false);
5783    }
5784
5785    enum TxResult {
5786        ExecutedWithSuccess,
5787        ExecutedWithFailure,
5788        NotExecuted,
5789    }
5790
5791    #[test_matrix(
5792        [TxResult::ExecutedWithSuccess, TxResult::ExecutedWithFailure, TxResult::NotExecuted],
5793        [Ok(None), Ok(Some(4)), Err(TransactionError::CommitCancelled)]
5794    )]
5795    fn test_execute_batch_pre_commit_callback(
5796        tx_result: TxResult,
5797        poh_result: Result<Option<usize>>,
5798    ) {
5799        agave_logger::setup();
5800        let dummy_leader_pubkey = solana_pubkey::new_rand();
5801        let GenesisConfigInfo {
5802            genesis_config,
5803            mint_keypair,
5804            ..
5805        } = create_genesis_config_with_leader(500, &dummy_leader_pubkey, 100);
5806        let bank = Bank::new_for_tests(&genesis_config);
5807        let (bank, _bank_forks) = bank.wrap_with_bank_forks_for_tests();
5808        let bank = Arc::new(bank);
5809        let pubkey = solana_pubkey::new_rand();
5810        let (tx, expected_tx_result) = match tx_result {
5811            TxResult::ExecutedWithSuccess => (
5812                RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer(
5813                    &mint_keypair,
5814                    &pubkey,
5815                    1,
5816                    genesis_config.hash(),
5817                )),
5818                Ok(()),
5819            ),
5820            TxResult::ExecutedWithFailure => (
5821                RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer(
5822                    &mint_keypair,
5823                    &pubkey,
5824                    100000000,
5825                    genesis_config.hash(),
5826                )),
5827                Ok(()),
5828            ),
5829            TxResult::NotExecuted => (
5830                RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer(
5831                    &mint_keypair,
5832                    &pubkey,
5833                    1,
5834                    Hash::default(),
5835                )),
5836                Err(TransactionError::BlockhashNotFound),
5837            ),
5838        };
5839        let mut batch = TransactionBatch::new(
5840            vec![Ok(()); 1],
5841            &bank,
5842            OwnedOrBorrowed::Borrowed(slice::from_ref(&tx)),
5843        );
5844        batch.set_needs_unlock(false);
5845        let poh_with_index = matches!(&poh_result, Ok(Some(_)));
5846        let batch = TransactionBatchWithIndexes {
5847            batch,
5848            transaction_indexes: vec![],
5849        };
5850        let mut timing = ExecuteTimings::default();
5851        let (sender, receiver) = bounded(1024);
5852
5853        assert_eq!(bank.transaction_count(), 0);
5854        assert_eq!(bank.transaction_error_count(), 0);
5855        let should_commit = poh_result.is_ok();
5856        let mut is_called = false;
5857        let result = execute_batch(
5858            &batch,
5859            &bank,
5860            Some(&TransactionStatusSender {
5861                sender,
5862                dependency_tracker: None,
5863            }),
5864            None,
5865            ReplayVoteSendType::VerifiedExecuted,
5866            &mut timing,
5867            None,
5868            None,
5869            Some(|processing_result: &'_ Result<_>| {
5870                is_called = true;
5871                let ok = poh_result?;
5872                if let Err(error) = processing_result {
5873                    Err(error.clone())?;
5874                };
5875                Ok(ok)
5876            }),
5877        );
5878
5879        // pre_commit_callback() should always be called regardless of tx_result
5880        assert!(is_called);
5881
5882        if should_commit {
5883            assert_eq!(result, expected_tx_result);
5884            if expected_tx_result.is_ok() {
5885                assert_eq!(bank.transaction_count(), 1);
5886                if matches!(tx_result, TxResult::ExecutedWithFailure) {
5887                    assert_eq!(bank.transaction_error_count(), 1);
5888                } else {
5889                    assert_eq!(bank.transaction_error_count(), 0);
5890                }
5891            } else {
5892                assert_eq!(bank.transaction_count(), 0);
5893            }
5894        } else {
5895            assert_matches!(result, Err(TransactionError::CommitCancelled));
5896            assert_eq!(bank.transaction_count(), 0);
5897        }
5898        if poh_with_index && expected_tx_result.is_ok() {
5899            assert_matches!(
5900                receiver.try_recv(),
5901                Ok(TransactionStatusMessage::Batch((TransactionStatusBatch{transaction_indexes, ..}, _sequence)))
5902                    if transaction_indexes == vec![4_usize]
5903            );
5904        } else if should_commit && expected_tx_result.is_ok() {
5905            assert_matches!(
5906                receiver.try_recv(),
5907                Ok(TransactionStatusMessage::Batch((TransactionStatusBatch{transaction_indexes, ..}, _sequence)))
5908                    if transaction_indexes.is_empty()
5909            );
5910        } else {
5911            assert_matches!(receiver.try_recv(), Err(_));
5912        }
5913    }
5914
5915    #[test]
5916    fn test_confirm_slot_entries_with_fix() {
5917        const HASHES_PER_TICK: u64 = 10;
5918        const TICKS_PER_SLOT: u64 = 2;
5919
5920        let leader = SlotLeader::new_unique();
5921
5922        let GenesisConfigInfo {
5923            mut genesis_config,
5924            mint_keypair,
5925            ..
5926        } = create_genesis_config(10_000);
5927        genesis_config.poh_config.hashes_per_tick = Some(HASHES_PER_TICK);
5928        genesis_config.ticks_per_slot = TICKS_PER_SLOT;
5929        let genesis_hash = genesis_config.hash();
5930
5931        let (slot_0_bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
5932        let hashes_per_tick = slot_0_bank.hashes_per_tick().unwrap();
5933        assert_eq!(slot_0_bank.slot(), 0);
5934        assert_eq!(slot_0_bank.tick_height(), 0);
5935        assert_eq!(slot_0_bank.max_tick_height(), 2);
5936        assert_eq!(slot_0_bank.last_blockhash(), genesis_hash);
5937        assert_eq!(slot_0_bank.get_hash_age(&genesis_hash), Some(0));
5938
5939        let slot_0_entries = entry::create_ticks(TICKS_PER_SLOT, hashes_per_tick, genesis_hash);
5940        let slot_0_hash = slot_0_entries.last().unwrap().hash;
5941        confirm_slot_entries_for_tests(&slot_0_bank, slot_0_entries, true, genesis_hash).unwrap();
5942        assert_eq!(slot_0_bank.tick_height(), slot_0_bank.max_tick_height());
5943        assert_eq!(slot_0_bank.last_blockhash(), slot_0_hash);
5944        assert_eq!(slot_0_bank.get_hash_age(&genesis_hash), Some(1));
5945        assert_eq!(slot_0_bank.get_hash_age(&slot_0_hash), Some(0));
5946
5947        let new_bank = Bank::new_from_parent(slot_0_bank, leader, 2);
5948        let slot_2_bank = bank_forks
5949            .write()
5950            .unwrap()
5951            .insert(new_bank)
5952            .clone_without_scheduler();
5953        assert_eq!(slot_2_bank.slot(), 2);
5954        assert_eq!(slot_2_bank.tick_height(), 2);
5955        assert_eq!(slot_2_bank.max_tick_height(), 6);
5956        assert_eq!(slot_2_bank.last_blockhash(), slot_0_hash);
5957
5958        let slot_1_entries = entry::create_ticks(TICKS_PER_SLOT, hashes_per_tick, slot_0_hash);
5959        let slot_1_hash = slot_1_entries.last().unwrap().hash;
5960        confirm_slot_entries_for_tests(&slot_2_bank, slot_1_entries, false, slot_0_hash).unwrap();
5961        assert_eq!(slot_2_bank.tick_height(), 4);
5962        assert_eq!(slot_2_bank.last_blockhash(), slot_0_hash);
5963        assert_eq!(slot_2_bank.get_hash_age(&genesis_hash), Some(1));
5964        assert_eq!(slot_2_bank.get_hash_age(&slot_0_hash), Some(0));
5965
5966        struct TestCase {
5967            recent_blockhash: Hash,
5968            expected_result: result::Result<(), BlockstoreProcessorError>,
5969        }
5970
5971        let test_cases = [
5972            TestCase {
5973                recent_blockhash: slot_1_hash,
5974                expected_result: Err(BlockstoreProcessorError::InvalidTransaction(
5975                    TransactionError::BlockhashNotFound,
5976                )),
5977            },
5978            TestCase {
5979                recent_blockhash: slot_0_hash,
5980                expected_result: Ok(()),
5981            },
5982        ];
5983
5984        // Check that slot 2 transactions can only use hashes for completed blocks.
5985        for TestCase {
5986            recent_blockhash,
5987            expected_result,
5988        } in test_cases
5989        {
5990            let slot_2_entries = {
5991                let to_pubkey = Pubkey::new_unique();
5992                let mut prev_entry_hash = slot_1_hash;
5993                let mut remaining_entry_hashes = hashes_per_tick;
5994
5995                let tx =
5996                    system_transaction::transfer(&mint_keypair, &to_pubkey, 1, recent_blockhash);
5997                remaining_entry_hashes = remaining_entry_hashes.checked_sub(1).unwrap();
5998                let mut entries = vec![next_entry_mut(&mut prev_entry_hash, 1, vec![tx])];
5999
6000                entries.push(next_entry_mut(
6001                    &mut prev_entry_hash,
6002                    remaining_entry_hashes,
6003                    vec![],
6004                ));
6005                entries.push(next_entry_mut(
6006                    &mut prev_entry_hash,
6007                    hashes_per_tick,
6008                    vec![],
6009                ));
6010
6011                entries
6012            };
6013
6014            let slot_2_hash = slot_2_entries.last().unwrap().hash;
6015            let result =
6016                confirm_slot_entries_for_tests(&slot_2_bank, slot_2_entries, true, slot_1_hash);
6017            match (result, expected_result) {
6018                (Ok(()), Ok(())) => {
6019                    assert_eq!(slot_2_bank.tick_height(), slot_2_bank.max_tick_height());
6020                    assert_eq!(slot_2_bank.last_blockhash(), slot_2_hash);
6021                    assert_eq!(slot_2_bank.get_hash_age(&genesis_hash), Some(2));
6022                    assert_eq!(slot_2_bank.get_hash_age(&slot_0_hash), Some(1));
6023                    assert_eq!(slot_2_bank.get_hash_age(&slot_2_hash), Some(0));
6024                }
6025                (
6026                    Err(BlockstoreProcessorError::InvalidTransaction(err)),
6027                    Err(BlockstoreProcessorError::InvalidTransaction(expected_err)),
6028                ) => {
6029                    assert_eq!(err, expected_err);
6030                }
6031                (result, expected_result) => {
6032                    panic!("actual result {result:?} != expected result {expected_result:?}");
6033                }
6034            }
6035        }
6036    }
6037
6038    fn confirm_slot_with_block_markers_common()
6039    -> (Blockstore, GenesisConfig, tempfile::TempDir, ThreadPool) {
6040        let GenesisConfigInfo {
6041            mut genesis_config, ..
6042        } = create_genesis_config(100 * LAMPORTS_PER_SOL);
6043
6044        let ticks_per_slot = 1;
6045        genesis_config.ticks_per_slot = ticks_per_slot;
6046
6047        let ledger_path = get_tmp_ledger_path_auto_delete!();
6048        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
6049        let keypair = Arc::new(Keypair::new());
6050        let reed_solomon_cache = ReedSolomonCache::default();
6051
6052        let header = VersionedBlockMarker::from_block_header(BlockHeaderV1 {
6053            parent_slot: 0,
6054            parent_block_id: Hash::default(),
6055        });
6056        let header_component = BlockComponent::new_block_marker(header);
6057
6058        let block_producer_time_nanos = u64::try_from(
6059            genesis_config
6060                .creation_time
6061                .saturating_mul(1_000_000_000)
6062                .saturating_add(1),
6063        )
6064        .unwrap();
6065        let footer = VersionedBlockMarker::from_block_footer(BlockFooterV1 {
6066            bank_hash: Hash::new_unique(),
6067            block_producer_time_nanos,
6068            block_user_agent: b"test".to_vec(),
6069            block_final_cert: None,
6070            skip_reward_cert: None,
6071            notar_reward_cert: None,
6072        });
6073        let footer_component = BlockComponent::new_block_marker(footer);
6074
6075        let shredder = Shredder::new(1, 0, 0, 0).unwrap();
6076        let mut next_shred_index = 0u32;
6077
6078        let header_shreds: Vec<Shred> = shredder
6079            .make_merkle_shreds_from_component(
6080                &keypair,
6081                &header_component,
6082                false,
6083                Hash::default(),
6084                next_shred_index,
6085                0,
6086                &reed_solomon_cache,
6087                &mut ProcessShredsStats::default(),
6088            )
6089            .filter(Shred::is_data)
6090            .collect();
6091        next_shred_index = header_shreds.last().unwrap().index() + 1;
6092
6093        let entries = create_ticks(ticks_per_slot, 0, genesis_config.hash());
6094        let entry_shreds: Vec<Shred> = shredder
6095            .make_merkle_shreds_from_entries(
6096                &keypair,
6097                &entries,
6098                false,
6099                Hash::default(),
6100                next_shred_index,
6101                0,
6102                &reed_solomon_cache,
6103                &mut ProcessShredsStats::default(),
6104            )
6105            .filter(Shred::is_data)
6106            .collect();
6107        next_shred_index = entry_shreds.last().unwrap().index() + 1;
6108
6109        let footer_shreds: Vec<Shred> = shredder
6110            .make_merkle_shreds_from_component(
6111                &keypair,
6112                &footer_component,
6113                true, // last in slot
6114                Hash::default(),
6115                next_shred_index,
6116                0,
6117                &reed_solomon_cache,
6118                &mut ProcessShredsStats::default(),
6119            )
6120            .filter(Shred::is_data)
6121            .collect();
6122
6123        let mut all_shreds = header_shreds;
6124        all_shreds.extend(entry_shreds);
6125        all_shreds.extend(footer_shreds);
6126        blockstore.insert_shreds(all_shreds, None, true).unwrap();
6127
6128        let replay_tx_thread_pool = create_thread_pool(1);
6129
6130        (
6131            blockstore,
6132            genesis_config,
6133            ledger_path,
6134            replay_tx_thread_pool,
6135        )
6136    }
6137
6138    #[test]
6139    fn test_confirm_slot_block_with_markers_fails_without_alpenglow() {
6140        let (blockstore, genesis_config, _ledger_path, replay_tx_thread_pool) =
6141            confirm_slot_with_block_markers_common();
6142
6143        let bank_forks = BankForks::new_rw_arc(Bank::new_for_tests(&genesis_config));
6144        let bank0 = bank_forks.read().unwrap().get(0).unwrap();
6145        let bank1 = Bank::new_from_parent(bank0.clone(), SlotLeader::default(), 1);
6146        assert!(
6147            !bank1
6148                .feature_set
6149                .is_active(&agave_feature_set::alpenglow::id())
6150        );
6151        let bank1 = bank_forks.write().unwrap().insert(bank1);
6152
6153        confirm_slot(
6154            &blockstore,
6155            &bank1,
6156            compute_shred_version(&genesis_config.hash(), None),
6157            &replay_tx_thread_pool,
6158            &mut ConfirmationTiming::default(),
6159            &mut ConfirmationProgress::new(bank0.last_blockhash()),
6160            false,
6161            None,
6162            None,
6163            None,
6164            None,
6165            false,
6166            None,
6167            None,
6168            &MigrationStatus::default(),
6169        )
6170        .unwrap_err();
6171    }
6172
6173    #[test]
6174    fn test_confirm_slot_block_with_markers_succeeds_with_alpenglow() {
6175        let (blockstore, genesis_config, _ledger_path, replay_tx_thread_pool) =
6176            confirm_slot_with_block_markers_common();
6177
6178        let bank_forks = BankForks::new_rw_arc(Bank::new_for_tests(&genesis_config));
6179        let bank0 = bank_forks.read().unwrap().get(0).unwrap();
6180        let mut bank1 = Bank::new_from_parent(bank0.clone(), SlotLeader::default(), 1);
6181        bank1.activate_feature(&agave_feature_set::alpenglow::id());
6182        assert!(
6183            bank1
6184                .feature_set
6185                .is_active(&agave_feature_set::alpenglow::id())
6186        );
6187        let bank1 = bank_forks.write().unwrap().insert(bank1);
6188
6189        confirm_slot(
6190            &blockstore,
6191            &bank1,
6192            compute_shred_version(&genesis_config.hash(), None),
6193            &replay_tx_thread_pool,
6194            &mut ConfirmationTiming::default(),
6195            &mut ConfirmationProgress::new(bank0.last_blockhash()),
6196            true,
6197            None,
6198            None,
6199            None,
6200            None,
6201            false,
6202            None,
6203            None,
6204            &MigrationStatus::post_migration_status(),
6205        )
6206        .unwrap();
6207    }
6208
6209    #[test]
6210    fn test_check_block_cost_limit() {
6211        let dummy_leader_pubkey = solana_pubkey::new_rand();
6212        let GenesisConfigInfo {
6213            genesis_config,
6214            mint_keypair,
6215            ..
6216        } = create_genesis_config_with_leader(500, &dummy_leader_pubkey, 100);
6217        let bank = Bank::new_for_tests(&genesis_config);
6218
6219        let tx = RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer(
6220            &mint_keypair,
6221            &Pubkey::new_unique(),
6222            1,
6223            genesis_config.hash(),
6224        ));
6225        let mut tx_cost = CostModel::calculate_cost(&tx, &bank.feature_set);
6226        let actual_execution_cu = 1;
6227        let actual_loaded_accounts_data_size = 64 * 1024;
6228        let usage_cost_details = tx_cost.usage_cost_details_mut();
6229        usage_cost_details.programs_execution_cost = actual_execution_cu;
6230        usage_cost_details.loaded_accounts_data_size_cost =
6231            CostModel::calculate_loaded_accounts_data_size_cost(
6232                actual_loaded_accounts_data_size,
6233                &bank.feature_set,
6234            );
6235        // set block-limit to be able to just have one transaction
6236        let block_limit = tx_cost.sum();
6237        bank.write_cost_tracker()
6238            .unwrap()
6239            .set_limits(CostTrackerLimits::new(u64::MAX, block_limit, u64::MAX));
6240
6241        let tx_costs = vec![None, Some(tx_cost), None];
6242        // The transaction will fit when added the first time
6243        assert!(check_block_cost_limits(&bank, &tx_costs).is_ok());
6244        // But adding a second time will exceed the block limit
6245        assert_eq!(
6246            Err(TransactionError::WouldExceedMaxBlockCostLimit),
6247            check_block_cost_limits(&bank, &tx_costs)
6248        );
6249        // Adding another None will noop (even though the block is already full)
6250        assert!(check_block_cost_limits(&bank, &tx_costs[0..1]).is_ok());
6251    }
6252
6253    #[test]
6254    fn test_check_chained_block_id() {
6255        use crate::shred::{ProcessShredsStats, ReedSolomonCache, Shred, Shredder};
6256
6257        let ledger_path = get_tmp_ledger_path_auto_delete!();
6258        let blockstore = Arc::new(
6259            Blockstore::open(ledger_path.path())
6260                .expect("Expected to be able to open database ledger"),
6261        );
6262
6263        // Helper to create and insert data shreds for a slot with a specific
6264        // chained merkle root.
6265        let insert_shreds_with_chained_merkle_root =
6266            |slot: Slot, parent: Slot, chained_merkle_root: Hash| {
6267                let entries = create_ticks(8, 1, Hash::new_unique());
6268                let shreds: Vec<Shred> = Shredder::new(slot, parent, 0, 0)
6269                    .unwrap()
6270                    .make_merkle_shreds_from_entries(
6271                        &Keypair::new(),
6272                        &entries,
6273                        true,
6274                        chained_merkle_root,
6275                        0,
6276                        0,
6277                        &ReedSolomonCache::default(),
6278                        &mut ProcessShredsStats::default(),
6279                    )
6280                    .filter(Shred::is_data)
6281                    .collect();
6282                blockstore.insert_shreds(shreds, None, true).unwrap();
6283            };
6284
6285        // Create a genesis bank (slot 0) with all features active.
6286        let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
6287        let (parent_bank, _bank_forks) =
6288            Bank::new_for_tests(&genesis_config).wrap_with_bank_forks_for_tests();
6289
6290        // Insert parent shreds at slot 0 so get_block_merkle_root returns the
6291        // parent's block ID.
6292        insert_shreds_with_chained_merkle_root(0, 0, Hash::new_unique());
6293        let parent_block_id = blockstore
6294            .get_last_shred_merkle_root(0)
6295            .unwrap()
6296            .expect("parent should have a merkle root");
6297
6298        // Case 1: No shreds for child slot — should return Unavailable
6299        let child_bank = Bank::new_from_parent(parent_bank.clone(), SlotLeader::default(), 10);
6300        assert!(matches!(
6301            check_chained_block_id(&blockstore, &child_bank, &MigrationStatus::default()),
6302            ChainedBlockIdCheck::Unavailable
6303        ));
6304
6305        // Case 2: Chained merkle root matches parent block ID — should return
6306        // Pass
6307        insert_shreds_with_chained_merkle_root(11, 0, parent_block_id);
6308        let child_bank = Bank::new_from_parent(parent_bank.clone(), SlotLeader::default(), 11);
6309        assert!(matches!(
6310            check_chained_block_id(&blockstore, &child_bank, &MigrationStatus::default()),
6311            ChainedBlockIdCheck::Pass
6312        ));
6313
6314        // Case 3: Chained merkle root does NOT match parent block ID — should
6315        // return Mismatch
6316        insert_shreds_with_chained_merkle_root(12, 0, Hash::new_unique());
6317        let child_bank = Bank::new_from_parent(parent_bank.clone(), SlotLeader::default(), 12);
6318        assert!(matches!(
6319            check_chained_block_id(&blockstore, &child_bank, &MigrationStatus::default()),
6320            ChainedBlockIdCheck::Mismatch
6321        ));
6322
6323        // Case 3a: With only the replacement feature active, replay should
6324        // still run the existing inter-slot SIMD-0340 check.
6325        insert_shreds_with_chained_merkle_root(14, 0, Hash::new_unique());
6326        let mut child_bank = Bank::new_from_parent(parent_bank.clone(), SlotLeader::default(), 14);
6327        child_bank.deactivate_feature(&agave_feature_set::validate_chained_block_id::id());
6328        child_bank.activate_feature(&agave_feature_set::validate_chained_block_id_2::id());
6329        assert!(matches!(
6330            check_chained_block_id(&blockstore, &child_bank, &MigrationStatus::default()),
6331            ChainedBlockIdCheck::Mismatch
6332        ));
6333
6334        // Case 3b: With both feature gates inactive, replay should not run the
6335        // chained block ID check.
6336        let mut child_bank = Bank::new_from_parent(parent_bank.clone(), SlotLeader::default(), 14);
6337        child_bank.deactivate_feature(&agave_feature_set::validate_chained_block_id::id());
6338        child_bank.deactivate_feature(&agave_feature_set::validate_chained_block_id_2::id());
6339        assert!(matches!(
6340            check_chained_block_id(&blockstore, &child_bank, &MigrationStatus::default()),
6341            ChainedBlockIdCheck::Inactive
6342        ));
6343
6344        // Case 4: UpdateParent metadata does not bypass Tower validation.
6345        insert_shreds_with_chained_merkle_root(16, 0, Hash::new_unique());
6346        let mut meta = blockstore.meta(16).unwrap().unwrap();
6347        meta.replay_fec_set_index = 32;
6348        blockstore.put_meta(16, &meta).unwrap();
6349        let child_bank = Bank::new_from_parent(parent_bank.clone(), SlotLeader::default(), 16);
6350        assert!(matches!(
6351            check_chained_block_id(&blockstore, &child_bank, &MigrationStatus::default()),
6352            ChainedBlockIdCheck::Mismatch
6353        ));
6354
6355        // Case 5: When alpenglow is active, SIMD-0340 is skipped
6356        assert!(matches!(
6357            check_chained_block_id(
6358                &blockstore,
6359                &child_bank,
6360                &MigrationStatus::post_migration_status()
6361            ),
6362            ChainedBlockIdCheck::Inactive
6363        ));
6364
6365        // Case 6: Parent has no shreds (get_block_merkle_root returns Err) —
6366        // should return Pass regardless of chained merkle root.
6367        let no_shreds_parent_bank = Arc::new(Bank::new_from_parent(
6368            parent_bank,
6369            SlotLeader::default(),
6370            20,
6371        ));
6372        insert_shreds_with_chained_merkle_root(21, 20, Hash::new_unique());
6373        let child_bank = Bank::new_from_parent(no_shreds_parent_bank, SlotLeader::default(), 21);
6374        assert!(matches!(
6375            check_chained_block_id(&blockstore, &child_bank, &MigrationStatus::default()),
6376            ChainedBlockIdCheck::Pass
6377        ));
6378    }
6379
6380    #[test]
6381    fn test_cleanup_alpenglow_genesis_cleans_pending_slots() {
6382        let ledger_path = get_tmp_ledger_path_auto_delete!();
6383        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
6384
6385        let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
6386        let bank_forks = BankForks::new_rw_arc(Bank::new_for_tests(&genesis_config));
6387        let bank0 = bank_forks.read().unwrap().get(0).unwrap();
6388        let leader_schedule_cache = LeaderScheduleCache::new_from_bank(&bank0);
6389
6390        let mut genesis_meta = SlotMeta::new(0, None);
6391        genesis_meta.next_slots = vec![1, 2];
6392        blockstore.put_meta(0, &genesis_meta).unwrap();
6393
6394        for slot in [1, 2] {
6395            let mut meta = SlotMeta::new(slot, Some(0));
6396            meta.consumed = 1;
6397            meta.received = 1;
6398            meta.last_index = Some(0);
6399            blockstore.put_meta(slot, &meta).unwrap();
6400            blockstore.set_dead_slot(slot).unwrap();
6401        }
6402
6403        let owner = Pubkey::new_unique();
6404        let first_alpenglow_key = Pubkey::new_unique();
6405        let pending_key = Pubkey::new_unique();
6406
6407        let first_alpenglow_bank = Arc::new(Bank::new_from_parent(
6408            bank0.clone(),
6409            SlotLeader::default(),
6410            1,
6411        ));
6412        first_alpenglow_bank
6413            .store_account(&first_alpenglow_key, &AccountSharedData::new(1, 0, &owner));
6414        assert!(
6415            first_alpenglow_bank
6416                .get_account(&first_alpenglow_key)
6417                .is_some()
6418        );
6419        let first_alpenglow_bank =
6420            BankWithScheduler::new_without_scheduler(first_alpenglow_bank.clone());
6421
6422        let pending_bank = Bank::new_from_parent(bank0.clone(), SlotLeader::default(), 2);
6423        pending_bank.store_account(&pending_key, &AccountSharedData::new(1, 0, &owner));
6424        assert!(pending_bank.get_account(&pending_key).is_some());
6425
6426        let pending_meta = blockstore.meta(2).unwrap().unwrap();
6427        let mut pending_slots = vec![(pending_meta, pending_bank, bank0.last_blockhash())];
6428
6429        cleanup_and_populate_pending_from_alpenglow_genesis(
6430            &first_alpenglow_bank,
6431            0,
6432            &bank_forks,
6433            &blockstore,
6434            &leader_schedule_cache,
6435            &mut pending_slots,
6436            &ProcessOptions::default(),
6437            &MigrationStatus::post_migration_status(),
6438        )
6439        .unwrap();
6440
6441        assert!(!blockstore.is_dead(1));
6442        assert!(!blockstore.is_dead(2));
6443        assert!(
6444            first_alpenglow_bank
6445                .get_account(&first_alpenglow_key)
6446                .is_none()
6447        );
6448
6449        let queued_slots: BTreeSet<_> = pending_slots
6450            .iter()
6451            .map(|(_, bank, _)| bank.slot())
6452            .collect();
6453        assert_eq!(queued_slots, BTreeSet::from([1, 2]));
6454        for (_, bank, _) in &pending_slots {
6455            assert!(bank.get_account(&first_alpenglow_key).is_none());
6456            assert!(bank.get_account(&pending_key).is_none());
6457        }
6458    }
6459
6460    #[test]
6461    fn test_startup_parent_id_check() {
6462        let ledger_path = get_tmp_ledger_path_auto_delete!();
6463        let blockstore = Blockstore::open(ledger_path.path()).unwrap();
6464
6465        let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
6466        let bank_forks = BankForks::new_rw_arc(Bank::new_for_tests(&genesis_config));
6467        let bank0 = bank_forks.read().unwrap().get(0).unwrap();
6468        let parent_bank = Arc::new(Bank::new_from_parent(bank0, SlotLeader::default(), 1));
6469        let parent_block_id = Hash::new_unique();
6470        parent_bank.set_block_id(Some(parent_block_id));
6471
6472        let leader_schedule_cache = LeaderScheduleCache::new_from_bank(&parent_bank);
6473        let mut parent_meta = SlotMeta::new(1, Some(0));
6474        parent_meta.next_slots = vec![2, 3];
6475
6476        for (slot, block_id) in [(2, Hash::new_unique()), (3, parent_block_id)] {
6477            let mut meta = SlotMeta::new(slot, Some(1));
6478            meta.consumed = 1;
6479            meta.received = 1;
6480            meta.last_index = Some(0);
6481            meta.parent_block_id = block_id;
6482            meta.replay_fec_set_index = 32;
6483            blockstore.put_meta(slot, &meta).unwrap();
6484        }
6485
6486        let mut pending_slots = Vec::new();
6487        process_next_slots(
6488            &parent_bank,
6489            &parent_meta,
6490            &blockstore,
6491            &leader_schedule_cache,
6492            &mut pending_slots,
6493            &ProcessOptions::default(),
6494            &MigrationStatus::post_migration_status(),
6495        )
6496        .unwrap();
6497
6498        assert_eq!(pending_slots.len(), 1);
6499        assert_eq!(pending_slots[0].1.slot(), 3);
6500
6501        let mut pending_slots = Vec::new();
6502        process_next_slots(
6503            &parent_bank,
6504            &parent_meta,
6505            &blockstore,
6506            &leader_schedule_cache,
6507            &mut pending_slots,
6508            &ProcessOptions {
6509                skip_inter_slot_verification: true,
6510                ..ProcessOptions::default()
6511            },
6512            &MigrationStatus::post_migration_status(),
6513        )
6514        .unwrap();
6515
6516        assert_eq!(
6517            pending_slots
6518                .iter()
6519                .map(|(_, bank, _)| bank.slot())
6520                .collect::<BTreeSet<_>>(),
6521            BTreeSet::from([2, 3])
6522        );
6523    }
6524}