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