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