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_votor_messages::{
14 certificate::{CertSignature, Certificate, CertificateType, GenesisCert},
15 consensus_message::Block,
16 migration::MigrationStatus,
17 unverified_vote_message::UnverifiedCertificate,
18 },
19 crossbeam_channel::{Sender, TrySendError},
20 log::*,
21 smallvec::{SmallVec, smallvec},
22 solana_clock::Slot,
23 solana_entry::{
24 block_component::{
25 BlockFooterV1, BlockMarkerV1, GenesisCertBlockMarker, VersionedBlockFooter,
26 VersionedBlockHeader, VersionedBlockMarker, VersionedUpdateParent,
27 },
28 entry::Entry,
29 },
30 solana_hash::Hash,
31 solana_pubkey::Pubkey,
32 std::{collections::HashSet, sync::Arc},
33 thiserror::Error,
34};
35
36pub(crate) mod vote_reward;
37
38#[derive(Debug, Error)]
39pub enum BankFooterError {
40 #[error("calc vote rewards updating vote states failed with \"{0}\"")]
41 CalcVoteRewardUpdateVoteStates(#[from] CalcVoteRewardUpdateVoteStatesError),
42}
43
44#[derive(Debug, Error)]
45pub enum BlockComponentProcessorError {
46 #[error("BlockComponent detected pre-migration")]
47 BlockComponentPreMigration,
48 #[error("GenesisCertificate marker detected when GenesisCertificate is already populated")]
49 GenesisCertificateAlreadyPopulated,
50 #[error("GenesisCertificate marker detected when the cluster has Alpenglow enabled at slot 0")]
51 GenesisCertificateInAlpenglowCluster,
52 #[error("GenesisCertificate marker detected on a block which is not a child of genesis")]
53 GenesisCertificateOnNonChild,
54 #[error("GenesisCertificate was invalid and failed to verify")]
55 GenesisCertificateFailedVerification,
56 #[error("Alpenglow migration became ready; aborting the TowerBFT bank")]
57 AlpenglowMigrationTransition,
58 #[error("GenesisCertificate marker must immediately follow the block header")]
59 GenesisCertificateOutOfOrder,
60 #[error("FinalizationCertificate was invalid or failed to verify {0}")]
61 InvalidFinalizationCertificate(#[from] BlockFinalizationCertError),
62 #[error("Missing block footer")]
63 MissingBlockFooter,
64 #[error("Missing genesis certificate marker")]
65 MissingGenesisCertificateMarker,
66 #[error("Missing parent marker (neither a header nor an update parent was present)")]
67 MissingParentMarker,
68 #[error("Entry batch detected after block footer")]
69 EntryBatchAfterBlockFooter,
70 #[error("Alpentick must be the final block component and appear after block footer")]
71 InvalidAlpentickPosition,
72 #[error("Multiple block footers detected")]
73 MultipleBlockFooters,
74 #[error("Multiple block headers detected")]
75 MultipleBlockHeaders,
76 #[error(
77 "Block header parent slot mismatch: header={header_parent_slot}, bank={bank_parent_slot}"
78 )]
79 HeaderParentSlotMismatch {
80 header_parent_slot: Slot,
81 bank_parent_slot: Slot,
82 },
83 #[error("Multiple update parents detected")]
84 MultipleUpdateParents,
85 #[error("Nanosecond clock out of bounds")]
86 NanosecondClockOutOfBounds,
87 #[error("Spurious update parent")]
88 SpuriousUpdateParent,
89 #[error("UpdateParent marker is only valid in the first slot of a leader window: slot {0}")]
90 UpdateParentNotFirstInLeaderWindow(Slot),
91 #[error(
92 "UpdateParent cannot be the initial parent marker unless replay starts at UpdateParent"
93 )]
94 UnexpectedInitialUpdateParent,
95 #[error("Abandoned bank")]
96 AbandonedBank(VersionedUpdateParent),
97 #[error("invalid reward certs {0}")]
98 InvalidRewardCerts(#[from] ValidatedRewardCertError),
99 #[error("updating bank footer failed with \"{0}\"")]
100 UpdateBankFooter(#[from] BankFooterError),
101}
102
103impl BlockComponentProcessorError {
104 pub fn is_update_parent_recoverable_replay_error(&self) -> bool {
110 match self {
111 BlockComponentProcessorError::MissingParentMarker
112 | BlockComponentProcessorError::EntryBatchAfterBlockFooter
113 | BlockComponentProcessorError::InvalidAlpentickPosition
114 | BlockComponentProcessorError::MultipleBlockFooters
115 | BlockComponentProcessorError::MultipleBlockHeaders
116 | BlockComponentProcessorError::HeaderParentSlotMismatch { .. }
117 | BlockComponentProcessorError::NanosecondClockOutOfBounds
118 | BlockComponentProcessorError::UnexpectedInitialUpdateParent
119 | BlockComponentProcessorError::GenesisCertificateOutOfOrder
120 | BlockComponentProcessorError::GenesisCertificateAlreadyPopulated
121 | BlockComponentProcessorError::GenesisCertificateInAlpenglowCluster
122 | BlockComponentProcessorError::GenesisCertificateOnNonChild
123 | BlockComponentProcessorError::GenesisCertificateFailedVerification
124 | BlockComponentProcessorError::SpuriousUpdateParent
125 | BlockComponentProcessorError::AbandonedBank(_)
126 | BlockComponentProcessorError::InvalidRewardCerts(_)
127 | BlockComponentProcessorError::UpdateBankFooter(_)
128 | BlockComponentProcessorError::InvalidFinalizationCertificate(_) => true,
129 BlockComponentProcessorError::BlockComponentPreMigration
130 | BlockComponentProcessorError::MissingBlockFooter
131 | BlockComponentProcessorError::MissingGenesisCertificateMarker
132 | BlockComponentProcessorError::MultipleUpdateParents
133 | BlockComponentProcessorError::AlpenglowMigrationTransition
134 | BlockComponentProcessorError::UpdateParentNotFirstInLeaderWindow(_) => false,
135 }
136 }
137}
138
139#[derive(Debug, Clone, PartialEq, Eq)]
140enum EntryParentMarker {
142 BlockHeader,
143 UpdateParent,
144}
145
146#[derive(Default, Debug, Clone, PartialEq, Eq)]
147enum BlockComponentStage {
162 #[default]
163 PreParentMarker,
165 AcceptingGenesisOrEntries,
167 AcceptingEntriesOrFooter { parent_marker: EntryParentMarker },
170 AcceptingAlpentick,
172 Done,
174}
175
176impl BlockComponentStage {
177 fn on_header(&mut self) -> Result<(), BlockComponentProcessorError> {
179 match self {
180 Self::PreParentMarker => {
181 *self = Self::AcceptingGenesisOrEntries;
182 Ok(())
183 }
184 Self::AcceptingGenesisOrEntries
185 | Self::AcceptingEntriesOrFooter {
186 parent_marker: EntryParentMarker::BlockHeader,
187 }
188 | Self::AcceptingAlpentick
189 | Self::Done => Err(BlockComponentProcessorError::MultipleBlockHeaders),
190 Self::AcceptingEntriesOrFooter {
191 parent_marker: EntryParentMarker::UpdateParent,
192 } => Err(BlockComponentProcessorError::SpuriousUpdateParent),
193 }
194 }
195
196 fn on_genesis_certificate(&mut self) -> Result<(), BlockComponentProcessorError> {
198 match self {
199 Self::PreParentMarker => Err(BlockComponentProcessorError::MissingParentMarker),
200 Self::AcceptingGenesisOrEntries => {
201 *self = Self::AcceptingEntriesOrFooter {
202 parent_marker: EntryParentMarker::BlockHeader,
203 };
204 Ok(())
205 }
206 Self::AcceptingEntriesOrFooter { .. } | Self::AcceptingAlpentick | Self::Done => {
207 Err(BlockComponentProcessorError::GenesisCertificateOutOfOrder)
208 }
209 }
210 }
211
212 fn on_entry_batch(&mut self) -> Result<(), BlockComponentProcessorError> {
215 match self {
216 Self::PreParentMarker => Err(BlockComponentProcessorError::MissingParentMarker),
217 Self::AcceptingGenesisOrEntries => {
218 *self = Self::AcceptingEntriesOrFooter {
219 parent_marker: EntryParentMarker::BlockHeader,
220 };
221 Ok(())
222 }
223 Self::AcceptingEntriesOrFooter { .. } => Ok(()),
224 Self::AcceptingAlpentick | Self::Done => {
225 Err(BlockComponentProcessorError::EntryBatchAfterBlockFooter)
226 }
227 }
228 }
229
230 fn on_update_parent(
234 &mut self,
235 update_parent: &VersionedUpdateParent,
236 allow_initial_update_parent: bool,
237 ) -> Result<(), BlockComponentProcessorError> {
238 match self {
239 Self::PreParentMarker => {
240 if !allow_initial_update_parent {
241 return Err(BlockComponentProcessorError::UnexpectedInitialUpdateParent);
242 }
243 *self = Self::AcceptingEntriesOrFooter {
244 parent_marker: EntryParentMarker::UpdateParent,
245 };
246 Ok(())
247 }
248 Self::AcceptingGenesisOrEntries
249 | Self::AcceptingEntriesOrFooter {
250 parent_marker: EntryParentMarker::BlockHeader,
251 } => {
252 Err(BlockComponentProcessorError::AbandonedBank(
256 update_parent.clone(),
257 ))
258 }
259 Self::AcceptingEntriesOrFooter {
260 parent_marker: EntryParentMarker::UpdateParent,
261 } => Err(BlockComponentProcessorError::MultipleUpdateParents),
262 Self::AcceptingAlpentick | BlockComponentStage::Done => {
263 Err(BlockComponentProcessorError::SpuriousUpdateParent)
264 }
265 }
266 }
267
268 fn on_footer(&mut self) -> Result<(), BlockComponentProcessorError> {
271 match self {
272 Self::PreParentMarker => Err(BlockComponentProcessorError::MissingParentMarker),
273 Self::AcceptingGenesisOrEntries | Self::AcceptingEntriesOrFooter { .. } => {
274 *self = Self::AcceptingAlpentick;
275 Ok(())
276 }
277 Self::AcceptingAlpentick | Self::Done => {
278 Err(BlockComponentProcessorError::MultipleBlockFooters)
279 }
280 }
281 }
282
283 fn on_alpentick(&mut self) -> Result<(), BlockComponentProcessorError> {
285 match self {
286 Self::PreParentMarker => Err(BlockComponentProcessorError::MissingParentMarker),
287 Self::AcceptingGenesisOrEntries => {
288 Err(BlockComponentProcessorError::InvalidAlpentickPosition)
289 }
290 Self::AcceptingEntriesOrFooter { .. } => {
291 Err(BlockComponentProcessorError::InvalidAlpentickPosition)
292 }
293 Self::AcceptingAlpentick => {
294 *self = Self::Done;
295 Ok(())
296 }
297 Self::Done => Err(BlockComponentProcessorError::InvalidAlpentickPosition),
298 }
299 }
300
301 fn on_final(&self) -> Result<(), BlockComponentProcessorError> {
303 match self {
304 Self::Done => Ok(()),
305 Self::AcceptingAlpentick => Err(BlockComponentProcessorError::InvalidAlpentickPosition),
306 Self::PreParentMarker
307 | Self::AcceptingGenesisOrEntries
308 | Self::AcceptingEntriesOrFooter { .. } => {
309 Err(BlockComponentProcessorError::MissingBlockFooter)
310 }
311 }
312 }
313}
314
315#[derive(Default)]
316pub struct BlockComponentProcessor {
317 stage: BlockComponentStage,
318 has_genesis_certificate_marker: bool,
319}
320
321impl BlockComponentProcessor {
322 pub fn on_final(
323 &self,
324 migration_status: &MigrationStatus,
325 slot: Slot,
326 parent_slot: Slot,
327 ) -> Result<(), BlockComponentProcessorError> {
328 if !migration_status.should_allow_block_markers(slot) {
331 if self.stage == BlockComponentStage::PreParentMarker {
332 return Ok(());
333 } else {
334 return Err(BlockComponentProcessorError::BlockComponentPreMigration);
335 };
336 }
337
338 if Self::requires_genesis_certificate_marker(migration_status, parent_slot)
339 && !self.has_genesis_certificate_marker
340 {
341 return Err(BlockComponentProcessorError::MissingGenesisCertificateMarker);
342 }
343
344 self.stage.on_final()
345 }
346
347 fn requires_genesis_certificate_marker(
353 migration_status: &MigrationStatus,
354 parent_slot: Slot,
355 ) -> bool {
356 migration_status
357 .genesis_block()
358 .is_some_and(|genesis_block| {
359 genesis_block.slot != 0 && parent_slot == genesis_block.slot
360 })
361 }
362
363 pub fn on_entry_batch(
369 &mut self,
370 migration_status: &MigrationStatus,
371 slot: Slot,
372 entries: &[Entry],
373 is_final_component: bool,
374 ) -> Result<(), BlockComponentProcessorError> {
375 if !migration_status.should_allow_block_markers(slot) {
376 return Ok(());
377 }
378
379 let is_alpentick = is_final_component
383 && matches!(entries, [entry] if entry.is_tick() && entry.num_hashes == 1);
384
385 if is_alpentick {
386 self.stage.on_alpentick()
387 } else {
388 self.stage.on_entry_batch()
389 }
390 }
391
392 pub fn on_marker(
399 &mut self,
400 bank: Arc<Bank>,
401 parent_bank: Arc<Bank>,
402 shred_version: u16,
403 marker: VersionedBlockMarker,
404 allow_initial_update_parent: bool,
405 finalization_cert_sender: Option<&Sender<SmallVec<[Certificate; 2]>>>,
406 migration_status: &MigrationStatus,
407 ) -> Result<(), BlockComponentProcessorError> {
408 let slot = bank.slot();
409 let VersionedBlockMarker::V1(marker) = marker;
410
411 let markers_fully_enabled = migration_status.should_allow_block_markers(slot);
412 let in_migration = migration_status.is_in_migration();
413 let fast_leader_handover_active =
414 bank.feature_set.snapshot().alpenglow_fast_leader_handover;
415
416 match marker {
417 BlockMarkerV1::BlockHeader(header) if markers_fully_enabled || in_migration => {
421 self.on_header(header.inner(), bank.parent_slot())
422 }
423 BlockMarkerV1::GenesisCertificate(genesis_cert_block_marker)
424 if markers_fully_enabled || in_migration =>
425 {
426 self.on_genesis_cert_block_marker(
427 bank,
428 shred_version,
429 genesis_cert_block_marker.into_inner(),
430 migration_status,
431 )
432 }
433
434 BlockMarkerV1::BlockFooter(footer) if markers_fully_enabled => self.on_footer(
436 &migration_status.my_pubkey(),
437 bank,
438 parent_bank,
439 shred_version,
440 footer.into_inner(),
441 finalization_cert_sender,
442 ),
443
444 BlockMarkerV1::UpdateParent(update_parent) if markers_fully_enabled => {
445 if fast_leader_handover_active {
446 self.on_update_parent(slot, update_parent.inner(), allow_initial_update_parent)
447 } else {
448 Err(BlockComponentProcessorError::SpuriousUpdateParent)
449 }
450 }
451
452 _ => Err(BlockComponentProcessorError::BlockComponentPreMigration),
454 }
455 }
456
457 pub fn on_genesis_cert_block_marker(
459 &mut self,
460 bank: Arc<Bank>,
461 shred_version: u16,
462 genesis_block_marker: GenesisCertBlockMarker,
463 migration_status: &MigrationStatus,
464 ) -> Result<(), BlockComponentProcessorError> {
465 self.stage.on_genesis_certificate()?;
466 self.process_unvalidated_genesis_cert_block_marker(
467 bank,
468 genesis_block_marker,
469 migration_status,
470 Some(shred_version),
471 )?;
472 Ok(())
473 }
474
475 pub fn on_genesis_cert_block_marker_leader(
477 &mut self,
478 bank: Arc<Bank>,
479 genesis_block_marker: GenesisCertBlockMarker,
480 migration_status: &MigrationStatus,
481 ) -> Result<(), BlockComponentProcessorError> {
482 self.process_unvalidated_genesis_cert_block_marker(
483 bank,
484 genesis_block_marker,
485 migration_status,
486 None,
487 )?;
488 Ok(())
489 }
490
491 fn process_unvalidated_genesis_cert_block_marker(
493 &mut self,
494 bank: Arc<Bank>,
495 genesis_block_marker: GenesisCertBlockMarker,
496 migration_status: &MigrationStatus,
497 shred_version: Option<u16>,
498 ) -> Result<(), BlockComponentProcessorError> {
499 if bank.parent_slot() == 0 {
501 return Err(BlockComponentProcessorError::GenesisCertificateInAlpenglowCluster);
502 }
503
504 let parent_block_id = bank
505 .parent_block_id()
506 .expect("Block id is populated for all slots > 0");
507 if (bank.parent_slot(), parent_block_id)
508 != (genesis_block_marker.slot, genesis_block_marker.block_id)
509 {
510 return Err(BlockComponentProcessorError::GenesisCertificateOnNonChild);
511 }
512
513 if bank.get_alpenglow_genesis_certificate().is_some() {
514 return Err(BlockComponentProcessorError::GenesisCertificateAlreadyPopulated);
515 }
516
517 let genesis_cert = GenesisCert {
518 block: Block {
519 slot: genesis_block_marker.slot,
520 block_id: genesis_block_marker.block_id,
521 },
522 signature: CertSignature {
523 signature: genesis_block_marker.bls_signature,
524 bitmap: genesis_block_marker.bitmap,
525 },
526 };
527 if let Some(shred_version) = shred_version {
528 Self::verify_genesis_certificate(&bank, &genesis_cert, shred_version)?;
529 }
530
531 bank.set_alpenglow_genesis_certificate(&genesis_cert);
532 self.has_genesis_certificate_marker = true;
533
534 if migration_status.is_alpenglow_enabled() {
535 bank.set_hashes_per_tick(None);
537 return Ok(());
538 }
539
540 warn!(
546 "{}: Alpenglow genesis marker processed during replay of {}. Transitioning Alpenglow \
547 to ReadyToEnable",
548 migration_status.my_pubkey(),
549 bank.slot()
550 );
551 migration_status.set_genesis_block(genesis_cert.block);
552 migration_status.set_genesis_certificate(Arc::new(genesis_cert));
553 assert!(migration_status.is_ready_to_enable());
554
555 Err(BlockComponentProcessorError::AlpenglowMigrationTransition)
558 }
559
560 fn verify_genesis_certificate(
561 bank: &Bank,
562 cert: &GenesisCert,
563 shred_version: u16,
564 ) -> Result<(), BlockComponentProcessorError> {
565 let cert_slot = cert.block.slot;
566 let unverified_cert = UnverifiedCertificate {
567 cert_type: CertificateType::Genesis(cert.block),
568 signature: cert.signature.signature,
569 bitmap: cert.signature.bitmap.clone(),
570 shred_version,
571 };
572 bank.verify_certificate(unverified_cert, None)
573 .map_err(|_| {
574 warn!(
575 "Failed to verify genesis certificate for slot {cert_slot} in bank slot {}",
576 bank.slot()
577 );
578 BlockComponentProcessorError::GenesisCertificateFailedVerification
579 })?;
580
581 Ok(())
582 }
583
584 fn on_footer(
585 &mut self,
586 my_pubkey: &Pubkey,
587 bank: Arc<Bank>,
588 parent_bank: Arc<Bank>,
589 shred_version: u16,
590 footer: VersionedBlockFooter,
591 finalization_cert_sender: Option<&Sender<SmallVec<[Certificate; 2]>>>,
592 ) -> Result<(), BlockComponentProcessorError> {
593 self.stage.on_footer()?;
594
595 let VersionedBlockFooter::V1(footer) = footer;
596
597 Self::enforce_nanosecond_clock_bounds(&bank, &parent_bank, &footer)?;
598
599 let BlockFooterV1 {
600 bank_hash,
601 block_producer_time_nanos,
602 block_user_agent: _,
603 block_final_cert,
604 skip_reward_cert,
605 notar_reward_cert,
606 } = footer;
607
608 let reward_cert = ValidatedRewardCert::try_new(
609 &bank,
610 shred_version,
611 &skip_reward_cert,
612 ¬ar_reward_cert,
613 )?;
614 let block_producer_time_nanos =
615 Self::block_producer_time_nanos_as_i64(block_producer_time_nanos)?;
616 let final_cert = block_final_cert
617 .map(|final_cert| {
618 ValidatedBlockFinalizationCert::try_from_footer(final_cert, &bank, shred_version)
619 .map_err(BlockComponentProcessorError::InvalidFinalizationCertificate)
620 })
621 .transpose()?;
622
623 let (footer_input, pool_input) = match final_cert {
624 None => (None, None),
625 Some(cert) => {
626 let (signers, finalize_cert, notarize_cert) = cert.into_parts();
627 let final_slot = finalize_cert.cert_type.slot();
628 (
629 Some((signers, final_slot)),
630 Some((finalize_cert, notarize_cert)),
631 )
632 }
633 };
634
635 Self::update_bank_with_footer_fields(
636 &bank,
637 block_producer_time_nanos,
638 Some(bank_hash),
639 reward_cert,
640 footer_input
641 .as_ref()
642 .map(|(validators, slot)| (validators, *slot)),
643 )?;
644
645 if let Some((finalize_cert, notarize_cert)) = pool_input
647 && let Some(sender) = finalization_cert_sender
648 {
649 let channel_name = "finalization_cert_sender";
650 let certs = match notarize_cert {
651 None => smallvec![finalize_cert],
652 Some(c) => smallvec![finalize_cert, c],
653 };
654 match sender.try_send(certs) {
655 Ok(()) => (),
656 Err(TrySendError::Full(_)) => {
657 warn!("{my_pubkey}: channel \"{channel_name}\" is full, dropping msg")
658 }
659 Err(TrySendError::Disconnected(_)) => {
660 warn!("{my_pubkey}: channel \"{channel_name}\" disconnected")
661 }
662 }
663 }
664
665 Ok(())
666 }
667
668 fn on_header(
669 &mut self,
670 header: &VersionedBlockHeader,
671 bank_parent_slot: Slot,
672 ) -> Result<(), BlockComponentProcessorError> {
673 self.stage.on_header()?;
674
675 let VersionedBlockHeader::V1(header) = header;
676 if header.parent_slot != bank_parent_slot {
677 return Err(BlockComponentProcessorError::HeaderParentSlotMismatch {
678 header_parent_slot: header.parent_slot,
679 bank_parent_slot,
680 });
681 }
682 Ok(())
683 }
684
685 fn on_update_parent(
686 &mut self,
687 slot: Slot,
688 update_parent: &VersionedUpdateParent,
689 allow_initial_update_parent: bool,
690 ) -> Result<(), BlockComponentProcessorError> {
691 if leader_slot_index(slot) != 0 {
692 return Err(BlockComponentProcessorError::UpdateParentNotFirstInLeaderWindow(slot));
693 }
694
695 self.stage
696 .on_update_parent(update_parent, allow_initial_update_parent)
697 }
698
699 fn enforce_nanosecond_clock_bounds(
700 bank: &Bank,
701 parent_bank: &Bank,
702 footer: &BlockFooterV1,
703 ) -> Result<(), BlockComponentProcessorError> {
704 let parent_time_nanos = parent_bank
707 .get_nanosecond_clock()
708 .unwrap_or_else(|| bank.clock().unix_timestamp.saturating_mul(1_000_000_000));
709
710 let parent_slot = parent_bank.slot();
711 let current_time_nanos =
712 Self::block_producer_time_nanos_as_i64(footer.block_producer_time_nanos)?;
713 let current_slot = bank.slot();
714 let elapsed_slot_duration_nanos =
715 bank.slot_range_duration_nanos(parent_slot.saturating_add(1), current_slot);
716
717 let (lower_bound_nanos, upper_bound_nanos) =
718 Self::nanosecond_time_bounds(parent_time_nanos, elapsed_slot_duration_nanos);
719
720 let is_valid =
721 lower_bound_nanos <= current_time_nanos && current_time_nanos <= upper_bound_nanos;
722
723 match is_valid {
724 true => Ok(()),
725 false => Err(BlockComponentProcessorError::NanosecondClockOutOfBounds),
726 }
727 }
728
729 fn block_producer_time_nanos_as_i64(
736 block_producer_time_nanos: u64,
737 ) -> Result<i64, BlockComponentProcessorError> {
738 i64::try_from(block_producer_time_nanos)
739 .map_err(|_| BlockComponentProcessorError::NanosecondClockOutOfBounds)
740 }
741
742 pub fn nanosecond_time_bounds(
755 parent_time_nanos: i64,
756 elapsed_slot_duration_nanos: u128,
757 ) -> (i64, i64) {
758 let min_working_bank_time = parent_time_nanos.saturating_add(1);
759 let max_working_bank_time_offset = elapsed_slot_duration_nanos
760 .saturating_mul(2)
761 .min(i64::MAX as u128) as i64;
762 let max_working_bank_time = parent_time_nanos.saturating_add(max_working_bank_time_offset);
763
764 (min_working_bank_time, max_working_bank_time)
765 }
766
767 pub fn update_bank_with_footer_fields(
768 bank: &Bank,
769 block_producer_time_nanos: i64,
770 bank_hash: Option<Hash>,
771 reward_cert: Option<ValidatedRewardCert>,
772 final_cert_input: Option<(&HashSet<Pubkey>, Slot)>,
773 ) -> Result<(), BankFooterError> {
774 bank.update_clock_from_footer(block_producer_time_nanos);
775 calc_vote_rewards_update_vote_states(
776 bank,
777 reward_cert,
778 final_cert_input,
779 block_producer_time_nanos,
780 )?;
781
782 if let Some(hash) = bank_hash {
783 bank.set_expected_bank_hash(hash);
785 }
786 Ok(())
787 }
788}
789
790#[cfg(test)]
791mod tests {
792 use {
793 super::*,
794 crate::{
795 bank::{Bank, SlotLeader},
796 bank_forks::BankForks,
797 genesis_utils::{activate_all_features_alpenglow, create_genesis_config},
798 },
799 rand::Rng,
800 solana_bls_signatures::{BLS_SIGNATURE_AFFINE_SIZE, Signature as BLSSignature},
801 solana_clock::DEFAULT_MS_PER_SLOT,
802 solana_entry::{
803 block_component::{
804 BlockFooterV1, BlockHeaderV1, UpdateParentV1, VersionedUpdateParent,
805 },
806 entry::Entry,
807 },
808 solana_hash::Hash,
809 std::{
810 assert_matches,
811 sync::{Arc, RwLock},
812 },
813 };
814
815 const DEFAULT_NS_PER_SLOT: u64 = DEFAULT_MS_PER_SLOT * 1_000_000;
816
817 fn create_test_bank() -> (Arc<Bank>, Arc<RwLock<BankForks>>) {
818 let genesis_config_info = create_genesis_config(10_000);
819 Bank::new_with_bank_forks_for_tests(&genesis_config_info.genesis_config)
820 }
821
822 fn create_test_bank_alpenglow() -> (Arc<Bank>, Arc<RwLock<BankForks>>) {
823 let mut genesis_config_info = create_genesis_config(10_000);
824 activate_all_features_alpenglow(&mut genesis_config_info.genesis_config);
825 Bank::new_with_bank_forks_for_tests(&genesis_config_info.genesis_config)
826 }
827
828 fn create_child_bank(
829 bank_forks: &RwLock<BankForks>,
830 parent: &Arc<Bank>,
831 slot: u64,
832 ) -> Arc<Bank> {
833 Bank::new_from_parent_with_bank_forks(
834 bank_forks,
835 parent.clone(),
836 SlotLeader::new_unique(),
837 slot,
838 )
839 }
840
841 fn test_genesis_cert_marker() -> GenesisCertBlockMarker {
842 GenesisCertBlockMarker {
843 slot: 0,
844 block_id: Hash::default(),
845 bls_signature: BLSSignature([0; BLS_SIGNATURE_AFFINE_SIZE]),
846 bitmap: vec![],
847 }
848 }
849
850 fn post_migration_status_with_genesis_slot(genesis_slot: Slot) -> MigrationStatus {
851 let migration_status = MigrationStatus::default();
852 let migration_slot = migration_status.record_feature_activation(0);
853 assert!(genesis_slot < migration_slot);
854
855 let genesis_block = Block {
856 slot: genesis_slot,
857 block_id: Hash::default(),
858 };
859 migration_status.set_genesis_block(genesis_block);
860 let cert = Arc::new(GenesisCert {
861 block: genesis_block,
862 signature: CertSignature {
863 signature: BLSSignature([0; BLS_SIGNATURE_AFFINE_SIZE]),
864 bitmap: vec![],
865 },
866 });
867 migration_status.set_genesis_certificate(cert);
868 migration_status.enable_alpenglow_during_startup();
869
870 migration_status
871 }
872
873 fn processor_after_header() -> BlockComponentProcessor {
874 BlockComponentProcessor {
875 stage: BlockComponentStage::AcceptingGenesisOrEntries,
876 ..BlockComponentProcessor::default()
877 }
878 }
879
880 fn processor_after_footer() -> BlockComponentProcessor {
881 BlockComponentProcessor {
882 stage: BlockComponentStage::AcceptingAlpentick,
883 ..BlockComponentProcessor::default()
884 }
885 }
886
887 fn processor_done() -> BlockComponentProcessor {
888 BlockComponentProcessor {
889 stage: BlockComponentStage::Done,
890 ..BlockComponentProcessor::default()
891 }
892 }
893
894 fn alpentick(num_hashes: u64) -> [Entry; 1] {
895 [Entry::new(&Hash::default(), num_hashes, vec![])]
896 }
897
898 #[test]
899 fn test_missing_header_error_on_entry_batch() {
900 let migration_status = MigrationStatus::post_migration_status();
901 let mut processor = BlockComponentProcessor::default();
902
903 let result = processor.on_entry_batch(&migration_status, 1, &[], false);
905 assert!(matches!(
906 result,
907 Err(BlockComponentProcessorError::MissingParentMarker)
908 ));
909 }
910
911 #[test]
912 fn test_missing_header_error_on_genesis_certificate() {
913 let migration_status = MigrationStatus::post_migration_status();
914 let mut processor = BlockComponentProcessor::default();
915 let marker =
916 VersionedBlockMarker::from_genesis_cert_block_marker(test_genesis_cert_marker());
917
918 let (parent, bank_forks) = create_test_bank();
919 let bank = create_child_bank(&bank_forks, &parent, 1);
920 let shred_version = rand::rng().random();
921
922 let result = processor.on_marker(
923 bank,
924 parent,
925 shred_version,
926 marker,
927 false,
928 None,
929 &migration_status,
930 );
931 assert!(matches!(
932 result,
933 Err(BlockComponentProcessorError::MissingParentMarker)
934 ));
935 }
936
937 #[test]
938 fn test_genesis_certificate_after_entry_batch_errors() {
939 let migration_status = MigrationStatus::post_migration_status();
940 let mut processor = BlockComponentProcessor::default();
941 let (parent, bank_forks) = create_test_bank();
942 let bank = create_child_bank(&bank_forks, &parent, 1);
943 let shred_version = rand::rng().random();
944
945 let header = VersionedBlockMarker::from_block_header(BlockHeaderV1 {
946 parent_slot: 0,
947 parent_block_id: Hash::default(),
948 });
949 processor
950 .on_marker(
951 bank.clone(),
952 parent.clone(),
953 shred_version,
954 header,
955 false,
956 None,
957 &migration_status,
958 )
959 .unwrap();
960 processor
961 .on_entry_batch(&migration_status, bank.slot(), &[], false)
962 .unwrap();
963
964 let marker =
965 VersionedBlockMarker::from_genesis_cert_block_marker(test_genesis_cert_marker());
966 let result = processor.on_marker(
967 bank,
968 parent,
969 shred_version,
970 marker,
971 false,
972 None,
973 &migration_status,
974 );
975 assert!(matches!(
976 result,
977 Err(BlockComponentProcessorError::GenesisCertificateOutOfOrder)
978 ));
979 }
980
981 #[test]
982 fn test_genesis_certificate_immediately_after_header_passes_order_check() {
983 let migration_status = MigrationStatus::post_migration_status();
984 let mut processor = BlockComponentProcessor::default();
985 let (parent, bank_forks) = create_test_bank();
986 let bank = create_child_bank(&bank_forks, &parent, 1);
987 let shred_version = rand::rng().random();
988
989 let header = VersionedBlockMarker::from_block_header(BlockHeaderV1 {
990 parent_slot: 0,
991 parent_block_id: Hash::default(),
992 });
993 processor
994 .on_marker(
995 bank.clone(),
996 parent.clone(),
997 shred_version,
998 header,
999 false,
1000 None,
1001 &migration_status,
1002 )
1003 .unwrap();
1004
1005 let marker =
1006 VersionedBlockMarker::from_genesis_cert_block_marker(test_genesis_cert_marker());
1007 let result = processor.on_marker(
1008 bank,
1009 parent,
1010 shred_version,
1011 marker,
1012 false,
1013 None,
1014 &migration_status,
1015 );
1016 assert!(matches!(
1017 result,
1018 Err(BlockComponentProcessorError::GenesisCertificateInAlpenglowCluster)
1019 ));
1020 }
1021
1022 #[test]
1023 fn test_missing_footer_error_on_slot_full() {
1024 let migration_status = MigrationStatus::post_migration_status();
1025 let processor = processor_after_header();
1026
1027 let result = processor.on_final(&migration_status, 1, 0);
1029 assert!(matches!(
1030 result,
1031 Err(BlockComponentProcessorError::MissingBlockFooter)
1032 ));
1033 }
1034
1035 #[test]
1036 fn test_header_during_tower_bft_slot() {
1037 let migration_status = MigrationStatus::default();
1038 migration_status.record_feature_activation(0);
1039 let mut processor = BlockComponentProcessor::default();
1040 let (parent, bank_forks) = create_test_bank();
1041 let bank = create_child_bank(&bank_forks, &parent, 1);
1042 let marker = VersionedBlockMarker::from_block_header(BlockHeaderV1 {
1043 parent_slot: parent.slot(),
1044 parent_block_id: Hash::default(),
1045 });
1046
1047 assert!(!migration_status.should_allow_block_markers(bank.slot()));
1048 processor
1049 .on_marker(
1050 bank.clone(),
1051 parent,
1052 rand::rng().random(),
1053 marker,
1054 false,
1055 None,
1056 &migration_status,
1057 )
1058 .unwrap();
1059
1060 assert_matches!(
1061 processor.on_final(&migration_status, bank.slot(), bank.parent_slot()),
1062 Err(BlockComponentProcessorError::BlockComponentPreMigration)
1063 );
1064 }
1065
1066 #[test]
1067 fn test_first_alpenglow_block_requires_genesis_certificate_marker() {
1068 let migration_status = post_migration_status_with_genesis_slot(1);
1069 let processor = processor_after_footer();
1070
1071 let result = processor.on_final(&migration_status, 2, 1);
1072 assert!(matches!(
1073 result,
1074 Err(BlockComponentProcessorError::MissingGenesisCertificateMarker)
1075 ));
1076 }
1077
1078 #[test]
1079 fn test_first_alpenglow_block_with_genesis_certificate_marker_succeeds() {
1080 let migration_status = post_migration_status_with_genesis_slot(1);
1081 let (genesis_bank, bank_forks) = create_test_bank();
1082 let parent = create_child_bank(&bank_forks, &genesis_bank, 1);
1083 let parent_block_id = Hash::new_unique();
1084 parent.set_block_id(Some(parent_block_id));
1085 let bank = create_child_bank(&bank_forks, &parent, 2);
1086 let genesis_marker = GenesisCertBlockMarker {
1087 slot: parent.slot(),
1088 block_id: parent_block_id,
1089 bls_signature: BLSSignature([0; BLS_SIGNATURE_AFFINE_SIZE]),
1090 bitmap: vec![],
1091 };
1092 let mut processor = processor_after_header();
1093 bank.set_hashes_per_tick(Some(42));
1094 assert!(bank.hashes_per_tick().is_some());
1095
1096 processor
1097 .on_genesis_cert_block_marker_leader(bank.clone(), genesis_marker, &migration_status)
1098 .unwrap();
1099 assert!(bank.hashes_per_tick().is_none());
1100 processor.stage = BlockComponentStage::Done;
1101 assert!(processor.on_final(&migration_status, 2, 1).is_ok());
1102 }
1103
1104 #[test]
1105 fn test_genesis_certificate_marker_aborts_tower_bank_during_migration() {
1106 let migration_status = MigrationStatus::default();
1107 migration_status.record_feature_activation(0);
1108 let (genesis_bank, bank_forks) = create_test_bank();
1109 let parent = create_child_bank(&bank_forks, &genesis_bank, 1);
1110 let parent_block_id = Hash::new_unique();
1111 parent.set_block_id(Some(parent_block_id));
1112 let bank = create_child_bank(&bank_forks, &parent, 2);
1113 let genesis_marker = GenesisCertBlockMarker {
1114 slot: parent.slot(),
1115 block_id: parent_block_id,
1116 bls_signature: BLSSignature([0; BLS_SIGNATURE_AFFINE_SIZE]),
1117 bitmap: vec![],
1118 };
1119 let mut processor = processor_after_header();
1120 bank.set_hashes_per_tick(Some(42));
1121 let tower_hashes_per_tick = bank.hashes_per_tick();
1122 assert!(tower_hashes_per_tick.is_some());
1123
1124 assert_matches!(
1125 processor.on_genesis_cert_block_marker_leader(
1126 bank.clone(),
1127 genesis_marker,
1128 &migration_status,
1129 ),
1130 Err(BlockComponentProcessorError::AlpenglowMigrationTransition)
1131 );
1132
1133 assert!(migration_status.is_ready_to_enable());
1134 assert_eq!(bank.hashes_per_tick(), tower_hashes_per_tick);
1135 assert!(bank.get_alpenglow_genesis_certificate().is_some());
1136 }
1137
1138 #[test]
1139 fn test_first_alpenglow_block_genesis_slot_zero_skips_genesis_certificate_marker_check() {
1140 let migration_status = MigrationStatus::post_migration_status();
1141 let processor = processor_done();
1142
1143 assert!(processor.on_final(&migration_status, 1, 0).is_ok());
1144 }
1145
1146 #[test]
1147 fn test_multiple_headers_error() {
1148 let mut processor = BlockComponentProcessor::default();
1149 let header = VersionedBlockHeader::V1(BlockHeaderV1 {
1150 parent_slot: 0,
1151 parent_block_id: Hash::default(),
1152 });
1153
1154 assert!(processor.on_header(&header, 0).is_ok());
1156
1157 let result = processor.on_header(&header, 0);
1159 assert!(matches!(
1160 result,
1161 Err(BlockComponentProcessorError::MultipleBlockHeaders)
1162 ));
1163 }
1164
1165 #[test]
1166 fn test_multiple_footers_error() {
1167 let my_pubkey = Pubkey::new_unique();
1168 let mut processor = processor_after_header();
1169
1170 let (parent, bank_forks) = create_test_bank();
1171 let bank = create_child_bank(&bank_forks, &parent, 1);
1172 let shred_version = rand::rng().random();
1173
1174 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1176 let footer_time_nanos = parent_time_nanos + 400_000_000; let footer = VersionedBlockFooter::V1(BlockFooterV1 {
1179 bank_hash: Hash::new_unique(),
1180 block_producer_time_nanos: footer_time_nanos as u64,
1181 block_user_agent: vec![],
1182 block_final_cert: None,
1183 skip_reward_cert: None,
1184 notar_reward_cert: None,
1185 });
1186
1187 processor
1189 .on_footer(
1190 &my_pubkey,
1191 bank.clone(),
1192 parent.clone(),
1193 shred_version,
1194 footer.clone(),
1195 None,
1196 )
1197 .unwrap();
1198
1199 let err = processor
1201 .on_footer(&my_pubkey, bank, parent, shred_version, footer, None)
1202 .unwrap_err();
1203 assert!(matches!(
1204 err,
1205 BlockComponentProcessorError::MultipleBlockFooters
1206 ));
1207 }
1208
1209 #[test]
1210 fn test_on_footer_sets_timestamp() {
1211 let my_pubkey = Pubkey::new_unique();
1212 let mut processor = processor_after_header();
1213
1214 let (parent, bank_forks) = create_test_bank();
1215 let bank = create_child_bank(&bank_forks, &parent, 1);
1216 let shred_version = rand::rng().random();
1217
1218 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1220 let footer_time_nanos = parent_time_nanos + 200_000_000; let expected_time_secs = footer_time_nanos / 1_000_000_000;
1222
1223 let footer = VersionedBlockFooter::V1(BlockFooterV1 {
1224 bank_hash: Hash::new_unique(),
1225 block_producer_time_nanos: footer_time_nanos as u64,
1226 block_user_agent: vec![],
1227 block_final_cert: None,
1228 skip_reward_cert: None,
1229 notar_reward_cert: None,
1230 });
1231
1232 processor
1233 .on_footer(
1234 &my_pubkey,
1235 bank.clone(),
1236 parent,
1237 shred_version,
1238 footer,
1239 None,
1240 )
1241 .unwrap();
1242
1243 assert_eq!(processor.stage, BlockComponentStage::AcceptingAlpentick);
1244
1245 assert_eq!(bank.clock().unix_timestamp, expected_time_secs);
1247 }
1248
1249 #[test]
1250 fn test_on_header_sets_flag() {
1251 let mut processor = BlockComponentProcessor::default();
1252 let header = VersionedBlockHeader::V1(BlockHeaderV1 {
1253 parent_slot: 0,
1254 parent_block_id: Hash::default(),
1255 });
1256
1257 processor.on_header(&header, 0).unwrap();
1258 assert_eq!(
1259 processor.stage,
1260 BlockComponentStage::AcceptingGenesisOrEntries
1261 );
1262 }
1263
1264 #[test]
1265 fn test_on_header_parent_slot_mismatch_error() {
1266 let mut processor = BlockComponentProcessor::default();
1267 let header = VersionedBlockHeader::V1(BlockHeaderV1 {
1268 parent_slot: 2,
1269 parent_block_id: Hash::default(),
1270 });
1271
1272 assert!(matches!(
1273 processor.on_header(&header, 0),
1274 Err(BlockComponentProcessorError::HeaderParentSlotMismatch {
1275 header_parent_slot: 2,
1276 bank_parent_slot: 0,
1277 })
1278 ));
1279 }
1280
1281 #[test]
1282 fn test_on_marker_processes_header() {
1283 let migration_status = MigrationStatus::post_migration_status();
1284 let mut processor = BlockComponentProcessor::default();
1285 let marker = VersionedBlockMarker::from_block_header(BlockHeaderV1 {
1286 parent_slot: 0,
1287 parent_block_id: Hash::default(),
1288 });
1289
1290 let (parent, bank_forks) = create_test_bank();
1291 let bank = create_child_bank(&bank_forks, &parent, 1);
1292 let shred_version = rand::rng().random();
1293
1294 processor
1295 .on_marker(
1296 bank,
1297 parent,
1298 shred_version,
1299 marker,
1300 false,
1301 None,
1302 &migration_status,
1303 )
1304 .unwrap();
1305 assert_eq!(
1306 processor.stage,
1307 BlockComponentStage::AcceptingGenesisOrEntries
1308 );
1309 }
1310
1311 #[test]
1312 fn test_on_marker_rejects_header_parent_slot_mismatch() {
1313 let migration_status = MigrationStatus::post_migration_status();
1314 let mut processor = BlockComponentProcessor::default();
1315 let marker = VersionedBlockMarker::from_block_header(BlockHeaderV1 {
1316 parent_slot: 7, parent_block_id: Hash::default(),
1318 });
1319
1320 let (parent, bank_forks) = create_test_bank();
1321 let bank = create_child_bank(&bank_forks, &parent, 1);
1322 let shred_version = rand::rng().random();
1323
1324 assert!(matches!(
1325 processor.on_marker(
1326 bank,
1327 parent,
1328 shred_version,
1329 marker,
1330 false,
1331 None,
1332 &migration_status
1333 ),
1334 Err(BlockComponentProcessorError::HeaderParentSlotMismatch {
1335 header_parent_slot: 7,
1336 bank_parent_slot: 0,
1337 })
1338 ));
1339 }
1340
1341 #[test]
1342 fn test_on_marker_processes_footer() {
1343 let migration_status = MigrationStatus::post_migration_status();
1344 let mut processor = processor_after_header();
1345
1346 let (parent, bank_forks) = create_test_bank();
1347 let bank = create_child_bank(&bank_forks, &parent, 1);
1348 let shred_version = rand::rng().random();
1349
1350 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1352 let footer_time_nanos = parent_time_nanos + 300_000_000; let expected_time_secs = footer_time_nanos / 1_000_000_000;
1354
1355 let marker = VersionedBlockMarker::from_block_footer(BlockFooterV1 {
1356 bank_hash: Hash::new_unique(),
1357 block_producer_time_nanos: footer_time_nanos as u64,
1358 block_user_agent: vec![],
1359 block_final_cert: None,
1360 skip_reward_cert: None,
1361 notar_reward_cert: None,
1362 });
1363
1364 processor
1365 .on_marker(
1366 bank.clone(),
1367 parent,
1368 shred_version,
1369 marker,
1370 false,
1371 None,
1372 &migration_status,
1373 )
1374 .unwrap();
1375 assert_eq!(processor.stage, BlockComponentStage::AcceptingAlpentick);
1376
1377 assert_eq!(bank.clock().unix_timestamp, expected_time_secs);
1379 }
1380
1381 #[test]
1382 fn test_complete_workflow_success() {
1383 let migration_status = MigrationStatus::post_migration_status();
1384 let mut processor = BlockComponentProcessor::default();
1385 let (parent, bank_forks) = create_test_bank();
1386 let bank = create_child_bank(&bank_forks, &parent, 1);
1387 let shred_version = rand::rng().random();
1388
1389 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1391 let footer_time_nanos = parent_time_nanos + 100_000_000; let expected_time_secs = footer_time_nanos / 1_000_000_000;
1393
1394 let header = VersionedBlockHeader::V1(BlockHeaderV1 {
1396 parent_slot: 0,
1397 parent_block_id: Hash::default(),
1398 });
1399 processor.on_header(&header, bank.parent_slot()).unwrap();
1400
1401 processor
1403 .on_entry_batch(&migration_status, 1, &[], false)
1404 .unwrap();
1405
1406 let footer = VersionedBlockFooter::V1(BlockFooterV1 {
1408 bank_hash: Hash::new_unique(),
1409 block_producer_time_nanos: footer_time_nanos as u64,
1410 block_user_agent: vec![],
1411 block_final_cert: None,
1412 skip_reward_cert: None,
1413 notar_reward_cert: None,
1414 });
1415 processor
1416 .on_footer(
1417 &migration_status.my_pubkey(),
1418 bank.clone(),
1419 parent.clone(),
1420 shred_version,
1421 footer,
1422 None,
1423 )
1424 .unwrap();
1425
1426 assert_eq!(bank.clock().unix_timestamp, expected_time_secs);
1428
1429 let result = processor.on_entry_batch(&migration_status, 1, &[], false);
1431 assert_matches!(
1432 result,
1433 Err(BlockComponentProcessorError::EntryBatchAfterBlockFooter)
1434 );
1435 }
1436
1437 #[test]
1438 fn test_alpentick_position_validation() {
1439 let migration_status = MigrationStatus::post_migration_status();
1440 let mut processor = processor_after_footer();
1441 let good_alpentick = alpentick(1);
1442
1443 processor
1444 .on_entry_batch(&migration_status, 1, &good_alpentick, true)
1445 .unwrap();
1446 assert_matches!(
1447 processor.on_entry_batch(&migration_status, 1, &good_alpentick, true),
1448 Err(BlockComponentProcessorError::InvalidAlpentickPosition)
1449 );
1450
1451 let mut processor = BlockComponentProcessor::default();
1452 assert_matches!(
1453 processor.on_entry_batch(&migration_status, 1, &good_alpentick, true),
1454 Err(BlockComponentProcessorError::MissingParentMarker)
1455 );
1456
1457 let mut processor = processor_after_footer();
1458 let bad_alpentick = alpentick(2);
1459 assert_matches!(
1460 processor.on_entry_batch(&migration_status, 1, &bad_alpentick, true),
1461 Err(BlockComponentProcessorError::EntryBatchAfterBlockFooter)
1462 );
1463
1464 let migration_status = MigrationStatus::default();
1465 let mut processor = BlockComponentProcessor::default();
1466 processor
1467 .on_entry_batch(&migration_status, 1, &good_alpentick, true)
1468 .unwrap();
1469 }
1470
1471 #[test]
1472 fn test_block_marker_detected_pre_migration() {
1473 let migration_status = MigrationStatus::default();
1474 let mut processor = BlockComponentProcessor::default();
1475 let (parent, bank_forks) = create_test_bank();
1476 let bank = create_child_bank(&bank_forks, &parent, 1);
1477 let shred_version = rand::rng().random();
1478
1479 let marker = VersionedBlockMarker::from_block_header(BlockHeaderV1 {
1481 parent_slot: 0,
1482 parent_block_id: Hash::default(),
1483 });
1484
1485 let err = processor
1486 .on_marker(
1487 bank,
1488 parent,
1489 shred_version,
1490 marker,
1491 false,
1492 None,
1493 &migration_status,
1494 )
1495 .unwrap_err();
1496 assert!(matches!(
1497 err,
1498 BlockComponentProcessorError::BlockComponentPreMigration
1499 ));
1500 }
1501
1502 #[test]
1503 fn test_footer_and_update_parent_rejected_pre_migration() {
1504 let migration_status = MigrationStatus::default();
1505 let (parent, bank_forks) = create_test_bank();
1506 let bank = create_child_bank(&bank_forks, &parent, 1);
1507 let shred_version = rand::rng().random();
1508
1509 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1510 let footer_marker = VersionedBlockMarker::from_block_footer(BlockFooterV1 {
1511 bank_hash: Hash::new_unique(),
1512 block_producer_time_nanos: (parent_time_nanos + 500_000_000) as u64,
1513 block_user_agent: vec![],
1514 block_final_cert: None,
1515 skip_reward_cert: None,
1516 notar_reward_cert: None,
1517 });
1518
1519 let mut processor = BlockComponentProcessor::default();
1520 assert!(matches!(
1521 processor
1522 .on_marker(
1523 bank.clone(),
1524 parent.clone(),
1525 shred_version,
1526 footer_marker,
1527 false,
1528 None,
1529 &migration_status
1530 )
1531 .unwrap_err(),
1532 BlockComponentProcessorError::BlockComponentPreMigration
1533 ));
1534
1535 let update_parent_marker = VersionedBlockMarker::from_update_parent(UpdateParentV1 {
1536 new_parent_slot: 0,
1537 new_parent_block_id: Hash::default(),
1538 });
1539
1540 let mut processor = BlockComponentProcessor::default();
1541 assert!(matches!(
1542 processor
1543 .on_marker(
1544 bank,
1545 parent,
1546 shred_version,
1547 update_parent_marker,
1548 false,
1549 None,
1550 &migration_status
1551 )
1552 .unwrap_err(),
1553 BlockComponentProcessorError::BlockComponentPreMigration
1554 ));
1555 }
1556
1557 #[test]
1558 fn test_entry_batch_pre_migration_succeeds() {
1559 let migration_status = MigrationStatus::default();
1560 let mut processor = BlockComponentProcessor::default();
1561
1562 let result = processor.on_entry_batch(&migration_status, 1, &[], false);
1564 assert!(result.is_ok());
1565
1566 let result = processor.on_entry_batch(&migration_status, 1, &[], false);
1568 assert!(result.is_ok());
1569
1570 assert!(processor.on_final(&migration_status, 1, 0).is_ok());
1572 }
1573
1574 #[test]
1575 fn test_complete_workflow_post_migration() {
1576 let migration_status = MigrationStatus::post_migration_status();
1577 let mut processor = BlockComponentProcessor::default();
1578 let (parent, bank_forks) = create_test_bank();
1579 let bank = create_child_bank(&bank_forks, &parent, 1);
1580 let shred_version = rand::rng().random();
1581
1582 let header_marker = VersionedBlockMarker::from_block_header(BlockHeaderV1 {
1584 parent_slot: 0,
1585 parent_block_id: Hash::default(),
1586 });
1587 processor
1588 .on_marker(
1589 bank.clone(),
1590 parent.clone(),
1591 shred_version,
1592 header_marker,
1593 false,
1594 None,
1595 &migration_status,
1596 )
1597 .unwrap();
1598
1599 processor
1601 .on_entry_batch(&migration_status, 1, &[], false)
1602 .unwrap();
1603
1604 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1606 let footer_time_nanos = parent_time_nanos + 500_000_000; let expected_time_secs = footer_time_nanos / 1_000_000_000;
1608
1609 let footer_marker = VersionedBlockMarker::from_block_footer(BlockFooterV1 {
1611 bank_hash: Hash::new_unique(),
1612 block_producer_time_nanos: footer_time_nanos as u64,
1613 block_user_agent: vec![],
1614 block_final_cert: None,
1615 skip_reward_cert: None,
1616 notar_reward_cert: None,
1617 });
1618 processor
1619 .on_marker(
1620 bank.clone(),
1621 parent,
1622 shred_version,
1623 footer_marker,
1624 false,
1625 None,
1626 &migration_status,
1627 )
1628 .unwrap();
1629
1630 assert_eq!(bank.clock().unix_timestamp, expected_time_secs);
1632
1633 let result = processor.on_entry_batch(&migration_status, 1, &[], false);
1635 assert_matches!(
1636 result,
1637 Err(BlockComponentProcessorError::EntryBatchAfterBlockFooter)
1638 );
1639 }
1640
1641 #[test]
1642 fn test_footer_without_header_errors() {
1643 let my_pubkey = Pubkey::new_unique();
1644 let mut processor = BlockComponentProcessor::default();
1645 let (parent, bank_forks) = create_test_bank();
1646 let bank = create_child_bank(&bank_forks, &parent, 1);
1647 let shred_version = rand::rng().random();
1648
1649 let footer = VersionedBlockFooter::V1(BlockFooterV1 {
1650 bank_hash: Hash::new_unique(),
1651 block_producer_time_nanos: 1_000_000_000,
1652 block_user_agent: vec![],
1653 block_final_cert: None,
1654 skip_reward_cert: None,
1655 notar_reward_cert: None,
1656 });
1657
1658 let err = processor
1660 .on_footer(&my_pubkey, bank, parent, shred_version, footer, None)
1661 .unwrap_err();
1662 assert!(matches!(
1663 err,
1664 BlockComponentProcessorError::MissingParentMarker
1665 ));
1666 }
1667
1668 #[test]
1669 fn test_marker_with_footer_at_slot_full() {
1670 let migration_status = MigrationStatus::post_migration_status();
1671 let mut processor = processor_after_header();
1672 let (parent, bank_forks) = create_test_bank();
1673 let bank = create_child_bank(&bank_forks, &parent, 1);
1674 let shred_version = rand::rng().random();
1675
1676 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1678 let footer_time_nanos = parent_time_nanos + 600_000_000; let expected_time_secs = footer_time_nanos / 1_000_000_000;
1680
1681 let footer_marker = VersionedBlockMarker::from_block_footer(BlockFooterV1 {
1683 bank_hash: Hash::new_unique(),
1684 block_producer_time_nanos: footer_time_nanos as u64,
1685 block_user_agent: vec![],
1686 block_final_cert: None,
1687 skip_reward_cert: None,
1688 notar_reward_cert: None,
1689 });
1690
1691 processor
1693 .on_marker(
1694 bank.clone(),
1695 parent,
1696 shred_version,
1697 footer_marker,
1698 false,
1699 None,
1700 &migration_status,
1701 )
1702 .unwrap();
1703 assert_eq!(processor.stage, BlockComponentStage::AcceptingAlpentick);
1704
1705 assert_eq!(bank.clock().unix_timestamp, expected_time_secs);
1707 }
1708
1709 #[test]
1710 fn test_entry_batch_with_header_not_full_succeeds() {
1711 let migration_status = MigrationStatus::post_migration_status();
1712 let mut processor = processor_after_header();
1713
1714 let result = processor.on_entry_batch(&migration_status, 1, &[], false);
1716 assert!(result.is_ok());
1717 }
1718
1719 #[test]
1720 fn test_footer_sets_epoch_start_timestamp_on_epoch_change() {
1721 let my_pubkey = Pubkey::new_unique();
1722 let mut processor = processor_after_header();
1723 let shred_version = rand::rng().random();
1724
1725 let genesis_config_info = create_genesis_config(10_000);
1727 let (genesis_bank, bank_forks) =
1728 Bank::new_with_bank_forks_for_tests(&genesis_config_info.genesis_config);
1729
1730 let epoch_schedule = genesis_bank.epoch_schedule();
1732 let first_slot_in_epoch_1 = epoch_schedule.get_first_slot_in_epoch(1);
1733
1734 let mut parent = genesis_bank.clone();
1736 for slot in 1..first_slot_in_epoch_1 {
1737 parent = create_child_bank(&bank_forks, &parent, slot);
1738 }
1739
1740 let bank = create_child_bank(&bank_forks, &parent, first_slot_in_epoch_1);
1742
1743 assert_eq!(bank.epoch(), 1);
1745
1746 let parent_slot = parent.slot();
1748 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1749 let current_slot = bank.slot();
1750 let elapsed_slot_duration_nanos =
1751 bank.slot_range_duration_nanos(parent_slot.saturating_add(1), current_slot);
1752
1753 let (lower_bound, upper_bound) = BlockComponentProcessor::nanosecond_time_bounds(
1755 parent_time_nanos,
1756 elapsed_slot_duration_nanos,
1757 );
1758 let footer_time_nanos = (lower_bound + upper_bound) / 2;
1759 let expected_time_secs = footer_time_nanos / 1_000_000_000;
1760
1761 let footer = VersionedBlockFooter::V1(BlockFooterV1 {
1762 bank_hash: Hash::new_unique(),
1763 block_producer_time_nanos: footer_time_nanos as u64,
1764 block_user_agent: vec![],
1765 block_final_cert: None,
1766 skip_reward_cert: None,
1767 notar_reward_cert: None,
1768 });
1769
1770 processor
1771 .on_footer(
1772 &my_pubkey,
1773 bank.clone(),
1774 parent,
1775 shred_version,
1776 footer,
1777 None,
1778 )
1779 .unwrap();
1780
1781 assert_eq!(bank.clock().unix_timestamp, expected_time_secs);
1783
1784 assert_eq!(bank.clock().epoch_start_timestamp, expected_time_secs);
1786 }
1787
1788 fn test_clock_bounds_helper(
1790 slot_gap: u64,
1791 timestamp_fn: impl FnOnce(i64, i64, i64) -> i64,
1792 should_pass: bool,
1793 ) {
1794 let my_pubkey = Pubkey::new_unique();
1795 let mut processor = processor_after_header();
1796 let shred_version = rand::rng().random();
1797
1798 let (parent, bank_forks) = create_test_bank_alpenglow();
1799 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1800
1801 parent.update_clock_from_footer(parent_time_nanos);
1803
1804 let bank: Arc<Bank> = create_child_bank(&bank_forks, &parent, slot_gap);
1805 let elapsed_slot_duration_nanos = bank.slot_range_duration_nanos(1, slot_gap);
1806
1807 let (lower_bound, upper_bound) = BlockComponentProcessor::nanosecond_time_bounds(
1808 parent_time_nanos,
1809 elapsed_slot_duration_nanos,
1810 );
1811
1812 let footer_time_nanos = timestamp_fn(parent_time_nanos, lower_bound, upper_bound);
1813
1814 let footer = VersionedBlockFooter::V1(BlockFooterV1 {
1815 bank_hash: Hash::new_unique(),
1816 block_producer_time_nanos: footer_time_nanos as u64,
1817 block_user_agent: vec![],
1818 block_final_cert: None,
1819 skip_reward_cert: None,
1820 notar_reward_cert: None,
1821 });
1822
1823 let result = processor.on_footer(&my_pubkey, bank, parent, shred_version, footer, None);
1824 if should_pass {
1825 result.unwrap();
1826 } else {
1827 assert!(matches!(
1828 result.unwrap_err(),
1829 BlockComponentProcessorError::NanosecondClockOutOfBounds
1830 ));
1831 }
1832 }
1833
1834 #[test]
1835 fn test_clock_bounds_at_minimum() {
1836 test_clock_bounds_helper(1, |_, lower, _| lower, true);
1837 }
1838
1839 #[test]
1840 fn test_clock_bounds_at_maximum() {
1841 test_clock_bounds_helper(1, |_, _, upper| upper, true);
1842 }
1843
1844 #[test]
1845 fn test_clock_bounds_below_minimum() {
1846 test_clock_bounds_helper(1, |_, lower, _| lower - 1, false);
1847 }
1848
1849 #[test]
1850 fn test_clock_bounds_above_maximum() {
1851 test_clock_bounds_helper(1, |_, _, upper| upper + 1, false);
1852 }
1853
1854 #[test]
1855 fn test_clock_bounds_multi_slot_gap() {
1856 test_clock_bounds_helper(5, |_, lower, _| lower + 2_000_000_000, true);
1859 }
1860
1861 #[test]
1862 fn test_clock_bounds_multi_slot_gap_exceeds() {
1863 test_clock_bounds_helper(5, |_, _, upper| upper + 1_000_000_000, false);
1865 }
1866
1867 #[test]
1868 fn test_clock_bounds_timestamp_equals_parent() {
1869 test_clock_bounds_helper(1, |parent_time, _, _| parent_time, false);
1871 }
1872
1873 #[test]
1874 fn test_clock_bounds_without_parent_nanosecond_clock_rejects_out_of_bounds() {
1875 let my_pubkey = Pubkey::new_unique();
1876 let mut processor = processor_after_header();
1877 let shred_version = rand::rng().random();
1878
1879 let (parent, bank_forks) = create_test_bank_alpenglow();
1880 assert_eq!(parent.get_nanosecond_clock(), None);
1881
1882 let bank = create_child_bank(&bank_forks, &parent, 1);
1883 let parent_time_nanos = bank.clock().unix_timestamp.saturating_mul(1_000_000_000);
1884 let elapsed_slot_duration_nanos =
1885 bank.slot_range_duration_nanos(parent.slot().saturating_add(1), bank.slot());
1886 let (_, upper_bound) = BlockComponentProcessor::nanosecond_time_bounds(
1887 parent_time_nanos,
1888 elapsed_slot_duration_nanos,
1889 );
1890
1891 let footer = VersionedBlockFooter::V1(BlockFooterV1 {
1892 bank_hash: Hash::new_unique(),
1893 block_producer_time_nanos: u64::try_from(upper_bound.saturating_add(1)).unwrap(),
1894 block_user_agent: vec![],
1895 block_final_cert: None,
1896 skip_reward_cert: None,
1897 notar_reward_cert: None,
1898 });
1899
1900 assert!(matches!(
1901 processor
1902 .on_footer(&my_pubkey, bank, parent, shred_version, footer, None)
1903 .unwrap_err(),
1904 BlockComponentProcessorError::NanosecondClockOutOfBounds
1905 ));
1906 }
1907
1908 #[test]
1909 fn test_clock_bounds_rejects_timestamp_above_i64() {
1910 let my_pubkey = Pubkey::new_unique();
1911 let mut processor = processor_after_header();
1912 let shred_version = rand::rng().random();
1913
1914 let (parent, bank_forks) = create_test_bank_alpenglow();
1915 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1916 parent.update_clock_from_footer(parent_time_nanos);
1917 let bank = create_child_bank(&bank_forks, &parent, 1);
1918
1919 let footer = VersionedBlockFooter::V1(BlockFooterV1 {
1920 bank_hash: Hash::new_unique(),
1921 block_producer_time_nanos: u64::MAX,
1922 block_user_agent: vec![],
1923 block_final_cert: None,
1924 skip_reward_cert: None,
1925 notar_reward_cert: None,
1926 });
1927
1928 assert!(matches!(
1929 processor
1930 .on_footer(&my_pubkey, bank, parent, shred_version, footer, None)
1931 .unwrap_err(),
1932 BlockComponentProcessorError::NanosecondClockOutOfBounds
1933 ));
1934 }
1935
1936 fn test_nanosecond_time_bounds_helper(
1938 parent_time_nanos: i64,
1939 elapsed_slot_duration_nanos: u128,
1940 expected_lower: i64,
1941 expected_upper: i64,
1942 ) {
1943 let (lower, upper) = BlockComponentProcessor::nanosecond_time_bounds(
1944 parent_time_nanos,
1945 elapsed_slot_duration_nanos,
1946 );
1947
1948 assert_eq!(lower, expected_lower);
1949 assert_eq!(upper, expected_upper);
1950 }
1951
1952 #[test]
1953 fn test_nanosecond_time_bounds_calculation() {
1954 let parent_slot = 10;
1959 let parent_time = 1_000_000_000_000; let working_slot = 15;
1961 let slot_delta = working_slot - parent_slot;
1962 test_nanosecond_time_bounds_helper(
1963 parent_time,
1964 u128::from(slot_delta).saturating_mul(u128::from(DEFAULT_NS_PER_SLOT)),
1965 parent_time + 1,
1966 parent_time + (2 * DEFAULT_NS_PER_SLOT * slot_delta) as i64,
1967 );
1968 }
1969
1970 #[test]
1971 fn test_nanosecond_time_bounds_same_slot() {
1972 let parent_time = 1_000_000_000_000;
1979 test_nanosecond_time_bounds_helper(parent_time, 0, parent_time + 1, parent_time);
1980 }
1981
1982 #[test]
1983 fn test_nanosecond_time_bounds_saturates_upper_bound() {
1984 let parent_time = i64::MAX - 5;
1985 let (lower, upper) =
1986 BlockComponentProcessor::nanosecond_time_bounds(parent_time, u128::MAX);
1987
1988 assert_eq!(lower, parent_time + 1);
1989 assert_eq!(upper, i64::MAX);
1990 }
1991
1992 #[test]
1993 fn test_initial_up_reject() {
1994 let mut processor = BlockComponentProcessor::default();
1995 let update_parent = VersionedUpdateParent::V1(UpdateParentV1 {
1996 new_parent_slot: 0,
1997 new_parent_block_id: Hash::default(),
1998 });
1999
2000 assert!(matches!(
2001 processor.on_update_parent(4, &update_parent, false),
2002 Err(BlockComponentProcessorError::UnexpectedInitialUpdateParent)
2003 ));
2004 assert_eq!(processor.stage, BlockComponentStage::PreParentMarker);
2005 }
2006
2007 #[test]
2008 fn test_update_parent_rejects_non_first_leader_window_slot() {
2009 let mut processor = BlockComponentProcessor::default();
2010 let update_parent = VersionedUpdateParent::V1(UpdateParentV1 {
2011 new_parent_slot: 0,
2012 new_parent_block_id: Hash::default(),
2013 });
2014
2015 assert!(matches!(
2016 processor.on_update_parent(5, &update_parent, true),
2017 Err(BlockComponentProcessorError::UpdateParentNotFirstInLeaderWindow(5))
2018 ));
2019 assert_eq!(processor.stage, BlockComponentStage::PreParentMarker);
2020 }
2021
2022 #[test]
2023 fn test_initial_up_ok() {
2024 let mut processor = BlockComponentProcessor::default();
2025 let update_parent = VersionedUpdateParent::V1(UpdateParentV1 {
2026 new_parent_slot: 0,
2027 new_parent_block_id: Hash::default(),
2028 });
2029
2030 processor.on_update_parent(4, &update_parent, true).unwrap();
2031 assert_eq!(
2032 processor.stage,
2033 BlockComponentStage::AcceptingEntriesOrFooter {
2034 parent_marker: EntryParentMarker::UpdateParent,
2035 }
2036 );
2037 }
2038
2039 #[test]
2040 fn test_update_parent_after_header_abandoned_bank() {
2041 let mut processor = BlockComponentProcessor::default();
2042 processor
2043 .on_header(
2044 &VersionedBlockHeader::V1(BlockHeaderV1 {
2045 parent_slot: 0,
2046 parent_block_id: Hash::default(),
2047 }),
2048 0,
2049 )
2050 .unwrap();
2051
2052 let update_parent = VersionedUpdateParent::V1(UpdateParentV1 {
2053 new_parent_slot: 0,
2054 new_parent_block_id: Hash::default(),
2055 });
2056
2057 assert!(matches!(
2058 processor.on_update_parent(4, &update_parent, false),
2059 Err(BlockComponentProcessorError::AbandonedBank(_))
2060 ));
2061 }
2062
2063 #[test]
2064 fn test_update_parent_after_footer_error() {
2065 let mut processor = processor_after_footer();
2066 let update_parent = VersionedUpdateParent::V1(UpdateParentV1 {
2067 new_parent_slot: 0,
2068 new_parent_block_id: Hash::default(),
2069 });
2070
2071 assert_matches!(
2072 processor.on_update_parent(4, &update_parent, false),
2073 Err(BlockComponentProcessorError::SpuriousUpdateParent)
2074 );
2075 }
2076
2077 #[test]
2078 fn test_multiple_update_parents_error() {
2079 let mut processor = BlockComponentProcessor::default();
2080 let update_parent = VersionedUpdateParent::V1(UpdateParentV1 {
2081 new_parent_slot: 0,
2082 new_parent_block_id: Hash::default(),
2083 });
2084
2085 processor.on_update_parent(4, &update_parent, true).unwrap();
2087
2088 assert_matches!(
2090 processor.on_update_parent(4, &update_parent, true),
2091 Err(BlockComponentProcessorError::MultipleUpdateParents)
2092 );
2093 }
2094
2095 #[test]
2096 fn test_header_after_update_parent_error() {
2097 let mut processor = BlockComponentProcessor::default();
2098 processor
2099 .on_update_parent(
2100 4,
2101 &VersionedUpdateParent::V1(UpdateParentV1 {
2102 new_parent_slot: 0,
2103 new_parent_block_id: Hash::default(),
2104 }),
2105 true,
2106 )
2107 .unwrap();
2108
2109 let header = VersionedBlockHeader::V1(BlockHeaderV1 {
2110 parent_slot: 0,
2111 parent_block_id: Hash::default(),
2112 });
2113
2114 assert!(matches!(
2115 processor.on_header(&header, 0),
2116 Err(BlockComponentProcessorError::SpuriousUpdateParent)
2117 ));
2118 }
2119
2120 #[test]
2121 fn test_workflow_with_update_parent() {
2122 let migration_status = MigrationStatus::post_migration_status();
2123 let mut processor = BlockComponentProcessor::default();
2124 let (parent, bank_forks) = create_test_bank();
2125 let bank = create_child_bank(&bank_forks, &parent, 4);
2126 let slot = bank.slot();
2127 let shred_version = rand::rng().random();
2128
2129 processor
2130 .on_update_parent(
2131 slot,
2132 &VersionedUpdateParent::V1(UpdateParentV1 {
2133 new_parent_slot: 0,
2134 new_parent_block_id: Hash::default(),
2135 }),
2136 true,
2137 )
2138 .unwrap();
2139
2140 processor
2141 .on_entry_batch(&migration_status, slot, &[], false)
2142 .unwrap();
2143
2144 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
2145 let footer = VersionedBlockFooter::V1(BlockFooterV1 {
2146 bank_hash: Hash::new_unique(),
2147 block_producer_time_nanos: (parent_time_nanos + 100_000_000) as u64,
2148 block_user_agent: vec![],
2149 block_final_cert: None,
2150 skip_reward_cert: None,
2151 notar_reward_cert: None,
2152 });
2153 processor
2154 .on_footer(
2155 &migration_status.my_pubkey(),
2156 bank,
2157 parent,
2158 shred_version,
2159 footer,
2160 None,
2161 )
2162 .unwrap();
2163
2164 let good_alpentick = alpentick(1);
2165 processor
2166 .on_entry_batch(&migration_status, slot, &good_alpentick, true)
2167 .unwrap();
2168
2169 processor.on_final(&migration_status, slot, 0).unwrap();
2170 }
2171}