1use {
2 crate::{
3 bank::Bank,
4 block_component_processor::vote_reward::{
5 CalcVoteRewardUpdateVoteStatesError, calc_vote_rewards_update_vote_states,
6 },
7 installed_scheduler_pool::BankWithScheduler,
8 leader_schedule_utils::leader_slot_index,
9 validated_block_finalization::{
10 BlockFinalizationCertError, ValidatedBlockFinalizationCert,
11 },
12 validated_reward_certificate::{Error as ValidatedRewardCertError, ValidatedRewardCert},
13 },
14 agave_transaction_view::transaction_data::TransactionData,
15 agave_votor_messages::{
16 certificate::{CertSignature, Certificate, CertificateType, GenesisCert},
17 consensus_message::Block,
18 migration::MigrationStatus,
19 unverified_vote_message::UnverifiedCertificate,
20 },
21 crossbeam_channel::{Sender, TrySendError},
22 log::*,
23 smallvec::{SmallVec, smallvec},
24 solana_clock::Slot,
25 solana_entry::{
26 block_component::{
27 BlockFooterV1, BlockMarkerV1, GenesisCertBlockMarker, VersionedBlockFooter,
28 VersionedBlockHeader, VersionedBlockMarker, VersionedUpdateParent,
29 },
30 entry::EntryView,
31 },
32 solana_hash::Hash,
33 solana_measure::measure_us,
34 solana_pubkey::Pubkey,
35 solana_svm_timings::ExecuteTimings,
36 solana_transaction_error::TransactionError,
37 std::{collections::HashSet, sync::Arc},
38 thiserror::Error,
39};
40
41pub(crate) mod vote_reward;
42
43#[derive(Debug, Error)]
44pub enum BankFooterError {
45 #[error("calc vote rewards updating vote states failed with \"{0}\"")]
46 CalcVoteRewardUpdateVoteStates(#[from] CalcVoteRewardUpdateVoteStatesError),
47}
48
49#[derive(Debug, Error)]
50pub enum BlockComponentProcessorError {
51 #[error("BlockComponent detected pre-migration")]
52 BlockComponentPreMigration,
53 #[error("GenesisCertificate marker detected when GenesisCertificate is already populated")]
54 GenesisCertificateAlreadyPopulated,
55 #[error("GenesisCertificate marker detected when the cluster has Alpenglow enabled at slot 0")]
56 GenesisCertificateInAlpenglowCluster,
57 #[error("GenesisCertificate marker detected on a block which is not a child of genesis")]
58 GenesisCertificateOnNonChild,
59 #[error("GenesisCertificate was invalid and failed to verify")]
60 GenesisCertificateFailedVerification,
61 #[error("Alpenglow migration became ready; aborting the TowerBFT bank")]
62 AlpenglowMigrationTransition,
63 #[error("GenesisCertificate marker must immediately follow the block header")]
64 GenesisCertificateOutOfOrder,
65 #[error("FinalizationCertificate was invalid or failed to verify {0}")]
66 InvalidFinalizationCertificate(#[from] BlockFinalizationCertError),
67 #[error("Missing block footer")]
68 MissingBlockFooter,
69 #[error("Missing genesis certificate marker")]
70 MissingGenesisCertificateMarker,
71 #[error("Missing parent marker (neither a header nor an update parent was present)")]
72 MissingParentMarker,
73 #[error("Entry batch detected after block footer")]
74 EntryBatchAfterBlockFooter,
75 #[error("Alpentick must be the final block component and appear after block footer")]
76 InvalidAlpentickPosition,
77 #[error("Multiple block footers detected")]
78 MultipleBlockFooters,
79 #[error("Multiple block headers detected")]
80 MultipleBlockHeaders,
81 #[error(
82 "Block header parent slot mismatch: header={header_parent_slot}, bank={bank_parent_slot}"
83 )]
84 HeaderParentSlotMismatch {
85 header_parent_slot: Slot,
86 bank_parent_slot: Slot,
87 },
88 #[error("Multiple update parents detected")]
89 MultipleUpdateParents,
90 #[error("Nanosecond clock out of bounds")]
91 NanosecondClockOutOfBounds,
92 #[error("Spurious update parent")]
93 SpuriousUpdateParent,
94 #[error("UpdateParent marker is only valid in the first slot of a leader window: slot {0}")]
95 UpdateParentNotFirstInLeaderWindow(Slot),
96 #[error(
97 "UpdateParent cannot be the initial parent marker unless replay starts at UpdateParent"
98 )]
99 UnexpectedInitialUpdateParent,
100 #[error("Abandoned bank")]
101 AbandonedBank(VersionedUpdateParent),
102 #[error("invalid reward certs {0}")]
103 InvalidRewardCerts(#[from] ValidatedRewardCertError),
104 #[error("updating bank footer failed with \"{0}\"")]
105 UpdateBankFooter(#[from] BankFooterError),
106 #[error("transaction execution failed with \"{0}\"")]
107 TransactionExecution(#[from] TransactionError),
108}
109
110impl BlockComponentProcessorError {
111 pub fn is_update_parent_recoverable_replay_error(&self) -> bool {
117 match self {
118 BlockComponentProcessorError::MissingParentMarker
119 | BlockComponentProcessorError::EntryBatchAfterBlockFooter
120 | BlockComponentProcessorError::InvalidAlpentickPosition
121 | BlockComponentProcessorError::MultipleBlockFooters
122 | BlockComponentProcessorError::MultipleBlockHeaders
123 | BlockComponentProcessorError::HeaderParentSlotMismatch { .. }
124 | BlockComponentProcessorError::NanosecondClockOutOfBounds
125 | BlockComponentProcessorError::UnexpectedInitialUpdateParent
126 | BlockComponentProcessorError::GenesisCertificateOutOfOrder
127 | BlockComponentProcessorError::GenesisCertificateAlreadyPopulated
128 | BlockComponentProcessorError::GenesisCertificateInAlpenglowCluster
129 | BlockComponentProcessorError::GenesisCertificateOnNonChild
130 | BlockComponentProcessorError::GenesisCertificateFailedVerification
131 | BlockComponentProcessorError::SpuriousUpdateParent
132 | BlockComponentProcessorError::AbandonedBank(_)
133 | BlockComponentProcessorError::InvalidRewardCerts(_)
134 | BlockComponentProcessorError::UpdateBankFooter(_)
135 | BlockComponentProcessorError::InvalidFinalizationCertificate(_)
136 | BlockComponentProcessorError::TransactionExecution(_) => true,
137 BlockComponentProcessorError::BlockComponentPreMigration
138 | BlockComponentProcessorError::MissingBlockFooter
139 | BlockComponentProcessorError::MissingGenesisCertificateMarker
140 | BlockComponentProcessorError::MultipleUpdateParents
141 | BlockComponentProcessorError::AlpenglowMigrationTransition
142 | BlockComponentProcessorError::UpdateParentNotFirstInLeaderWindow(_) => false,
143 }
144 }
145}
146
147#[derive(Debug, Clone, PartialEq, Eq)]
148enum EntryParentMarker {
150 BlockHeader,
151 UpdateParent,
152}
153
154#[derive(Default, Debug, Clone, PartialEq, Eq)]
155enum BlockComponentStage {
170 #[default]
171 PreParentMarker,
173 AcceptingGenesisOrEntries,
175 AcceptingEntriesOrFooter { parent_marker: EntryParentMarker },
178 AcceptingAlpentick,
180 Done,
182}
183
184impl BlockComponentStage {
185 fn on_header(&mut self) -> Result<(), BlockComponentProcessorError> {
187 match self {
188 Self::PreParentMarker => {
189 *self = Self::AcceptingGenesisOrEntries;
190 Ok(())
191 }
192 Self::AcceptingGenesisOrEntries
193 | Self::AcceptingEntriesOrFooter {
194 parent_marker: EntryParentMarker::BlockHeader,
195 }
196 | Self::AcceptingAlpentick
197 | Self::Done => Err(BlockComponentProcessorError::MultipleBlockHeaders),
198 Self::AcceptingEntriesOrFooter {
199 parent_marker: EntryParentMarker::UpdateParent,
200 } => Err(BlockComponentProcessorError::SpuriousUpdateParent),
201 }
202 }
203
204 fn on_genesis_certificate(&mut self) -> Result<(), BlockComponentProcessorError> {
206 match self {
207 Self::PreParentMarker => Err(BlockComponentProcessorError::MissingParentMarker),
208 Self::AcceptingGenesisOrEntries => {
209 *self = Self::AcceptingEntriesOrFooter {
210 parent_marker: EntryParentMarker::BlockHeader,
211 };
212 Ok(())
213 }
214 Self::AcceptingEntriesOrFooter { .. } | Self::AcceptingAlpentick | Self::Done => {
215 Err(BlockComponentProcessorError::GenesisCertificateOutOfOrder)
216 }
217 }
218 }
219
220 fn on_entry_batch(&mut self) -> Result<(), BlockComponentProcessorError> {
223 match self {
224 Self::PreParentMarker => Err(BlockComponentProcessorError::MissingParentMarker),
225 Self::AcceptingGenesisOrEntries => {
226 *self = Self::AcceptingEntriesOrFooter {
227 parent_marker: EntryParentMarker::BlockHeader,
228 };
229 Ok(())
230 }
231 Self::AcceptingEntriesOrFooter { .. } => Ok(()),
232 Self::AcceptingAlpentick | Self::Done => {
233 Err(BlockComponentProcessorError::EntryBatchAfterBlockFooter)
234 }
235 }
236 }
237
238 fn on_update_parent(
242 &mut self,
243 update_parent: &VersionedUpdateParent,
244 allow_initial_update_parent: bool,
245 ) -> Result<(), BlockComponentProcessorError> {
246 match self {
247 Self::PreParentMarker => {
248 if !allow_initial_update_parent {
249 return Err(BlockComponentProcessorError::UnexpectedInitialUpdateParent);
250 }
251 *self = Self::AcceptingEntriesOrFooter {
252 parent_marker: EntryParentMarker::UpdateParent,
253 };
254 Ok(())
255 }
256 Self::AcceptingGenesisOrEntries
257 | Self::AcceptingEntriesOrFooter {
258 parent_marker: EntryParentMarker::BlockHeader,
259 } => {
260 Err(BlockComponentProcessorError::AbandonedBank(
264 update_parent.clone(),
265 ))
266 }
267 Self::AcceptingEntriesOrFooter {
268 parent_marker: EntryParentMarker::UpdateParent,
269 } => Err(BlockComponentProcessorError::MultipleUpdateParents),
270 Self::AcceptingAlpentick | BlockComponentStage::Done => {
271 Err(BlockComponentProcessorError::SpuriousUpdateParent)
272 }
273 }
274 }
275
276 fn on_footer(&mut self) -> Result<(), BlockComponentProcessorError> {
279 match self {
280 Self::PreParentMarker => Err(BlockComponentProcessorError::MissingParentMarker),
281 Self::AcceptingGenesisOrEntries | Self::AcceptingEntriesOrFooter { .. } => {
282 *self = Self::AcceptingAlpentick;
283 Ok(())
284 }
285 Self::AcceptingAlpentick | Self::Done => {
286 Err(BlockComponentProcessorError::MultipleBlockFooters)
287 }
288 }
289 }
290
291 fn on_alpentick(&mut self) -> Result<(), BlockComponentProcessorError> {
293 match self {
294 Self::PreParentMarker => Err(BlockComponentProcessorError::MissingParentMarker),
295 Self::AcceptingGenesisOrEntries => {
296 Err(BlockComponentProcessorError::InvalidAlpentickPosition)
297 }
298 Self::AcceptingEntriesOrFooter { .. } => {
299 Err(BlockComponentProcessorError::InvalidAlpentickPosition)
300 }
301 Self::AcceptingAlpentick => {
302 *self = Self::Done;
303 Ok(())
304 }
305 Self::Done => Err(BlockComponentProcessorError::InvalidAlpentickPosition),
306 }
307 }
308
309 fn on_final(&self) -> Result<(), BlockComponentProcessorError> {
311 match self {
312 Self::Done => Ok(()),
313 Self::AcceptingAlpentick => Err(BlockComponentProcessorError::InvalidAlpentickPosition),
314 Self::PreParentMarker
315 | Self::AcceptingGenesisOrEntries
316 | Self::AcceptingEntriesOrFooter { .. } => {
317 Err(BlockComponentProcessorError::MissingBlockFooter)
318 }
319 }
320 }
321}
322
323#[derive(Default)]
324pub struct BlockComponentProcessor {
325 stage: BlockComponentStage,
326 has_genesis_certificate_marker: bool,
327}
328
329impl BlockComponentProcessor {
330 pub fn on_final(
331 &self,
332 migration_status: &MigrationStatus,
333 slot: Slot,
334 parent_slot: Slot,
335 ) -> Result<(), BlockComponentProcessorError> {
336 if !migration_status.should_allow_block_markers(slot) {
339 if self.stage == BlockComponentStage::PreParentMarker {
340 return Ok(());
341 } else {
342 return Err(BlockComponentProcessorError::BlockComponentPreMigration);
343 };
344 }
345
346 if Self::requires_genesis_certificate_marker(migration_status, parent_slot)
347 && !self.has_genesis_certificate_marker
348 {
349 return Err(BlockComponentProcessorError::MissingGenesisCertificateMarker);
350 }
351
352 self.stage.on_final()
353 }
354
355 fn requires_genesis_certificate_marker(
361 migration_status: &MigrationStatus,
362 parent_slot: Slot,
363 ) -> bool {
364 migration_status
365 .genesis_block()
366 .is_some_and(|genesis_block| {
367 genesis_block.slot != 0 && parent_slot == genesis_block.slot
368 })
369 }
370
371 pub fn on_entry_batch<D: TransactionData>(
377 &mut self,
378 migration_status: &MigrationStatus,
379 slot: Slot,
380 entries: &[EntryView<D>],
381 is_final_component: bool,
382 ) -> Result<(), BlockComponentProcessorError> {
383 if !migration_status.should_allow_block_markers(slot) {
384 return Ok(());
385 }
386
387 let is_alpentick = is_final_component
391 && matches!(entries, [entry] if entry.is_tick() && entry.num_hashes == 1);
392
393 if is_alpentick {
394 self.stage.on_alpentick()
395 } else {
396 self.stage.on_entry_batch()
397 }
398 }
399
400 pub fn on_marker(
407 &mut self,
408 bank: &BankWithScheduler,
409 parent_bank: Arc<Bank>,
410 shred_version: u16,
411 marker: VersionedBlockMarker,
412 allow_initial_update_parent: bool,
413 finalization_cert_sender: Option<&Sender<SmallVec<[Certificate; 2]>>>,
414 migration_status: &MigrationStatus,
415 execute_timings: &mut ExecuteTimings,
416 ) -> Result<(), BlockComponentProcessorError> {
417 let slot = bank.slot();
418 let VersionedBlockMarker::V1(marker) = marker;
419
420 let markers_fully_enabled = migration_status.should_allow_block_markers(slot);
421 let in_migration = migration_status.is_in_migration();
422 let fast_leader_handover_active =
423 bank.feature_set.snapshot().alpenglow_fast_leader_handover;
424
425 match marker {
426 BlockMarkerV1::BlockHeader(header) if markers_fully_enabled || in_migration => {
430 self.on_header(header.inner(), bank.parent_slot())
431 }
432 BlockMarkerV1::GenesisCertificate(genesis_cert_block_marker)
433 if markers_fully_enabled || in_migration =>
434 {
435 self.on_genesis_cert_block_marker(
436 bank.clone_without_scheduler(),
437 shred_version,
438 genesis_cert_block_marker.into_inner(),
439 migration_status,
440 )
441 }
442
443 BlockMarkerV1::BlockFooter(footer) if markers_fully_enabled => self.on_footer(
445 &migration_status.my_pubkey(),
446 bank,
447 parent_bank,
448 shred_version,
449 footer.into_inner(),
450 finalization_cert_sender,
451 execute_timings,
452 ),
453
454 BlockMarkerV1::UpdateParent(update_parent) if markers_fully_enabled => {
455 if fast_leader_handover_active {
456 self.on_update_parent(slot, update_parent.inner(), allow_initial_update_parent)
457 } else {
458 Err(BlockComponentProcessorError::SpuriousUpdateParent)
459 }
460 }
461
462 _ => Err(BlockComponentProcessorError::BlockComponentPreMigration),
464 }
465 }
466
467 pub fn on_genesis_cert_block_marker(
469 &mut self,
470 bank: Arc<Bank>,
471 shred_version: u16,
472 genesis_block_marker: GenesisCertBlockMarker,
473 migration_status: &MigrationStatus,
474 ) -> Result<(), BlockComponentProcessorError> {
475 self.stage.on_genesis_certificate()?;
476 self.process_unvalidated_genesis_cert_block_marker(
477 bank,
478 genesis_block_marker,
479 migration_status,
480 Some(shred_version),
481 )?;
482 Ok(())
483 }
484
485 pub fn on_genesis_cert_block_marker_leader(
487 &mut self,
488 bank: Arc<Bank>,
489 genesis_block_marker: GenesisCertBlockMarker,
490 migration_status: &MigrationStatus,
491 ) -> Result<(), BlockComponentProcessorError> {
492 self.process_unvalidated_genesis_cert_block_marker(
493 bank,
494 genesis_block_marker,
495 migration_status,
496 None,
497 )?;
498 Ok(())
499 }
500
501 fn process_unvalidated_genesis_cert_block_marker(
503 &mut self,
504 bank: Arc<Bank>,
505 genesis_block_marker: GenesisCertBlockMarker,
506 migration_status: &MigrationStatus,
507 shred_version: Option<u16>,
508 ) -> Result<(), BlockComponentProcessorError> {
509 if bank.parent_slot() == 0 {
511 return Err(BlockComponentProcessorError::GenesisCertificateInAlpenglowCluster);
512 }
513
514 let parent_block_id = bank
515 .parent_block_id()
516 .expect("Block id is populated for all slots > 0");
517 if (bank.parent_slot(), parent_block_id)
518 != (genesis_block_marker.slot, genesis_block_marker.block_id)
519 {
520 return Err(BlockComponentProcessorError::GenesisCertificateOnNonChild);
521 }
522
523 if bank.get_alpenglow_genesis_certificate().is_some() {
524 return Err(BlockComponentProcessorError::GenesisCertificateAlreadyPopulated);
525 }
526
527 let genesis_cert = GenesisCert {
528 block: Block {
529 slot: genesis_block_marker.slot,
530 block_id: genesis_block_marker.block_id,
531 },
532 signature: CertSignature {
533 signature: genesis_block_marker.bls_signature,
534 bitmap: genesis_block_marker.bitmap,
535 },
536 };
537 if let Some(shred_version) = shred_version {
538 Self::verify_genesis_certificate(&bank, &genesis_cert, shred_version)?;
539 }
540
541 bank.set_alpenglow_genesis_certificate(&genesis_cert);
542 self.has_genesis_certificate_marker = true;
543
544 if migration_status.is_alpenglow_enabled() {
545 bank.set_hashes_per_tick(None);
547 return Ok(());
548 }
549
550 warn!(
556 "{}: Alpenglow genesis marker processed during replay of {}. Transitioning Alpenglow \
557 to ReadyToEnable",
558 migration_status.my_pubkey(),
559 bank.slot()
560 );
561 migration_status.set_genesis_block(genesis_cert.block);
562 migration_status.set_genesis_certificate(Arc::new(genesis_cert));
563 assert!(migration_status.is_ready_to_enable());
564
565 Err(BlockComponentProcessorError::AlpenglowMigrationTransition)
568 }
569
570 fn verify_genesis_certificate(
571 bank: &Bank,
572 cert: &GenesisCert,
573 shred_version: u16,
574 ) -> Result<(), BlockComponentProcessorError> {
575 let cert_slot = cert.block.slot;
576 let unverified_cert = UnverifiedCertificate {
577 cert_type: CertificateType::Genesis(cert.block),
578 signature: cert.signature.signature,
579 bitmap: cert.signature.bitmap.clone(),
580 shred_version,
581 };
582 bank.verify_certificate(unverified_cert, None)
583 .map_err(|_| {
584 warn!(
585 "Failed to verify genesis certificate for slot {cert_slot} in bank slot {}",
586 bank.slot()
587 );
588 BlockComponentProcessorError::GenesisCertificateFailedVerification
589 })?;
590
591 Ok(())
592 }
593
594 fn on_footer(
595 &mut self,
596 my_pubkey: &Pubkey,
597 bank: &BankWithScheduler,
598 parent_bank: Arc<Bank>,
599 shred_version: u16,
600 footer: VersionedBlockFooter,
601 finalization_cert_sender: Option<&Sender<SmallVec<[Certificate; 2]>>>,
602 execute_timings: &mut ExecuteTimings,
603 ) -> Result<(), BlockComponentProcessorError> {
604 self.stage.on_footer()?;
605
606 let VersionedBlockFooter::V1(footer) = footer;
607
608 Self::enforce_nanosecond_clock_bounds(bank, &parent_bank, &footer)?;
609
610 let BlockFooterV1 {
611 bank_hash,
612 block_producer_time_nanos,
613 block_user_agent: _,
614 block_final_cert,
615 skip_reward_cert,
616 notar_reward_cert,
617 } = footer;
618
619 let reward_cert = ValidatedRewardCert::try_new(
620 bank,
621 shred_version,
622 &skip_reward_cert,
623 ¬ar_reward_cert,
624 )?;
625 let block_producer_time_nanos =
626 Self::block_producer_time_nanos_as_i64(block_producer_time_nanos)?;
627 let final_cert = block_final_cert
628 .map(|final_cert| {
629 ValidatedBlockFinalizationCert::try_from_footer(final_cert, bank, shred_version)
630 .map_err(BlockComponentProcessorError::InvalidFinalizationCertificate)
631 })
632 .transpose()?;
633
634 let (footer_input, pool_input) = match final_cert {
635 None => (None, None),
636 Some(cert) => {
637 let (signers, finalize_cert, notarize_cert) = cert.into_parts();
638 let final_slot = finalize_cert.cert_type.slot();
639 (
640 Some((signers, final_slot)),
641 Some((finalize_cert, notarize_cert)),
642 )
643 }
644 };
645
646 if let Some((result, execute_time)) = bank.wait_for_completed_scheduler() {
648 execute_timings.accumulate(&execute_time);
649 result?;
650 }
651
652 let _ = Self::update_bank_with_footer_fields(
653 bank,
654 block_producer_time_nanos,
655 Some(bank_hash),
656 reward_cert,
657 footer_input
658 .as_ref()
659 .map(|(validators, slot)| (validators, *slot)),
660 )?;
661
662 if let Some((finalize_cert, notarize_cert)) = pool_input
664 && let Some(sender) = finalization_cert_sender
665 {
666 let channel_name = "finalization_cert_sender";
667 let certs = match notarize_cert {
668 None => smallvec![finalize_cert],
669 Some(c) => smallvec![finalize_cert, c],
670 };
671 match sender.try_send(certs) {
672 Ok(()) => (),
673 Err(TrySendError::Full(_)) => {
674 warn!("{my_pubkey}: channel \"{channel_name}\" is full, dropping msg")
675 }
676 Err(TrySendError::Disconnected(_)) => {
677 warn!("{my_pubkey}: channel \"{channel_name}\" disconnected")
678 }
679 }
680 }
681
682 Ok(())
683 }
684
685 fn on_header(
686 &mut self,
687 header: &VersionedBlockHeader,
688 bank_parent_slot: Slot,
689 ) -> Result<(), BlockComponentProcessorError> {
690 self.stage.on_header()?;
691
692 let VersionedBlockHeader::V1(header) = header;
693 if header.parent_slot != bank_parent_slot {
694 return Err(BlockComponentProcessorError::HeaderParentSlotMismatch {
695 header_parent_slot: header.parent_slot,
696 bank_parent_slot,
697 });
698 }
699 Ok(())
700 }
701
702 fn on_update_parent(
703 &mut self,
704 slot: Slot,
705 update_parent: &VersionedUpdateParent,
706 allow_initial_update_parent: bool,
707 ) -> Result<(), BlockComponentProcessorError> {
708 if leader_slot_index(slot) != 0 {
709 return Err(BlockComponentProcessorError::UpdateParentNotFirstInLeaderWindow(slot));
710 }
711
712 self.stage
713 .on_update_parent(update_parent, allow_initial_update_parent)
714 }
715
716 fn enforce_nanosecond_clock_bounds(
717 bank: &Bank,
718 parent_bank: &Bank,
719 footer: &BlockFooterV1,
720 ) -> Result<(), BlockComponentProcessorError> {
721 let parent_time_nanos = parent_bank
724 .get_nanosecond_clock()
725 .unwrap_or_else(|| bank.clock().unix_timestamp.saturating_mul(1_000_000_000));
726
727 let parent_slot = parent_bank.slot();
728 let current_time_nanos =
729 Self::block_producer_time_nanos_as_i64(footer.block_producer_time_nanos)?;
730 let current_slot = bank.slot();
731 let elapsed_slot_duration_nanos =
732 bank.slot_range_duration_nanos(parent_slot.saturating_add(1), current_slot);
733
734 let (lower_bound_nanos, upper_bound_nanos) =
735 Self::nanosecond_time_bounds(parent_time_nanos, elapsed_slot_duration_nanos);
736
737 let is_valid =
738 lower_bound_nanos <= current_time_nanos && current_time_nanos <= upper_bound_nanos;
739
740 match is_valid {
741 true => Ok(()),
742 false => Err(BlockComponentProcessorError::NanosecondClockOutOfBounds),
743 }
744 }
745
746 fn block_producer_time_nanos_as_i64(
753 block_producer_time_nanos: u64,
754 ) -> Result<i64, BlockComponentProcessorError> {
755 i64::try_from(block_producer_time_nanos)
756 .map_err(|_| BlockComponentProcessorError::NanosecondClockOutOfBounds)
757 }
758
759 pub fn nanosecond_time_bounds(
772 parent_time_nanos: i64,
773 elapsed_slot_duration_nanos: u128,
774 ) -> (i64, i64) {
775 let min_working_bank_time = parent_time_nanos.saturating_add(1);
776 let max_working_bank_time_offset = elapsed_slot_duration_nanos
777 .saturating_mul(2)
778 .min(i64::MAX as u128) as i64;
779 let max_working_bank_time = parent_time_nanos.saturating_add(max_working_bank_time_offset);
780
781 (min_working_bank_time, max_working_bank_time)
782 }
783
784 pub fn update_bank_with_footer_fields(
785 bank: &Bank,
786 block_producer_time_nanos: i64,
787 bank_hash: Option<Hash>,
788 reward_cert: Option<ValidatedRewardCert>,
789 final_cert_input: Option<(&HashSet<Pubkey>, Slot)>,
790 ) -> Result<VoteAccountUpdateStats, BankFooterError> {
791 bank.update_clock_from_footer(block_producer_time_nanos);
792 let (vote_accounts_updated, update_us) = measure_us!(calc_vote_rewards_update_vote_states(
793 bank,
794 reward_cert,
795 final_cert_input,
796 block_producer_time_nanos,
797 )?);
798
799 if let Some(hash) = bank_hash {
800 bank.set_expected_bank_hash(hash);
802 }
803 Ok(VoteAccountUpdateStats {
804 update_us,
805 vote_accounts_updated,
806 })
807 }
808}
809
810pub struct VoteAccountUpdateStats {
811 pub update_us: u64,
812 pub vote_accounts_updated: usize,
813}
814
815#[cfg(test)]
816mod tests {
817 use {
818 super::*,
819 crate::{
820 bank::{Bank, SlotLeader},
821 bank_forks::BankForks,
822 genesis_utils::{create_genesis_config, create_genesis_config_with_tower_leader},
823 },
824 agave_votor_messages::consensus_message::BlockId,
825 bytes::Bytes,
826 rand::Rng,
827 solana_bls_signatures::{BLS_SIGNATURE_AFFINE_SIZE, Signature as BLSSignature},
828 solana_clock::DEFAULT_MS_PER_SLOT,
829 solana_entry::block_component::{
830 BlockFooterV1, BlockHeaderV1, UpdateParentV1, VersionedUpdateParent,
831 },
832 solana_hash::Hash,
833 solana_leader_schedule::NUM_CONSECUTIVE_LEADER_SLOTS,
834 std::{
835 assert_matches,
836 sync::{Arc, RwLock},
837 },
838 test_case::test_case,
839 };
840
841 const DEFAULT_NS_PER_SLOT: u64 = DEFAULT_MS_PER_SLOT * 1_000_000;
842
843 fn create_test_bank() -> (Arc<Bank>, Arc<RwLock<BankForks>>) {
844 let genesis_config_info = create_genesis_config(10_000);
845 Bank::new_with_bank_forks_for_tests(&genesis_config_info.genesis_config)
846 }
847
848 fn create_test_bank_tower() -> (Arc<Bank>, Arc<RwLock<BankForks>>) {
849 let genesis_config_info =
850 create_genesis_config_with_tower_leader(10_000, &Pubkey::new_unique(), 0);
851 Bank::new_with_bank_forks_for_tests(&genesis_config_info.genesis_config)
852 }
853
854 fn create_child_bank(
855 bank_forks: &RwLock<BankForks>,
856 parent: &Arc<Bank>,
857 slot: u64,
858 ) -> Arc<Bank> {
859 Bank::new_from_parent_with_bank_forks(
860 bank_forks,
861 parent.clone(),
862 SlotLeader::new_unique(),
863 slot,
864 )
865 }
866
867 fn test_genesis_cert_marker() -> GenesisCertBlockMarker {
868 GenesisCertBlockMarker {
869 slot: 0,
870 block_id: BlockId::default(),
871 bls_signature: BLSSignature([0; BLS_SIGNATURE_AFFINE_SIZE]),
872 bitmap: vec![],
873 }
874 }
875
876 fn post_migration_status_with_genesis_slot(genesis_slot: Slot) -> MigrationStatus {
877 let migration_status = MigrationStatus::default();
878 let migration_slot = migration_status.record_feature_activation(0);
879 assert!(genesis_slot < migration_slot);
880
881 let genesis_block = Block::new_unique(genesis_slot);
882 migration_status.set_genesis_block(genesis_block);
883 let cert = Arc::new(GenesisCert {
884 block: genesis_block,
885 signature: CertSignature {
886 signature: BLSSignature([0; BLS_SIGNATURE_AFFINE_SIZE]),
887 bitmap: vec![],
888 },
889 });
890 migration_status.set_genesis_certificate(cert);
891 migration_status.enable_alpenglow_during_startup();
892
893 migration_status
894 }
895
896 fn processor_after_header() -> BlockComponentProcessor {
897 BlockComponentProcessor {
898 stage: BlockComponentStage::AcceptingGenesisOrEntries,
899 ..BlockComponentProcessor::default()
900 }
901 }
902
903 fn processor_after_footer() -> BlockComponentProcessor {
904 BlockComponentProcessor {
905 stage: BlockComponentStage::AcceptingAlpentick,
906 ..BlockComponentProcessor::default()
907 }
908 }
909
910 #[test]
911 fn test_first_alpenglow_block_requires_genesis_certificate_marker() {
912 let migration_status = post_migration_status_with_genesis_slot(1);
913 let processor = processor_after_footer();
914
915 let result = processor.on_final(&migration_status, 2, 1);
916 assert!(matches!(
917 result,
918 Err(BlockComponentProcessorError::MissingGenesisCertificateMarker)
919 ));
920 }
921
922 #[test]
923 fn test_first_alpenglow_block_with_genesis_certificate_marker_succeeds() {
924 let migration_status = post_migration_status_with_genesis_slot(1);
925 let (genesis_bank, bank_forks) = create_test_bank_tower();
926 let parent = create_child_bank(&bank_forks, &genesis_bank, 1);
927 let parent_block_id = BlockId::new_unique();
928 parent.set_block_id(Some(parent_block_id));
929 let bank = create_child_bank(&bank_forks, &parent, 2);
930 let genesis_marker = GenesisCertBlockMarker {
931 slot: parent.slot(),
932 block_id: parent_block_id,
933 bls_signature: BLSSignature([0; BLS_SIGNATURE_AFFINE_SIZE]),
934 bitmap: vec![],
935 };
936 let mut processor = processor_after_header();
937 bank.set_hashes_per_tick(Some(42));
938 assert!(bank.hashes_per_tick().is_some());
939
940 processor
941 .on_genesis_cert_block_marker_leader(bank.clone(), genesis_marker, &migration_status)
942 .unwrap();
943 assert!(bank.hashes_per_tick().is_none());
944 processor.stage = BlockComponentStage::Done;
945 assert!(processor.on_final(&migration_status, 2, 1).is_ok());
946 }
947
948 #[test]
949 fn test_genesis_certificate_marker_aborts_tower_bank_during_migration() {
950 let migration_status = MigrationStatus::default();
951 migration_status.record_feature_activation(0);
952 let (genesis_bank, bank_forks) = create_test_bank_tower();
953 let parent = create_child_bank(&bank_forks, &genesis_bank, 1);
954 let parent_block_id = BlockId::new_unique();
955 parent.set_block_id(Some(parent_block_id));
956 let bank = create_child_bank(&bank_forks, &parent, 2);
957 let genesis_marker = GenesisCertBlockMarker {
958 slot: parent.slot(),
959 block_id: parent_block_id,
960 bls_signature: BLSSignature([0; BLS_SIGNATURE_AFFINE_SIZE]),
961 bitmap: vec![],
962 };
963 let mut processor = processor_after_header();
964 bank.set_hashes_per_tick(Some(42));
965 let tower_hashes_per_tick = bank.hashes_per_tick();
966 assert!(tower_hashes_per_tick.is_some());
967
968 assert_matches!(
969 processor.on_genesis_cert_block_marker_leader(
970 bank.clone(),
971 genesis_marker,
972 &migration_status,
973 ),
974 Err(BlockComponentProcessorError::AlpenglowMigrationTransition)
975 );
976
977 assert!(migration_status.is_ready_to_enable());
978 assert_eq!(bank.hashes_per_tick(), tower_hashes_per_tick);
979 assert!(bank.get_alpenglow_genesis_certificate().is_some());
980 }
981
982 #[test]
983 fn test_on_footer_sets_timestamp() {
984 let my_pubkey = Pubkey::new_unique();
985 let mut processor = processor_after_header();
986
987 let (parent, bank_forks) = create_test_bank();
988 let bank = create_child_bank(&bank_forks, &parent, 1);
989 let shred_version = rand::rng().random();
990
991 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
993 let footer_time_nanos = parent_time_nanos + 200_000_000; let expected_time_secs = footer_time_nanos / 1_000_000_000;
995
996 let footer = VersionedBlockFooter::V1(BlockFooterV1 {
997 bank_hash: Hash::new_unique(),
998 block_producer_time_nanos: footer_time_nanos as u64,
999 block_user_agent: vec![],
1000 block_final_cert: None,
1001 skip_reward_cert: None,
1002 notar_reward_cert: None,
1003 });
1004 let bank_with_scheduler = BankWithScheduler::new_without_scheduler(bank.clone());
1005 processor
1006 .on_footer(
1007 &my_pubkey,
1008 &bank_with_scheduler,
1009 parent,
1010 shred_version,
1011 footer,
1012 None,
1013 &mut ExecuteTimings::default(),
1014 )
1015 .unwrap();
1016
1017 assert_eq!(processor.stage, BlockComponentStage::AcceptingAlpentick);
1018
1019 assert_eq!(bank.clock().unix_timestamp, expected_time_secs);
1021 }
1022
1023 #[test]
1024 fn test_footer_sets_epoch_start_timestamp_on_epoch_change() {
1025 let my_pubkey = Pubkey::new_unique();
1026 let mut processor = processor_after_header();
1027 let shred_version = rand::rng().random();
1028
1029 let genesis_config_info = create_genesis_config(10_000);
1031 let (genesis_bank, bank_forks) =
1032 Bank::new_with_bank_forks_for_tests(&genesis_config_info.genesis_config);
1033
1034 let epoch_schedule = genesis_bank.epoch_schedule();
1036 let first_slot_in_epoch_1 = epoch_schedule.get_first_slot_in_epoch(1);
1037
1038 let mut parent = genesis_bank.clone();
1040 for slot in 1..first_slot_in_epoch_1 {
1041 parent = create_child_bank(&bank_forks, &parent, slot);
1042 }
1043
1044 let bank = create_child_bank(&bank_forks, &parent, first_slot_in_epoch_1);
1046
1047 assert_eq!(bank.epoch(), 1);
1049
1050 let parent_slot = parent.slot();
1052 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1053 let current_slot = bank.slot();
1054 let elapsed_slot_duration_nanos =
1055 bank.slot_range_duration_nanos(parent_slot.saturating_add(1), current_slot);
1056
1057 let (lower_bound, upper_bound) = BlockComponentProcessor::nanosecond_time_bounds(
1059 parent_time_nanos,
1060 elapsed_slot_duration_nanos,
1061 );
1062 let footer_time_nanos = (lower_bound + upper_bound) / 2;
1063 let expected_time_secs = footer_time_nanos / 1_000_000_000;
1064
1065 let footer = VersionedBlockFooter::V1(BlockFooterV1 {
1066 bank_hash: Hash::new_unique(),
1067 block_producer_time_nanos: footer_time_nanos as u64,
1068 block_user_agent: vec![],
1069 block_final_cert: None,
1070 skip_reward_cert: None,
1071 notar_reward_cert: None,
1072 });
1073 let bank_with_scheduler = BankWithScheduler::new_without_scheduler(bank.clone());
1074 processor
1075 .on_footer(
1076 &my_pubkey,
1077 &bank_with_scheduler,
1078 parent,
1079 shred_version,
1080 footer,
1081 None,
1082 &mut ExecuteTimings::default(),
1083 )
1084 .unwrap();
1085
1086 assert_eq!(bank.clock().unix_timestamp, expected_time_secs);
1088
1089 assert_eq!(bank.clock().epoch_start_timestamp, expected_time_secs);
1091 }
1092
1093 #[test_case(1, |_, lower, _| lower, true; "at_minimum")]
1095 #[test_case(1, |_, _, upper| upper, true; "at_maximum")]
1096 #[test_case(1, |_, lower, _| lower - 1, false; "below_minimum")]
1097 #[test_case(1, |_, _, upper| upper + 1, false; "above_maximum")]
1098 #[test_case(5, |_, lower, _| lower + 2_000_000_000, true; "multi_slot_gap")]
1101 #[test_case(5, |_, _, upper| upper + 1_000_000_000, false; "multi_slot_gap_exceeds")]
1103 #[test_case(1, |parent_time, _, _| parent_time, false; "timestamp_equals_parent")]
1105 fn test_clock_bounds(
1106 slot_gap: u64,
1107 timestamp_fn: impl FnOnce(i64, i64, i64) -> i64,
1108 should_pass: bool,
1109 ) {
1110 let my_pubkey = Pubkey::new_unique();
1111 let mut processor = processor_after_header();
1112 let shred_version = rand::rng().random();
1113
1114 let (parent, bank_forks) = create_test_bank();
1115 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1116
1117 parent.update_clock_from_footer(parent_time_nanos);
1119
1120 let bank: Arc<Bank> = create_child_bank(&bank_forks, &parent, slot_gap);
1121 let elapsed_slot_duration_nanos = bank.slot_range_duration_nanos(1, slot_gap);
1122
1123 let (lower_bound, upper_bound) = BlockComponentProcessor::nanosecond_time_bounds(
1124 parent_time_nanos,
1125 elapsed_slot_duration_nanos,
1126 );
1127
1128 let footer_time_nanos = timestamp_fn(parent_time_nanos, lower_bound, upper_bound);
1129
1130 let footer = VersionedBlockFooter::V1(BlockFooterV1 {
1131 bank_hash: Hash::new_unique(),
1132 block_producer_time_nanos: footer_time_nanos as u64,
1133 block_user_agent: vec![],
1134 block_final_cert: None,
1135 skip_reward_cert: None,
1136 notar_reward_cert: None,
1137 });
1138 let bank = BankWithScheduler::new_without_scheduler(bank);
1139 let result = processor.on_footer(
1140 &my_pubkey,
1141 &bank,
1142 parent,
1143 shred_version,
1144 footer,
1145 None,
1146 &mut ExecuteTimings::default(),
1147 );
1148 if should_pass {
1149 result.unwrap();
1150 } else {
1151 assert!(matches!(
1152 result.unwrap_err(),
1153 BlockComponentProcessorError::NanosecondClockOutOfBounds
1154 ));
1155 }
1156 }
1157
1158 #[test]
1159 fn test_clock_bounds_without_parent_nanosecond_clock_rejects_out_of_bounds() {
1160 let my_pubkey = Pubkey::new_unique();
1161 let mut processor = processor_after_header();
1162 let shred_version = rand::rng().random();
1163
1164 let (parent, bank_forks) = create_test_bank();
1165 assert_eq!(parent.get_nanosecond_clock(), None);
1166
1167 let bank = create_child_bank(&bank_forks, &parent, 1);
1168 let parent_time_nanos = bank.clock().unix_timestamp.saturating_mul(1_000_000_000);
1169 let elapsed_slot_duration_nanos =
1170 bank.slot_range_duration_nanos(parent.slot().saturating_add(1), bank.slot());
1171 let (_, upper_bound) = BlockComponentProcessor::nanosecond_time_bounds(
1172 parent_time_nanos,
1173 elapsed_slot_duration_nanos,
1174 );
1175
1176 let footer = VersionedBlockFooter::V1(BlockFooterV1 {
1177 bank_hash: Hash::new_unique(),
1178 block_producer_time_nanos: u64::try_from(upper_bound.saturating_add(1)).unwrap(),
1179 block_user_agent: vec![],
1180 block_final_cert: None,
1181 skip_reward_cert: None,
1182 notar_reward_cert: None,
1183 });
1184 let bank = BankWithScheduler::new_without_scheduler(bank);
1185 assert!(matches!(
1186 processor
1187 .on_footer(
1188 &my_pubkey,
1189 &bank,
1190 parent,
1191 shred_version,
1192 footer,
1193 None,
1194 &mut ExecuteTimings::default(),
1195 )
1196 .unwrap_err(),
1197 BlockComponentProcessorError::NanosecondClockOutOfBounds
1198 ));
1199 }
1200
1201 #[test]
1202 fn test_clock_bounds_rejects_timestamp_above_i64() {
1203 let my_pubkey = Pubkey::new_unique();
1204 let mut processor = processor_after_header();
1205 let shred_version = rand::rng().random();
1206
1207 let (parent, bank_forks) = create_test_bank();
1208 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1209 parent.update_clock_from_footer(parent_time_nanos);
1210 let bank = create_child_bank(&bank_forks, &parent, 1);
1211
1212 let footer = VersionedBlockFooter::V1(BlockFooterV1 {
1213 bank_hash: Hash::new_unique(),
1214 block_producer_time_nanos: u64::MAX,
1215 block_user_agent: vec![],
1216 block_final_cert: None,
1217 skip_reward_cert: None,
1218 notar_reward_cert: None,
1219 });
1220 let bank = BankWithScheduler::new_without_scheduler(bank);
1221 assert!(matches!(
1222 processor
1223 .on_footer(
1224 &my_pubkey,
1225 &bank,
1226 parent,
1227 shred_version,
1228 footer,
1229 None,
1230 &mut ExecuteTimings::default(),
1231 )
1232 .unwrap_err(),
1233 BlockComponentProcessorError::NanosecondClockOutOfBounds
1234 ));
1235 }
1236
1237 fn test_nanosecond_time_bounds_helper(
1239 parent_time_nanos: i64,
1240 elapsed_slot_duration_nanos: u128,
1241 expected_lower: i64,
1242 expected_upper: i64,
1243 ) {
1244 let (lower, upper) = BlockComponentProcessor::nanosecond_time_bounds(
1245 parent_time_nanos,
1246 elapsed_slot_duration_nanos,
1247 );
1248
1249 assert_eq!(lower, expected_lower);
1250 assert_eq!(upper, expected_upper);
1251 }
1252
1253 #[test]
1254 fn test_nanosecond_time_bounds_calculation() {
1255 let parent_slot = 10;
1260 let parent_time = 1_000_000_000_000; let working_slot = 15;
1262 let slot_delta = working_slot - parent_slot;
1263 test_nanosecond_time_bounds_helper(
1264 parent_time,
1265 u128::from(slot_delta).saturating_mul(u128::from(DEFAULT_NS_PER_SLOT)),
1266 parent_time + 1,
1267 parent_time + (2 * DEFAULT_NS_PER_SLOT * slot_delta) as i64,
1268 );
1269 }
1270
1271 #[test]
1272 fn test_nanosecond_time_bounds_same_slot() {
1273 let parent_time = 1_000_000_000_000;
1280 test_nanosecond_time_bounds_helper(parent_time, 0, parent_time + 1, parent_time);
1281 }
1282
1283 #[test]
1284 fn test_nanosecond_time_bounds_saturates_upper_bound() {
1285 let parent_time = i64::MAX - 5;
1286 let (lower, upper) =
1287 BlockComponentProcessor::nanosecond_time_bounds(parent_time, u128::MAX);
1288
1289 assert_eq!(lower, parent_time + 1);
1290 assert_eq!(upper, i64::MAX);
1291 }
1292
1293 #[test]
1297 fn test_processor_component_sequences() {
1298 use BlockComponentProcessorError as E;
1299
1300 type Step = Box<
1301 dyn FnOnce(
1302 &mut BlockComponentProcessor,
1303 &MigrationStatus,
1304 ) -> Result<(), BlockComponentProcessorError>,
1305 >;
1306
1307 let post_migration = MigrationStatus::post_migration_status();
1308 let pre_migration = MigrationStatus::default();
1309 let in_migration = MigrationStatus::default();
1311 in_migration.record_feature_activation(0);
1312
1313 let (parent, bank_forks) = create_test_bank();
1314 let slot = NUM_CONSECUTIVE_LEADER_SLOTS.get() as Slot;
1316 let bank = create_child_bank(&bank_forks, &parent, slot);
1317 assert_eq!(leader_slot_index(slot), 0);
1318 let bank_not_window_start = create_child_bank(&bank_forks, &parent, slot + 1);
1321 assert_ne!(leader_slot_index(slot + 1), 0);
1322 let shred_version: u16 = rand::rng().random();
1323
1324 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1326 let footer_time_nanos = u64::try_from(parent_time_nanos + 400_000_000).unwrap();
1327
1328 let marker_step = {
1330 let (bank, parent) = (bank.clone(), parent.clone());
1331 move |marker: VersionedBlockMarker, allow_initial_update_parent: bool| -> Step {
1332 let (bank, parent) = (bank.clone(), parent.clone());
1333 Box::new(move |processor, migration_status| {
1334 let bank = BankWithScheduler::new_without_scheduler(bank);
1335 processor.on_marker(
1336 &bank,
1337 parent,
1338 shred_version,
1339 marker,
1340 allow_initial_update_parent,
1341 None,
1342 migration_status,
1343 &mut ExecuteTimings::default(),
1344 )
1345 })
1346 }
1347 };
1348 let batch_step = move |entries: Vec<EntryView<Bytes>>, is_final: bool| -> Step {
1350 Box::new(move |processor, migration_status| {
1351 processor.on_entry_batch(migration_status, slot, &entries, is_final)
1352 })
1353 };
1354
1355 let header_with_parent_slot = |parent_slot: Slot| {
1357 marker_step(
1358 VersionedBlockMarker::from_block_header(BlockHeaderV1 {
1359 parent_slot,
1360 parent_block_id: BlockId::default(),
1361 }),
1362 false,
1363 )
1364 };
1365 let header = || header_with_parent_slot(0);
1366 let genesis_cert = || {
1367 marker_step(
1368 VersionedBlockMarker::from_genesis_cert_block_marker(test_genesis_cert_marker()),
1369 false,
1370 )
1371 };
1372 let update_parent = |allow_initial_update_parent: bool| {
1373 marker_step(
1374 VersionedBlockMarker::from_update_parent(UpdateParentV1 {
1375 new_parent_slot: 0,
1376 new_parent_block_id: BlockId::default(),
1377 }),
1378 allow_initial_update_parent,
1379 )
1380 };
1381 let update_parent_not_window_start = {
1384 let (bank, parent) = (bank_not_window_start.clone(), parent.clone());
1385 move |allow_initial_update_parent: bool| -> Step {
1386 let (bank, parent) = (bank.clone(), parent.clone());
1387 Box::new(move |processor, migration_status| {
1388 let bank = BankWithScheduler::new_without_scheduler(bank);
1389 processor.on_marker(
1390 &bank,
1391 parent,
1392 shred_version,
1393 VersionedBlockMarker::from_update_parent(UpdateParentV1 {
1394 new_parent_slot: 0,
1395 new_parent_block_id: BlockId::default(),
1396 }),
1397 allow_initial_update_parent,
1398 None,
1399 migration_status,
1400 &mut ExecuteTimings::default(),
1401 )
1402 })
1403 }
1404 };
1405 let footer = || {
1406 marker_step(
1407 VersionedBlockMarker::from_block_footer(BlockFooterV1 {
1408 bank_hash: Hash::new_unique(),
1409 block_producer_time_nanos: footer_time_nanos,
1410 block_user_agent: vec![],
1411 block_final_cert: None,
1412 skip_reward_cert: None,
1413 notar_reward_cert: None,
1414 }),
1415 false,
1416 )
1417 };
1418 let entries = || {
1419 batch_step(
1420 vec![EntryView {
1421 num_hashes: 2,
1422 hash: Hash::default(),
1423 transactions: vec![],
1424 }],
1425 false,
1426 )
1427 };
1428 let final_entries = || {
1431 batch_step(
1432 vec![EntryView {
1433 num_hashes: 2,
1434 hash: Hash::default(),
1435 transactions: vec![],
1436 }],
1437 true,
1438 )
1439 };
1440 let tick = |is_final: bool| {
1443 batch_step(
1444 vec![EntryView {
1445 num_hashes: 1,
1446 hash: Hash::default(),
1447 transactions: vec![],
1448 }],
1449 is_final,
1450 )
1451 };
1452
1453 let abandoned = || {
1454 E::AbandonedBank(VersionedUpdateParent::V1(UpdateParentV1 {
1455 new_parent_slot: 0,
1456 new_parent_block_id: BlockId::default(),
1457 }))
1458 };
1459
1460 #[rustfmt::skip]
1461 let cases: Vec<(&MigrationStatus, Vec<Step>, Result<(), E>)> = vec![
1462 (&pre_migration, vec![entries(), tick(true)], Ok(())),
1464 (&pre_migration, vec![entries(), tick(false)], Ok(())),
1465 (&pre_migration, vec![tick(false)], Ok(())),
1466 (&pre_migration, vec![tick(true), entries()], Ok(())),
1467 (&pre_migration, vec![entries(), entries(), tick(false), tick(true)], Ok(())),
1468 (&pre_migration, vec![header()], Err(E::BlockComponentPreMigration)),
1469 (&pre_migration, vec![genesis_cert()], Err(E::BlockComponentPreMigration)),
1470 (&pre_migration, vec![footer()], Err(E::BlockComponentPreMigration)),
1471 (&pre_migration, vec![update_parent(false)], Err(E::BlockComponentPreMigration)),
1472 (&pre_migration, vec![update_parent(true)], Err(E::BlockComponentPreMigration)),
1473
1474 (&in_migration, vec![entries(), tick(true)], Ok(())),
1478 (&in_migration, vec![header()], Err(E::BlockComponentPreMigration)),
1479 (&in_migration, vec![genesis_cert()], Err(E::MissingParentMarker)),
1481 (&in_migration, vec![header(), genesis_cert()], Err(E::GenesisCertificateInAlpenglowCluster)),
1483 (&in_migration, vec![footer()], Err(E::BlockComponentPreMigration)),
1484 (&in_migration, vec![update_parent(false)], Err(E::BlockComponentPreMigration)),
1485 (&in_migration, vec![update_parent(true)], Err(E::BlockComponentPreMigration)),
1486
1487 (&post_migration, vec![header(), footer(), tick(true)], Ok(())),
1493 (&post_migration, vec![header(), entries(), entries(), footer(), tick(true)], Ok(())),
1494 (&post_migration, vec![update_parent(true), entries(), footer(), tick(true)], Ok(())),
1495 (&post_migration, vec![update_parent(true), footer(), tick(true)], Ok(())),
1496 (&post_migration, vec![header(), tick(false), footer(), tick(true)], Ok(())),
1498 (&post_migration, vec![update_parent(true), tick(false), footer(), tick(true)], Ok(())),
1499
1500 (&post_migration, vec![], Err(E::MissingBlockFooter)),
1502
1503 (&post_migration, vec![entries()], Err(E::MissingParentMarker)),
1505 (&post_migration, vec![genesis_cert()], Err(E::MissingParentMarker)),
1506 (&post_migration, vec![footer()], Err(E::MissingParentMarker)),
1507 (&post_migration, vec![tick(true)], Err(E::MissingParentMarker)),
1508 (&post_migration, vec![tick(false)], Err(E::MissingParentMarker)),
1509
1510 (&post_migration, vec![update_parent(false)], Err(E::UnexpectedInitialUpdateParent)),
1512
1513 (&post_migration, vec![header(), entries(), genesis_cert()], Err(E::GenesisCertificateOutOfOrder)),
1515 (&post_migration, vec![header(), footer(), genesis_cert()], Err(E::GenesisCertificateOutOfOrder)),
1516 (&post_migration, vec![update_parent(true), genesis_cert()], Err(E::GenesisCertificateOutOfOrder)),
1517 (&post_migration, vec![header(), genesis_cert()], Err(E::GenesisCertificateInAlpenglowCluster)),
1519
1520 (&post_migration, vec![header(), header()], Err(E::MultipleBlockHeaders)),
1522 (&post_migration, vec![header(), entries(), header()], Err(E::MultipleBlockHeaders)),
1523 (&post_migration, vec![header(), footer(), header()], Err(E::MultipleBlockHeaders)),
1524 (&post_migration, vec![update_parent(true), header()], Err(E::SpuriousUpdateParent)),
1525 (&post_migration, vec![update_parent(true), update_parent(true)], Err(E::MultipleUpdateParents)),
1526 (&post_migration, vec![update_parent(true), update_parent(false)], Err(E::MultipleUpdateParents)),
1527
1528 (&post_migration, vec![header_with_parent_slot(3)], Err(E::HeaderParentSlotMismatch { header_parent_slot: 3, bank_parent_slot: 0 })),
1530
1531 (&post_migration, vec![update_parent_not_window_start(true)], Err(E::UpdateParentNotFirstInLeaderWindow(5))),
1534 (&post_migration, vec![update_parent_not_window_start(false)], Err(E::UpdateParentNotFirstInLeaderWindow(5))),
1535
1536 (&post_migration, vec![header(), update_parent(false)], Err(abandoned())),
1539 (&post_migration, vec![header(), update_parent(true)], Err(abandoned())),
1540 (&post_migration, vec![header(), entries(), update_parent(false)], Err(abandoned())),
1541 (&post_migration, vec![header(), footer(), update_parent(false)], Err(E::SpuriousUpdateParent)),
1542 (&post_migration, vec![header(), footer(), update_parent(true)], Err(E::SpuriousUpdateParent)),
1543
1544 (&post_migration, vec![header(), tick(true)], Err(E::InvalidAlpentickPosition)),
1546 (&post_migration, vec![header(), entries(), tick(true)], Err(E::InvalidAlpentickPosition)),
1547 (&post_migration, vec![update_parent(true), tick(true)], Err(E::InvalidAlpentickPosition)),
1548
1549 (&post_migration, vec![header(), footer(), entries(), tick(true)], Err(E::EntryBatchAfterBlockFooter)),
1551 (&post_migration, vec![header(), footer(), footer()], Err(E::MultipleBlockFooters)),
1552 (&post_migration, vec![header(), footer(), tick(false), tick(true)], Err(E::EntryBatchAfterBlockFooter)),
1554 (&post_migration, vec![header(), footer(), final_entries()], Err(E::EntryBatchAfterBlockFooter)),
1556
1557 (&post_migration, vec![header(), footer(), tick(true), header()], Err(E::MultipleBlockHeaders)),
1559 (&post_migration, vec![header(), footer(), tick(true), genesis_cert()], Err(E::GenesisCertificateOutOfOrder)),
1560 (&post_migration, vec![header(), footer(), tick(true), entries()], Err(E::EntryBatchAfterBlockFooter)),
1561 (&post_migration, vec![header(), footer(), tick(true), tick(false)], Err(E::EntryBatchAfterBlockFooter)),
1562 (&post_migration, vec![header(), footer(), tick(true), tick(true)], Err(E::InvalidAlpentickPosition)),
1564 (&post_migration, vec![header(), footer(), tick(true), footer()], Err(E::MultipleBlockFooters)),
1565 (&post_migration, vec![header(), footer(), tick(true), update_parent(false)], Err(E::SpuriousUpdateParent)),
1566
1567 (&post_migration, vec![header()], Err(E::MissingBlockFooter)),
1569 (&post_migration, vec![header(), entries()], Err(E::MissingBlockFooter)),
1570 (&post_migration, vec![update_parent(true), entries()], Err(E::MissingBlockFooter)),
1571 (&post_migration, vec![header(), footer()], Err(E::InvalidAlpentickPosition)),
1573 ];
1574
1575 for (case_index, (migration_status, steps, expected)) in cases.into_iter().enumerate() {
1576 let mut processor = BlockComponentProcessor::default();
1577 let result = steps
1578 .into_iter()
1579 .try_for_each(|step| step(&mut processor, migration_status))
1580 .and_then(|()| processor.on_final(migration_status, slot, bank.parent_slot()));
1581
1582 match (result, expected) {
1583 (Ok(()), Ok(())) => (),
1584 (Err(err), Err(expected_err)) => {
1585 assert_eq!(
1586 std::mem::discriminant(&err),
1587 std::mem::discriminant(&expected_err),
1588 "case {case_index}: got {err:?}, expected {expected_err:?}"
1589 );
1590 }
1591 (result, expected) => {
1592 panic!("case {case_index}: got {result:?}, expected {expected:?}");
1593 }
1594 }
1595 }
1596 }
1597
1598 #[test]
1601 fn test_stage_transitions() {
1602 type Event =
1603 Box<dyn Fn(&mut BlockComponentStage) -> Result<(), BlockComponentProcessorError>>;
1604
1605 let update_parent_marker = VersionedUpdateParent::V1(UpdateParentV1 {
1606 new_parent_slot: 0,
1607 new_parent_block_id: BlockId::default(),
1608 });
1609 let abandoned =
1610 || BlockComponentProcessorError::AbandonedBank(update_parent_marker.clone());
1611
1612 let on_header = || -> Event { Box::new(|stage| stage.on_header()) };
1614 let on_genesis_certificate =
1615 || -> Event { Box::new(|stage| stage.on_genesis_certificate()) };
1616 let on_entry_batch = || -> Event { Box::new(|stage| stage.on_entry_batch()) };
1617 let on_update_parent = |allow_initial_update_parent: bool| -> Event {
1618 let marker = update_parent_marker.clone();
1619 Box::new(move |stage| stage.on_update_parent(&marker, allow_initial_update_parent))
1620 };
1621 let on_footer = || -> Event { Box::new(|stage| stage.on_footer()) };
1622 let on_alpentick = || -> Event { Box::new(|stage| stage.on_alpentick()) };
1623 let on_final = || -> Event { Box::new(|stage| stage.on_final()) };
1624
1625 let pre_parent_marker = BlockComponentStage::PreParentMarker;
1626 let genesis_or_entries = BlockComponentStage::AcceptingGenesisOrEntries;
1627 let entries_after_header = BlockComponentStage::AcceptingEntriesOrFooter {
1628 parent_marker: EntryParentMarker::BlockHeader,
1629 };
1630 let entries_after_update_parent = BlockComponentStage::AcceptingEntriesOrFooter {
1631 parent_marker: EntryParentMarker::UpdateParent,
1632 };
1633 let accepting_alpentick = BlockComponentStage::AcceptingAlpentick;
1634 let done = BlockComponentStage::Done;
1635
1636 #[rustfmt::skip]
1637 let cases: Vec<(&BlockComponentStage, Event, Result<BlockComponentStage, BlockComponentProcessorError>)> = vec![
1638 (&pre_parent_marker, on_header(), Ok(genesis_or_entries.clone())),
1640 (&pre_parent_marker, on_genesis_certificate(), Err(BlockComponentProcessorError::MissingParentMarker)),
1641 (&pre_parent_marker, on_entry_batch(), Err(BlockComponentProcessorError::MissingParentMarker)),
1642 (&pre_parent_marker, on_update_parent(false), Err(BlockComponentProcessorError::UnexpectedInitialUpdateParent)),
1643 (&pre_parent_marker, on_update_parent(true), Ok(entries_after_update_parent.clone())),
1644 (&pre_parent_marker, on_footer(), Err(BlockComponentProcessorError::MissingParentMarker)),
1645 (&pre_parent_marker, on_alpentick(), Err(BlockComponentProcessorError::MissingParentMarker)),
1646
1647 (&genesis_or_entries, on_header(), Err(BlockComponentProcessorError::MultipleBlockHeaders)),
1649 (&genesis_or_entries, on_genesis_certificate(), Ok(entries_after_header.clone())),
1650 (&genesis_or_entries, on_entry_batch(), Ok(entries_after_header.clone())),
1651 (&genesis_or_entries, on_update_parent(false), Err(abandoned())),
1652 (&genesis_or_entries, on_update_parent(true), Err(abandoned())),
1653 (&genesis_or_entries, on_footer(), Ok(accepting_alpentick.clone())),
1654 (&genesis_or_entries, on_alpentick(), Err(BlockComponentProcessorError::InvalidAlpentickPosition)),
1655
1656 (&entries_after_header, on_header(), Err(BlockComponentProcessorError::MultipleBlockHeaders)),
1658 (&entries_after_header, on_genesis_certificate(), Err(BlockComponentProcessorError::GenesisCertificateOutOfOrder)),
1659 (&entries_after_header, on_entry_batch(), Ok(entries_after_header.clone())),
1660 (&entries_after_header, on_update_parent(false), Err(abandoned())),
1661 (&entries_after_header, on_update_parent(true), Err(abandoned())),
1662 (&entries_after_header, on_footer(), Ok(accepting_alpentick.clone())),
1663 (&entries_after_header, on_alpentick(), Err(BlockComponentProcessorError::InvalidAlpentickPosition)),
1664
1665 (&entries_after_update_parent, on_header(), Err(BlockComponentProcessorError::SpuriousUpdateParent)),
1668 (&entries_after_update_parent, on_genesis_certificate(), Err(BlockComponentProcessorError::GenesisCertificateOutOfOrder)),
1669 (&entries_after_update_parent, on_entry_batch(), Ok(entries_after_update_parent.clone())),
1670 (&entries_after_update_parent, on_update_parent(false), Err(BlockComponentProcessorError::MultipleUpdateParents)),
1671 (&entries_after_update_parent, on_update_parent(true), Err(BlockComponentProcessorError::MultipleUpdateParents)),
1672 (&entries_after_update_parent, on_footer(), Ok(accepting_alpentick.clone())),
1673 (&entries_after_update_parent, on_alpentick(), Err(BlockComponentProcessorError::InvalidAlpentickPosition)),
1674
1675 (&accepting_alpentick, on_header(), Err(BlockComponentProcessorError::MultipleBlockHeaders)),
1677 (&accepting_alpentick, on_genesis_certificate(), Err(BlockComponentProcessorError::GenesisCertificateOutOfOrder)),
1678 (&accepting_alpentick, on_entry_batch(), Err(BlockComponentProcessorError::EntryBatchAfterBlockFooter)),
1679 (&accepting_alpentick, on_update_parent(false), Err(BlockComponentProcessorError::SpuriousUpdateParent)),
1680 (&accepting_alpentick, on_update_parent(true), Err(BlockComponentProcessorError::SpuriousUpdateParent)),
1681 (&accepting_alpentick, on_footer(), Err(BlockComponentProcessorError::MultipleBlockFooters)),
1682 (&accepting_alpentick, on_alpentick(), Ok(done.clone())),
1683
1684 (&done, on_header(), Err(BlockComponentProcessorError::MultipleBlockHeaders)),
1687 (&done, on_genesis_certificate(), Err(BlockComponentProcessorError::GenesisCertificateOutOfOrder)),
1688 (&done, on_entry_batch(), Err(BlockComponentProcessorError::EntryBatchAfterBlockFooter)),
1689 (&done, on_update_parent(false), Err(BlockComponentProcessorError::SpuriousUpdateParent)),
1690 (&done, on_update_parent(true), Err(BlockComponentProcessorError::SpuriousUpdateParent)),
1691 (&done, on_footer(), Err(BlockComponentProcessorError::MultipleBlockFooters)),
1692 (&done, on_alpentick(), Err(BlockComponentProcessorError::InvalidAlpentickPosition)),
1693
1694 (&pre_parent_marker, on_final(), Err(BlockComponentProcessorError::MissingBlockFooter)),
1696 (&genesis_or_entries, on_final(), Err(BlockComponentProcessorError::MissingBlockFooter)),
1697 (&entries_after_header, on_final(), Err(BlockComponentProcessorError::MissingBlockFooter)),
1698 (&entries_after_update_parent, on_final(), Err(BlockComponentProcessorError::MissingBlockFooter)),
1699 (&accepting_alpentick, on_final(), Err(BlockComponentProcessorError::InvalidAlpentickPosition)),
1700 (&done, on_final(), Ok(done.clone())),
1701 ];
1702
1703 for (case_index, (start, event, expected)) in cases.into_iter().enumerate() {
1704 let mut stage = start.clone();
1705 let result = event(&mut stage);
1706 match (result, expected) {
1707 (Ok(()), Ok(next)) => {
1708 assert_eq!(stage, next, "case {case_index}: from {start:?}");
1709 }
1710 (Err(err), Err(expected_err)) => {
1711 assert_eq!(
1712 std::mem::discriminant(&err),
1713 std::mem::discriminant(&expected_err),
1714 "case {case_index}: from {start:?}, got {err:?}, expected {expected_err:?}"
1715 );
1716 assert_eq!(&stage, start, "case {case_index}: stage changed on error");
1717 }
1718 (result, expected) => {
1719 panic!(
1720 "case {case_index}: from {start:?}, got {result:?}, expected {expected:?}"
1721 );
1722 }
1723 }
1724 }
1725 }
1726}