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