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
90pub 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
112pub enum ChainedBlockIdCheck {
114 Inactive,
116 Pass,
118 Mismatch,
120 Unavailable,
122}
123
124fn 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 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 get_first_error(batch, processing_results)?;
211 Ok(None)
212 }
213 Some(extra_pre_commit_callback) => {
214 let [result] = processing_results else {
217 panic!("unexpected result count: {}", processing_results.len());
218 };
219 assert!(transaction_indexes.is_empty());
222
223 let freeze_lock = bank.freeze_lock();
228
229 let committed_index = extra_pre_commit_callback(result)?;
233
234 if let Some(index) = committed_index {
238 let transaction_indexes = transaction_indexes.to_mut();
239 transaction_indexes.reserve_exact(1);
242 transaction_indexes.push(index);
243 }
244 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 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 Ok(get_transaction_costs(
275 bank,
276 &commit_results,
277 batch.sanitized_transactions(),
278 ))
279 } else {
280 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 debug_assert!(balance_collector.is_some());
318
319 let (balances, token_balances) =
320 compile_collected_balances(balance_collector.unwrap_or_default());
321
322 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
344fn 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
479fn 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 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 let mut first_err = Ok(());
552
553 for LockedTransactionsWithIndexes {
554 lock_results,
555 transactions,
556 starting_index,
557 } in locked_entries
558 {
559 bank.unlock_accounts(transactions.iter().zip(lock_results.iter()));
561 let indexes = starting_index..starting_index + transactions.len();
565 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 timing.accumulate(execute_batches_internal_metrics, false);
621 Ok(())
622}
623
624pub 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 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 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
760fn 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 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 bank.unlock_accounts(transactions.iter().zip(lock_results.iter()));
790
791 process_batches(batches.drain(..))?;
798
799 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 bank.unlock_accounts(transactions.iter().zip(lock_results.iter()));
813
814 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
870pub type ProcessSlotCallback = Arc<dyn Fn(&Bank) + Sync + Send>;
873
874#[derive(Default, Clone)]
875pub struct ProcessOptions {
876 pub run_verification: bool,
878 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 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 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#[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 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 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 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
1066fn 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 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#[derive(Debug)]
1189pub struct ConfirmationTiming {
1190 pub started: Instant,
1194
1195 pub confirmation_elapsed: u64,
1202
1203 pub replay_elapsed: u64,
1209
1210 pub poh_verify_elapsed: u64,
1212
1213 pub transaction_verify_elapsed: u64,
1216
1217 pub fetch_elapsed: u64,
1220
1221 pub fetch_fail_elapsed: u64,
1224
1225 pub batch_execute: BatchExecutionTiming,
1227
1228 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#[derive(Debug, Default)]
1250pub struct BatchExecutionTiming {
1251 pub totals: ExecuteTimings,
1254
1255 wall_clock_us: Saturating<u64>,
1258
1259 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 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 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 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 eager!{report_execute_timings!(self.batch_execute.totals, is_unified_scheduler_enabled)}
1419 );
1420 };
1421
1422 if !is_unified_scheduler_enabled {
1427 self.batch_execute.slowest_thread.report_stats(slot);
1428 }
1429
1430 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 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 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 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 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 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 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 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 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 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 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 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#[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
2144fn 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 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 pending_slots.clear();
2188 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
2202fn 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 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 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 pending_slots.sort_by_key(|b| cmp::Reverse(b.1.slot()));
2276 Ok(())
2277}
2278
2279pub fn set_alpenglow_ticks(bank: &Bank, migration_status: &MigrationStatus) {
2286 if !migration_status.should_have_alpenglow_ticks(bank.slot()) {
2287 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#[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 let mut total_slots_processed = 0;
2329 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 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 migration_status.is_ready_to_enable() {
2426 let genesis_slot = migration_status.enable_alpenglow_during_startup();
2427
2428 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 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 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 let cluster_root_bank = all_banks.get(&supermajority_root).unwrap();
2468
2469 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 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; } assert!(new_root_bank.slot() > root);
2483
2484 rooted_slots.push((new_root_bank.slot(), Some(new_root_bank.hash())));
2485 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 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 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 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
2584fn supermajority_root(roots: &[(Slot, u64)], total_epoch_stake: u64) -> Option<Slot> {
2586 if roots.is_empty() {
2587 return None;
2588 }
2589
2590 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 roots_stakes.sort_unstable_by_key(|a| cmp::Reverse(a.0));
2622
2623 supermajority_root(&roots_stakes, total_epoch_stake)
2625}
2626
2627pub 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#[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 return Ok(());
2728 }
2729 ChainedBlockIdCheck::Mismatch => {
2730 mark_dead_if_primary_access(blockstore, slot);
2732 return Err(BlockstoreProcessorError::ChainedBlockIdFailure(
2733 slot,
2734 bank.parent_slot(),
2735 ));
2736 }
2737 }
2738 }
2739
2740 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(); 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
2865pub 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 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 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 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, )
3045 .unwrap();
3046
3047 (bank_forks, leader_schedule_cache)
3048 }
3049
3050 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 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 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 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 let (ledger_path, blockhash) = create_new_tmp_ledger_auto_delete!(&genesis_config);
3169 let blockstore = Blockstore::open(ledger_path.path()).unwrap();
3170
3171 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 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 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 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 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 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 {
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 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 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]); 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 let (bank_forks, ..) = test_process_blockstore(&genesis_config, &blockstore, &opts);
3347
3348 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 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 let blockstore = Blockstore::open(ledger_path.path()).unwrap();
3379
3380 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 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 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 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 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 let blockstore = Blockstore::open(ledger_path.path()).unwrap();
3459
3460 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 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 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 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 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 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 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 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 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 epoch_schedule = get_epoch_schedule(&genesis_config);
3664 let last_slot = epoch_schedule.get_last_slot_in_epoch(1);
3665
3666 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 let rooted_slots: Vec<Slot> = (0..=last_slot).collect();
3679 blockstore.set_roots(rooted_slots.iter()).unwrap();
3680
3681 blockstore
3683 .set_roots(std::iter::once(&(last_slot + 1)))
3684 .unwrap();
3685
3686 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 assert_eq!(frozen_bank_slots(&bank_forks), vec![last_slot + 1]);
3696
3697 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 assert_eq!(
3762 bank.process_transaction(&tx),
3763 Err(TransactionError::BlockhashNotFound)
3764 );
3765
3766 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 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 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 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 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 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 assert_matches!(bank.transfer(4, &mint_keypair, &keypair1.pubkey()), Ok(_));
3946 assert_matches!(bank.transfer(4, &mint_keypair, &keypair2.pubkey()), Ok(_));
3947
3948 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 ), system_transaction::transfer(
3971 &keypair1,
3972 &mint_keypair.pubkey(),
3973 2,
3974 bank.last_blockhash(),
3975 ), ],
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 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 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(), ),
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 ), system_transaction::transfer(
4043 &keypair1,
4044 &mint_keypair.pubkey(),
4045 2,
4046 bank.last_blockhash(),
4047 ), ],
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 assert_eq!(bank.get_balance(&keypair1.pubkey()), 4);
4061 assert_eq!(bank.get_balance(&keypair2.pubkey()), 4);
4062
4063 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 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 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 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 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 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()); bank.freeze();
4230 let bank_details = SlotDetails::new_from_bank(&bank, true).unwrap();
4231
4232 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 assert_ne!(ok_bank_details, bank_details);
4247 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 assert_matches!(bank.transfer(5, &mint_keypair, &keypair1.pubkey()), Ok(_));
4278 assert_matches!(bank.transfer(4, &mint_keypair, &keypair2.pubkey()), Ok(_));
4279
4280 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 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 ), system_transaction::transfer(
4307 &keypair1,
4308 &mint_keypair.pubkey(),
4309 2,
4310 bank.last_blockhash(),
4311 ), ],
4313 );
4314 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 ), ],
4336 );
4337 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 assert_matches!(bank.transfer(5, &mint_keypair, &keypair1.pubkey()), Ok(_));
4378 assert_matches!(bank.transfer(5, &mint_keypair, &keypair2.pubkey()), Ok(_));
4379
4380 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 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 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 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 let keypairs: Vec<_> = (0..NUM_TRANSFERS * 2).map(|_| Keypair::new()).collect();
4477
4478 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, 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 let entropy_multiplier: usize = 25;
4526 let initial_lamports = 100;
4527
4528 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 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 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 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 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 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 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 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 assert_eq!(
4698 bank.process_transaction(&tx),
4699 Err(TransactionError::ProgramAccountNotFound)
4700 );
4701 assert_eq!(
4703 bank.process_transaction(&tx),
4704 Err(TransactionError::AlreadyProcessed)
4705 );
4706
4707 let tx = system_transaction::transfer(&mint_keypair, &pubkey, 1000, Hash::default());
4709 let signature = tx.signatures[0];
4710
4711 assert_eq!(
4713 bank.process_transaction(&tx).map(|_| signature),
4714 Err(TransactionError::BlockhashNotFound)
4715 );
4716
4717 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(), ],
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 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 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 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 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 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 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, )
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 assert_eq!(
4897 &bank_forks[6]
4898 .parents()
4899 .iter()
4900 .map(|bank| bank.slot())
4901 .collect::<Vec<_>>(),
4902 &[5]
4903 );
4904
4905 verify_fork_infos(&bank_forks);
4907 }
4908
4909 #[test]
4910 #[ignore]
4911 fn test_process_entries_stress() {
4912 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 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, 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 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 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 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 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 let bank_1_blockhash = bank1.last_blockhash();
5111
5112 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 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 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 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 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 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 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 let last_minor_fork_slot = really_last_main_fork_slot + 1;
5244 let minor_fork = tr(last_minor_fork_slot);
5245
5246 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 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 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 for slot in 0..=last_minor_fork_slot {
5333 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 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 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 assert!(supermajority_root_from_vote_accounts(total_stake, &HashMap::default()).is_none());
5427
5428 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 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 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 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 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
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 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 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, 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 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 assert!(check_block_cost_limits(&bank, &tx_costs).is_ok());
6244 assert_eq!(
6246 Err(TransactionError::WouldExceedMaxBlockCostLimit),
6247 check_block_cost_limits(&bank, &tx_costs)
6248 );
6249 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 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 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_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 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 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 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 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 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 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 assert!(matches!(
6357 check_chained_block_id(
6358 &blockstore,
6359 &child_bank,
6360 &MigrationStatus::post_migration_status()
6361 ),
6362 ChainedBlockIdCheck::Inactive
6363 ));
6364
6365 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}