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).map_err(|_| {
573 warn!(
574 "Failed to verify genesis certificate for slot {cert_slot} in bank slot {}",
575 bank.slot()
576 );
577 BlockComponentProcessorError::GenesisCertificateFailedVerification
578 })?;
579
580 Ok(())
581 }
582
583 fn on_footer(
584 &mut self,
585 my_pubkey: &Pubkey,
586 bank: Arc<Bank>,
587 parent_bank: Arc<Bank>,
588 shred_version: u16,
589 footer: VersionedBlockFooter,
590 finalization_cert_sender: Option<&Sender<SmallVec<[Certificate; 2]>>>,
591 ) -> Result<(), BlockComponentProcessorError> {
592 self.stage.on_footer()?;
593
594 let VersionedBlockFooter::V1(footer) = footer;
595
596 Self::enforce_nanosecond_clock_bounds(&bank, &parent_bank, &footer)?;
597
598 let BlockFooterV1 {
599 bank_hash,
600 block_producer_time_nanos,
601 block_user_agent: _,
602 block_final_cert,
603 skip_reward_cert,
604 notar_reward_cert,
605 } = footer;
606
607 let reward_cert = ValidatedRewardCert::try_new(
608 &bank,
609 shred_version,
610 &skip_reward_cert,
611 ¬ar_reward_cert,
612 )?;
613 let block_producer_time_nanos =
614 Self::block_producer_time_nanos_as_i64(block_producer_time_nanos)?;
615 let final_cert = block_final_cert
616 .map(|final_cert| {
617 ValidatedBlockFinalizationCert::try_from_footer(final_cert, &bank, shred_version)
618 .map_err(BlockComponentProcessorError::InvalidFinalizationCertificate)
619 })
620 .transpose()?;
621
622 let (footer_input, pool_input) = match final_cert {
623 None => (None, None),
624 Some(cert) => {
625 let (signers, finalize_cert, notarize_cert) = cert.into_parts();
626 let final_slot = finalize_cert.cert_type.slot();
627 (
628 Some((signers, final_slot)),
629 Some((finalize_cert, notarize_cert)),
630 )
631 }
632 };
633
634 Self::update_bank_with_footer_fields(
635 &bank,
636 block_producer_time_nanos,
637 Some(bank_hash),
638 reward_cert,
639 footer_input
640 .as_ref()
641 .map(|(validators, slot)| (validators, *slot)),
642 )?;
643
644 if let Some((finalize_cert, notarize_cert)) = pool_input
646 && let Some(sender) = finalization_cert_sender
647 {
648 let channel_name = "finalization_cert_sender";
649 let certs = match notarize_cert {
650 None => smallvec![finalize_cert],
651 Some(c) => smallvec![finalize_cert, c],
652 };
653 match sender.try_send(certs) {
654 Ok(()) => (),
655 Err(TrySendError::Full(_)) => {
656 warn!("{my_pubkey}: channel \"{channel_name}\" is full, dropping msg")
657 }
658 Err(TrySendError::Disconnected(_)) => {
659 warn!("{my_pubkey}: channel \"{channel_name}\" disconnected")
660 }
661 }
662 }
663
664 Ok(())
665 }
666
667 fn on_header(
668 &mut self,
669 header: &VersionedBlockHeader,
670 bank_parent_slot: Slot,
671 ) -> Result<(), BlockComponentProcessorError> {
672 self.stage.on_header()?;
673
674 let VersionedBlockHeader::V1(header) = header;
675 if header.parent_slot != bank_parent_slot {
676 return Err(BlockComponentProcessorError::HeaderParentSlotMismatch {
677 header_parent_slot: header.parent_slot,
678 bank_parent_slot,
679 });
680 }
681 Ok(())
682 }
683
684 fn on_update_parent(
685 &mut self,
686 slot: Slot,
687 update_parent: &VersionedUpdateParent,
688 allow_initial_update_parent: bool,
689 ) -> Result<(), BlockComponentProcessorError> {
690 if leader_slot_index(slot) != 0 {
691 return Err(BlockComponentProcessorError::UpdateParentNotFirstInLeaderWindow(slot));
692 }
693
694 self.stage
695 .on_update_parent(update_parent, allow_initial_update_parent)
696 }
697
698 fn enforce_nanosecond_clock_bounds(
699 bank: &Bank,
700 parent_bank: &Bank,
701 footer: &BlockFooterV1,
702 ) -> Result<(), BlockComponentProcessorError> {
703 let parent_time_nanos = parent_bank
706 .get_nanosecond_clock()
707 .unwrap_or_else(|| bank.clock().unix_timestamp.saturating_mul(1_000_000_000));
708
709 let parent_slot = parent_bank.slot();
710 let current_time_nanos =
711 Self::block_producer_time_nanos_as_i64(footer.block_producer_time_nanos)?;
712 let current_slot = bank.slot();
713 let elapsed_slot_duration_nanos =
714 bank.slot_range_duration_nanos(parent_slot.saturating_add(1), current_slot);
715
716 let (lower_bound_nanos, upper_bound_nanos) =
717 Self::nanosecond_time_bounds(parent_time_nanos, elapsed_slot_duration_nanos);
718
719 let is_valid =
720 lower_bound_nanos <= current_time_nanos && current_time_nanos <= upper_bound_nanos;
721
722 match is_valid {
723 true => Ok(()),
724 false => Err(BlockComponentProcessorError::NanosecondClockOutOfBounds),
725 }
726 }
727
728 fn block_producer_time_nanos_as_i64(
735 block_producer_time_nanos: u64,
736 ) -> Result<i64, BlockComponentProcessorError> {
737 i64::try_from(block_producer_time_nanos)
738 .map_err(|_| BlockComponentProcessorError::NanosecondClockOutOfBounds)
739 }
740
741 pub fn nanosecond_time_bounds(
754 parent_time_nanos: i64,
755 elapsed_slot_duration_nanos: u128,
756 ) -> (i64, i64) {
757 let min_working_bank_time = parent_time_nanos.saturating_add(1);
758 let max_working_bank_time_offset = elapsed_slot_duration_nanos
759 .saturating_mul(2)
760 .min(i64::MAX as u128) as i64;
761 let max_working_bank_time = parent_time_nanos.saturating_add(max_working_bank_time_offset);
762
763 (min_working_bank_time, max_working_bank_time)
764 }
765
766 pub fn update_bank_with_footer_fields(
767 bank: &Bank,
768 block_producer_time_nanos: i64,
769 bank_hash: Option<Hash>,
770 reward_cert: Option<ValidatedRewardCert>,
771 final_cert_input: Option<(&HashSet<Pubkey>, Slot)>,
772 ) -> Result<(), BankFooterError> {
773 bank.update_clock_from_footer(block_producer_time_nanos);
774 calc_vote_rewards_update_vote_states(
775 bank,
776 reward_cert,
777 final_cert_input,
778 block_producer_time_nanos,
779 )?;
780
781 if let Some(hash) = bank_hash {
782 bank.set_expected_bank_hash(hash);
784 }
785 Ok(())
786 }
787}
788
789#[cfg(test)]
790mod tests {
791 use {
792 super::*,
793 crate::{
794 bank::{Bank, SlotLeader},
795 bank_forks::BankForks,
796 genesis_utils::{activate_all_features_alpenglow, create_genesis_config},
797 },
798 rand::Rng,
799 solana_bls_signatures::{BLS_SIGNATURE_AFFINE_SIZE, Signature as BLSSignature},
800 solana_clock::DEFAULT_MS_PER_SLOT,
801 solana_entry::{
802 block_component::{
803 BlockFooterV1, BlockHeaderV1, UpdateParentV1, VersionedUpdateParent,
804 },
805 entry::Entry,
806 },
807 solana_hash::Hash,
808 std::{
809 assert_matches,
810 sync::{Arc, RwLock},
811 },
812 };
813
814 const DEFAULT_NS_PER_SLOT: u64 = DEFAULT_MS_PER_SLOT * 1_000_000;
815
816 fn create_test_bank() -> (Arc<Bank>, Arc<RwLock<BankForks>>) {
817 let genesis_config_info = create_genesis_config(10_000);
818 Bank::new_with_bank_forks_for_tests(&genesis_config_info.genesis_config)
819 }
820
821 fn create_test_bank_alpenglow() -> (Arc<Bank>, Arc<RwLock<BankForks>>) {
822 let mut genesis_config_info = create_genesis_config(10_000);
823 activate_all_features_alpenglow(&mut genesis_config_info.genesis_config);
824 Bank::new_with_bank_forks_for_tests(&genesis_config_info.genesis_config)
825 }
826
827 fn create_child_bank(
828 bank_forks: &RwLock<BankForks>,
829 parent: &Arc<Bank>,
830 slot: u64,
831 ) -> Arc<Bank> {
832 Bank::new_from_parent_with_bank_forks(
833 bank_forks,
834 parent.clone(),
835 SlotLeader::new_unique(),
836 slot,
837 )
838 }
839
840 fn test_genesis_cert_marker() -> GenesisCertBlockMarker {
841 GenesisCertBlockMarker {
842 slot: 0,
843 block_id: Hash::default(),
844 bls_signature: BLSSignature([0; BLS_SIGNATURE_AFFINE_SIZE]),
845 bitmap: vec![],
846 }
847 }
848
849 fn post_migration_status_with_genesis_slot(genesis_slot: Slot) -> MigrationStatus {
850 let migration_status = MigrationStatus::default();
851 let migration_slot = migration_status.record_feature_activation(0);
852 assert!(genesis_slot < migration_slot);
853
854 let genesis_block = Block {
855 slot: genesis_slot,
856 block_id: Hash::default(),
857 };
858 migration_status.set_genesis_block(genesis_block);
859 let cert = Arc::new(GenesisCert {
860 block: genesis_block,
861 signature: CertSignature {
862 signature: BLSSignature([0; BLS_SIGNATURE_AFFINE_SIZE]),
863 bitmap: vec![],
864 },
865 });
866 migration_status.set_genesis_certificate(cert);
867 migration_status.enable_alpenglow_during_startup();
868
869 migration_status
870 }
871
872 fn processor_after_header() -> BlockComponentProcessor {
873 BlockComponentProcessor {
874 stage: BlockComponentStage::AcceptingGenesisOrEntries,
875 ..BlockComponentProcessor::default()
876 }
877 }
878
879 fn processor_after_footer() -> BlockComponentProcessor {
880 BlockComponentProcessor {
881 stage: BlockComponentStage::AcceptingAlpentick,
882 ..BlockComponentProcessor::default()
883 }
884 }
885
886 fn processor_done() -> BlockComponentProcessor {
887 BlockComponentProcessor {
888 stage: BlockComponentStage::Done,
889 ..BlockComponentProcessor::default()
890 }
891 }
892
893 fn alpentick(num_hashes: u64) -> [Entry; 1] {
894 [Entry::new(&Hash::default(), num_hashes, vec![])]
895 }
896
897 #[test]
898 fn test_missing_header_error_on_entry_batch() {
899 let migration_status = MigrationStatus::post_migration_status();
900 let mut processor = BlockComponentProcessor::default();
901
902 let result = processor.on_entry_batch(&migration_status, 1, &[], false);
904 assert!(matches!(
905 result,
906 Err(BlockComponentProcessorError::MissingParentMarker)
907 ));
908 }
909
910 #[test]
911 fn test_missing_header_error_on_genesis_certificate() {
912 let migration_status = MigrationStatus::post_migration_status();
913 let mut processor = BlockComponentProcessor::default();
914 let marker =
915 VersionedBlockMarker::from_genesis_cert_block_marker(test_genesis_cert_marker());
916
917 let (parent, bank_forks) = create_test_bank();
918 let bank = create_child_bank(&bank_forks, &parent, 1);
919 let shred_version = rand::rng().random();
920
921 let result = processor.on_marker(
922 bank,
923 parent,
924 shred_version,
925 marker,
926 false,
927 None,
928 &migration_status,
929 );
930 assert!(matches!(
931 result,
932 Err(BlockComponentProcessorError::MissingParentMarker)
933 ));
934 }
935
936 #[test]
937 fn test_genesis_certificate_after_entry_batch_errors() {
938 let migration_status = MigrationStatus::post_migration_status();
939 let mut processor = BlockComponentProcessor::default();
940 let (parent, bank_forks) = create_test_bank();
941 let bank = create_child_bank(&bank_forks, &parent, 1);
942 let shred_version = rand::rng().random();
943
944 let header = VersionedBlockMarker::from_block_header(BlockHeaderV1 {
945 parent_slot: 0,
946 parent_block_id: Hash::default(),
947 });
948 processor
949 .on_marker(
950 bank.clone(),
951 parent.clone(),
952 shred_version,
953 header,
954 false,
955 None,
956 &migration_status,
957 )
958 .unwrap();
959 processor
960 .on_entry_batch(&migration_status, bank.slot(), &[], false)
961 .unwrap();
962
963 let marker =
964 VersionedBlockMarker::from_genesis_cert_block_marker(test_genesis_cert_marker());
965 let result = processor.on_marker(
966 bank,
967 parent,
968 shred_version,
969 marker,
970 false,
971 None,
972 &migration_status,
973 );
974 assert!(matches!(
975 result,
976 Err(BlockComponentProcessorError::GenesisCertificateOutOfOrder)
977 ));
978 }
979
980 #[test]
981 fn test_genesis_certificate_immediately_after_header_passes_order_check() {
982 let migration_status = MigrationStatus::post_migration_status();
983 let mut processor = BlockComponentProcessor::default();
984 let (parent, bank_forks) = create_test_bank();
985 let bank = create_child_bank(&bank_forks, &parent, 1);
986 let shred_version = rand::rng().random();
987
988 let header = VersionedBlockMarker::from_block_header(BlockHeaderV1 {
989 parent_slot: 0,
990 parent_block_id: Hash::default(),
991 });
992 processor
993 .on_marker(
994 bank.clone(),
995 parent.clone(),
996 shred_version,
997 header,
998 false,
999 None,
1000 &migration_status,
1001 )
1002 .unwrap();
1003
1004 let marker =
1005 VersionedBlockMarker::from_genesis_cert_block_marker(test_genesis_cert_marker());
1006 let result = processor.on_marker(
1007 bank,
1008 parent,
1009 shred_version,
1010 marker,
1011 false,
1012 None,
1013 &migration_status,
1014 );
1015 assert!(matches!(
1016 result,
1017 Err(BlockComponentProcessorError::GenesisCertificateInAlpenglowCluster)
1018 ));
1019 }
1020
1021 #[test]
1022 fn test_missing_footer_error_on_slot_full() {
1023 let migration_status = MigrationStatus::post_migration_status();
1024 let processor = processor_after_header();
1025
1026 let result = processor.on_final(&migration_status, 1, 0);
1028 assert!(matches!(
1029 result,
1030 Err(BlockComponentProcessorError::MissingBlockFooter)
1031 ));
1032 }
1033
1034 #[test]
1035 fn test_header_during_tower_bft_slot() {
1036 let migration_status = MigrationStatus::default();
1037 migration_status.record_feature_activation(0);
1038 let mut processor = BlockComponentProcessor::default();
1039 let (parent, bank_forks) = create_test_bank();
1040 let bank = create_child_bank(&bank_forks, &parent, 1);
1041 let marker = VersionedBlockMarker::from_block_header(BlockHeaderV1 {
1042 parent_slot: parent.slot(),
1043 parent_block_id: Hash::default(),
1044 });
1045
1046 assert!(!migration_status.should_allow_block_markers(bank.slot()));
1047 processor
1048 .on_marker(
1049 bank.clone(),
1050 parent,
1051 rand::rng().random(),
1052 marker,
1053 false,
1054 None,
1055 &migration_status,
1056 )
1057 .unwrap();
1058
1059 assert_matches!(
1060 processor.on_final(&migration_status, bank.slot(), bank.parent_slot()),
1061 Err(BlockComponentProcessorError::BlockComponentPreMigration)
1062 );
1063 }
1064
1065 #[test]
1066 fn test_first_alpenglow_block_requires_genesis_certificate_marker() {
1067 let migration_status = post_migration_status_with_genesis_slot(1);
1068 let processor = processor_after_footer();
1069
1070 let result = processor.on_final(&migration_status, 2, 1);
1071 assert!(matches!(
1072 result,
1073 Err(BlockComponentProcessorError::MissingGenesisCertificateMarker)
1074 ));
1075 }
1076
1077 #[test]
1078 fn test_first_alpenglow_block_with_genesis_certificate_marker_succeeds() {
1079 let migration_status = post_migration_status_with_genesis_slot(1);
1080 let (genesis_bank, bank_forks) = create_test_bank();
1081 let parent = create_child_bank(&bank_forks, &genesis_bank, 1);
1082 let parent_block_id = Hash::new_unique();
1083 parent.set_block_id(Some(parent_block_id));
1084 let bank = create_child_bank(&bank_forks, &parent, 2);
1085 let genesis_marker = GenesisCertBlockMarker {
1086 slot: parent.slot(),
1087 block_id: parent_block_id,
1088 bls_signature: BLSSignature([0; BLS_SIGNATURE_AFFINE_SIZE]),
1089 bitmap: vec![],
1090 };
1091 let mut processor = processor_after_header();
1092 bank.set_hashes_per_tick(Some(42));
1093 assert!(bank.hashes_per_tick().is_some());
1094
1095 processor
1096 .on_genesis_cert_block_marker_leader(bank.clone(), genesis_marker, &migration_status)
1097 .unwrap();
1098 assert!(bank.hashes_per_tick().is_none());
1099 processor.stage = BlockComponentStage::Done;
1100 assert!(processor.on_final(&migration_status, 2, 1).is_ok());
1101 }
1102
1103 #[test]
1104 fn test_genesis_certificate_marker_aborts_tower_bank_during_migration() {
1105 let migration_status = MigrationStatus::default();
1106 migration_status.record_feature_activation(0);
1107 let (genesis_bank, bank_forks) = create_test_bank();
1108 let parent = create_child_bank(&bank_forks, &genesis_bank, 1);
1109 let parent_block_id = Hash::new_unique();
1110 parent.set_block_id(Some(parent_block_id));
1111 let bank = create_child_bank(&bank_forks, &parent, 2);
1112 let genesis_marker = GenesisCertBlockMarker {
1113 slot: parent.slot(),
1114 block_id: parent_block_id,
1115 bls_signature: BLSSignature([0; BLS_SIGNATURE_AFFINE_SIZE]),
1116 bitmap: vec![],
1117 };
1118 let mut processor = processor_after_header();
1119 bank.set_hashes_per_tick(Some(42));
1120 let tower_hashes_per_tick = bank.hashes_per_tick();
1121 assert!(tower_hashes_per_tick.is_some());
1122
1123 assert_matches!(
1124 processor.on_genesis_cert_block_marker_leader(
1125 bank.clone(),
1126 genesis_marker,
1127 &migration_status,
1128 ),
1129 Err(BlockComponentProcessorError::AlpenglowMigrationTransition)
1130 );
1131
1132 assert!(migration_status.is_ready_to_enable());
1133 assert_eq!(bank.hashes_per_tick(), tower_hashes_per_tick);
1134 assert!(bank.get_alpenglow_genesis_certificate().is_some());
1135 }
1136
1137 #[test]
1138 fn test_first_alpenglow_block_genesis_slot_zero_skips_genesis_certificate_marker_check() {
1139 let migration_status = MigrationStatus::post_migration_status();
1140 let processor = processor_done();
1141
1142 assert!(processor.on_final(&migration_status, 1, 0).is_ok());
1143 }
1144
1145 #[test]
1146 fn test_multiple_headers_error() {
1147 let mut processor = BlockComponentProcessor::default();
1148 let header = VersionedBlockHeader::V1(BlockHeaderV1 {
1149 parent_slot: 0,
1150 parent_block_id: Hash::default(),
1151 });
1152
1153 assert!(processor.on_header(&header, 0).is_ok());
1155
1156 let result = processor.on_header(&header, 0);
1158 assert!(matches!(
1159 result,
1160 Err(BlockComponentProcessorError::MultipleBlockHeaders)
1161 ));
1162 }
1163
1164 #[test]
1165 fn test_multiple_footers_error() {
1166 let my_pubkey = Pubkey::new_unique();
1167 let mut processor = processor_after_header();
1168
1169 let (parent, bank_forks) = create_test_bank();
1170 let bank = create_child_bank(&bank_forks, &parent, 1);
1171 let shred_version = rand::rng().random();
1172
1173 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1175 let footer_time_nanos = parent_time_nanos + 400_000_000; let footer = VersionedBlockFooter::V1(BlockFooterV1 {
1178 bank_hash: Hash::new_unique(),
1179 block_producer_time_nanos: footer_time_nanos as u64,
1180 block_user_agent: vec![],
1181 block_final_cert: None,
1182 skip_reward_cert: None,
1183 notar_reward_cert: None,
1184 });
1185
1186 processor
1188 .on_footer(
1189 &my_pubkey,
1190 bank.clone(),
1191 parent.clone(),
1192 shred_version,
1193 footer.clone(),
1194 None,
1195 )
1196 .unwrap();
1197
1198 let err = processor
1200 .on_footer(&my_pubkey, bank, parent, shred_version, footer, None)
1201 .unwrap_err();
1202 assert!(matches!(
1203 err,
1204 BlockComponentProcessorError::MultipleBlockFooters
1205 ));
1206 }
1207
1208 #[test]
1209 fn test_on_footer_sets_timestamp() {
1210 let my_pubkey = Pubkey::new_unique();
1211 let mut processor = processor_after_header();
1212
1213 let (parent, bank_forks) = create_test_bank();
1214 let bank = create_child_bank(&bank_forks, &parent, 1);
1215 let shred_version = rand::rng().random();
1216
1217 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1219 let footer_time_nanos = parent_time_nanos + 200_000_000; let expected_time_secs = footer_time_nanos / 1_000_000_000;
1221
1222 let footer = VersionedBlockFooter::V1(BlockFooterV1 {
1223 bank_hash: Hash::new_unique(),
1224 block_producer_time_nanos: footer_time_nanos as u64,
1225 block_user_agent: vec![],
1226 block_final_cert: None,
1227 skip_reward_cert: None,
1228 notar_reward_cert: None,
1229 });
1230
1231 processor
1232 .on_footer(
1233 &my_pubkey,
1234 bank.clone(),
1235 parent,
1236 shred_version,
1237 footer,
1238 None,
1239 )
1240 .unwrap();
1241
1242 assert_eq!(processor.stage, BlockComponentStage::AcceptingAlpentick);
1243
1244 assert_eq!(bank.clock().unix_timestamp, expected_time_secs);
1246 }
1247
1248 #[test]
1249 fn test_on_header_sets_flag() {
1250 let mut processor = BlockComponentProcessor::default();
1251 let header = VersionedBlockHeader::V1(BlockHeaderV1 {
1252 parent_slot: 0,
1253 parent_block_id: Hash::default(),
1254 });
1255
1256 processor.on_header(&header, 0).unwrap();
1257 assert_eq!(
1258 processor.stage,
1259 BlockComponentStage::AcceptingGenesisOrEntries
1260 );
1261 }
1262
1263 #[test]
1264 fn test_on_header_parent_slot_mismatch_error() {
1265 let mut processor = BlockComponentProcessor::default();
1266 let header = VersionedBlockHeader::V1(BlockHeaderV1 {
1267 parent_slot: 2,
1268 parent_block_id: Hash::default(),
1269 });
1270
1271 assert!(matches!(
1272 processor.on_header(&header, 0),
1273 Err(BlockComponentProcessorError::HeaderParentSlotMismatch {
1274 header_parent_slot: 2,
1275 bank_parent_slot: 0,
1276 })
1277 ));
1278 }
1279
1280 #[test]
1281 fn test_on_marker_processes_header() {
1282 let migration_status = MigrationStatus::post_migration_status();
1283 let mut processor = BlockComponentProcessor::default();
1284 let marker = VersionedBlockMarker::from_block_header(BlockHeaderV1 {
1285 parent_slot: 0,
1286 parent_block_id: Hash::default(),
1287 });
1288
1289 let (parent, bank_forks) = create_test_bank();
1290 let bank = create_child_bank(&bank_forks, &parent, 1);
1291 let shred_version = rand::rng().random();
1292
1293 processor
1294 .on_marker(
1295 bank,
1296 parent,
1297 shred_version,
1298 marker,
1299 false,
1300 None,
1301 &migration_status,
1302 )
1303 .unwrap();
1304 assert_eq!(
1305 processor.stage,
1306 BlockComponentStage::AcceptingGenesisOrEntries
1307 );
1308 }
1309
1310 #[test]
1311 fn test_on_marker_rejects_header_parent_slot_mismatch() {
1312 let migration_status = MigrationStatus::post_migration_status();
1313 let mut processor = BlockComponentProcessor::default();
1314 let marker = VersionedBlockMarker::from_block_header(BlockHeaderV1 {
1315 parent_slot: 7, parent_block_id: Hash::default(),
1317 });
1318
1319 let (parent, bank_forks) = create_test_bank();
1320 let bank = create_child_bank(&bank_forks, &parent, 1);
1321 let shred_version = rand::rng().random();
1322
1323 assert!(matches!(
1324 processor.on_marker(
1325 bank,
1326 parent,
1327 shred_version,
1328 marker,
1329 false,
1330 None,
1331 &migration_status
1332 ),
1333 Err(BlockComponentProcessorError::HeaderParentSlotMismatch {
1334 header_parent_slot: 7,
1335 bank_parent_slot: 0,
1336 })
1337 ));
1338 }
1339
1340 #[test]
1341 fn test_on_marker_processes_footer() {
1342 let migration_status = MigrationStatus::post_migration_status();
1343 let mut processor = processor_after_header();
1344
1345 let (parent, bank_forks) = create_test_bank();
1346 let bank = create_child_bank(&bank_forks, &parent, 1);
1347 let shred_version = rand::rng().random();
1348
1349 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1351 let footer_time_nanos = parent_time_nanos + 300_000_000; let expected_time_secs = footer_time_nanos / 1_000_000_000;
1353
1354 let marker = VersionedBlockMarker::from_block_footer(BlockFooterV1 {
1355 bank_hash: Hash::new_unique(),
1356 block_producer_time_nanos: footer_time_nanos as u64,
1357 block_user_agent: vec![],
1358 block_final_cert: None,
1359 skip_reward_cert: None,
1360 notar_reward_cert: None,
1361 });
1362
1363 processor
1364 .on_marker(
1365 bank.clone(),
1366 parent,
1367 shred_version,
1368 marker,
1369 false,
1370 None,
1371 &migration_status,
1372 )
1373 .unwrap();
1374 assert_eq!(processor.stage, BlockComponentStage::AcceptingAlpentick);
1375
1376 assert_eq!(bank.clock().unix_timestamp, expected_time_secs);
1378 }
1379
1380 #[test]
1381 fn test_complete_workflow_success() {
1382 let migration_status = MigrationStatus::post_migration_status();
1383 let mut processor = BlockComponentProcessor::default();
1384 let (parent, bank_forks) = create_test_bank();
1385 let bank = create_child_bank(&bank_forks, &parent, 1);
1386 let shred_version = rand::rng().random();
1387
1388 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1390 let footer_time_nanos = parent_time_nanos + 100_000_000; let expected_time_secs = footer_time_nanos / 1_000_000_000;
1392
1393 let header = VersionedBlockHeader::V1(BlockHeaderV1 {
1395 parent_slot: 0,
1396 parent_block_id: Hash::default(),
1397 });
1398 processor.on_header(&header, bank.parent_slot()).unwrap();
1399
1400 processor
1402 .on_entry_batch(&migration_status, 1, &[], false)
1403 .unwrap();
1404
1405 let footer = VersionedBlockFooter::V1(BlockFooterV1 {
1407 bank_hash: Hash::new_unique(),
1408 block_producer_time_nanos: footer_time_nanos as u64,
1409 block_user_agent: vec![],
1410 block_final_cert: None,
1411 skip_reward_cert: None,
1412 notar_reward_cert: None,
1413 });
1414 processor
1415 .on_footer(
1416 &migration_status.my_pubkey(),
1417 bank.clone(),
1418 parent.clone(),
1419 shred_version,
1420 footer,
1421 None,
1422 )
1423 .unwrap();
1424
1425 assert_eq!(bank.clock().unix_timestamp, expected_time_secs);
1427
1428 let result = processor.on_entry_batch(&migration_status, 1, &[], false);
1430 assert_matches!(
1431 result,
1432 Err(BlockComponentProcessorError::EntryBatchAfterBlockFooter)
1433 );
1434 }
1435
1436 #[test]
1437 fn test_alpentick_position_validation() {
1438 let migration_status = MigrationStatus::post_migration_status();
1439 let mut processor = processor_after_footer();
1440 let good_alpentick = alpentick(1);
1441
1442 processor
1443 .on_entry_batch(&migration_status, 1, &good_alpentick, true)
1444 .unwrap();
1445 assert_matches!(
1446 processor.on_entry_batch(&migration_status, 1, &good_alpentick, true),
1447 Err(BlockComponentProcessorError::InvalidAlpentickPosition)
1448 );
1449
1450 let mut processor = BlockComponentProcessor::default();
1451 assert_matches!(
1452 processor.on_entry_batch(&migration_status, 1, &good_alpentick, true),
1453 Err(BlockComponentProcessorError::MissingParentMarker)
1454 );
1455
1456 let mut processor = processor_after_footer();
1457 let bad_alpentick = alpentick(2);
1458 assert_matches!(
1459 processor.on_entry_batch(&migration_status, 1, &bad_alpentick, true),
1460 Err(BlockComponentProcessorError::EntryBatchAfterBlockFooter)
1461 );
1462
1463 let migration_status = MigrationStatus::default();
1464 let mut processor = BlockComponentProcessor::default();
1465 processor
1466 .on_entry_batch(&migration_status, 1, &good_alpentick, true)
1467 .unwrap();
1468 }
1469
1470 #[test]
1471 fn test_block_marker_detected_pre_migration() {
1472 let migration_status = MigrationStatus::default();
1473 let mut processor = BlockComponentProcessor::default();
1474 let (parent, bank_forks) = create_test_bank();
1475 let bank = create_child_bank(&bank_forks, &parent, 1);
1476 let shred_version = rand::rng().random();
1477
1478 let marker = VersionedBlockMarker::from_block_header(BlockHeaderV1 {
1480 parent_slot: 0,
1481 parent_block_id: Hash::default(),
1482 });
1483
1484 let err = processor
1485 .on_marker(
1486 bank,
1487 parent,
1488 shred_version,
1489 marker,
1490 false,
1491 None,
1492 &migration_status,
1493 )
1494 .unwrap_err();
1495 assert!(matches!(
1496 err,
1497 BlockComponentProcessorError::BlockComponentPreMigration
1498 ));
1499 }
1500
1501 #[test]
1502 fn test_footer_and_update_parent_rejected_pre_migration() {
1503 let migration_status = MigrationStatus::default();
1504 let (parent, bank_forks) = create_test_bank();
1505 let bank = create_child_bank(&bank_forks, &parent, 1);
1506 let shred_version = rand::rng().random();
1507
1508 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1509 let footer_marker = VersionedBlockMarker::from_block_footer(BlockFooterV1 {
1510 bank_hash: Hash::new_unique(),
1511 block_producer_time_nanos: (parent_time_nanos + 500_000_000) as u64,
1512 block_user_agent: vec![],
1513 block_final_cert: None,
1514 skip_reward_cert: None,
1515 notar_reward_cert: None,
1516 });
1517
1518 let mut processor = BlockComponentProcessor::default();
1519 assert!(matches!(
1520 processor
1521 .on_marker(
1522 bank.clone(),
1523 parent.clone(),
1524 shred_version,
1525 footer_marker,
1526 false,
1527 None,
1528 &migration_status
1529 )
1530 .unwrap_err(),
1531 BlockComponentProcessorError::BlockComponentPreMigration
1532 ));
1533
1534 let update_parent_marker = VersionedBlockMarker::from_update_parent(UpdateParentV1 {
1535 new_parent_slot: 0,
1536 new_parent_block_id: Hash::default(),
1537 });
1538
1539 let mut processor = BlockComponentProcessor::default();
1540 assert!(matches!(
1541 processor
1542 .on_marker(
1543 bank,
1544 parent,
1545 shred_version,
1546 update_parent_marker,
1547 false,
1548 None,
1549 &migration_status
1550 )
1551 .unwrap_err(),
1552 BlockComponentProcessorError::BlockComponentPreMigration
1553 ));
1554 }
1555
1556 #[test]
1557 fn test_entry_batch_pre_migration_succeeds() {
1558 let migration_status = MigrationStatus::default();
1559 let mut processor = BlockComponentProcessor::default();
1560
1561 let result = processor.on_entry_batch(&migration_status, 1, &[], false);
1563 assert!(result.is_ok());
1564
1565 let result = processor.on_entry_batch(&migration_status, 1, &[], false);
1567 assert!(result.is_ok());
1568
1569 assert!(processor.on_final(&migration_status, 1, 0).is_ok());
1571 }
1572
1573 #[test]
1574 fn test_complete_workflow_post_migration() {
1575 let migration_status = MigrationStatus::post_migration_status();
1576 let mut processor = BlockComponentProcessor::default();
1577 let (parent, bank_forks) = create_test_bank();
1578 let bank = create_child_bank(&bank_forks, &parent, 1);
1579 let shred_version = rand::rng().random();
1580
1581 let header_marker = VersionedBlockMarker::from_block_header(BlockHeaderV1 {
1583 parent_slot: 0,
1584 parent_block_id: Hash::default(),
1585 });
1586 processor
1587 .on_marker(
1588 bank.clone(),
1589 parent.clone(),
1590 shred_version,
1591 header_marker,
1592 false,
1593 None,
1594 &migration_status,
1595 )
1596 .unwrap();
1597
1598 processor
1600 .on_entry_batch(&migration_status, 1, &[], false)
1601 .unwrap();
1602
1603 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1605 let footer_time_nanos = parent_time_nanos + 500_000_000; let expected_time_secs = footer_time_nanos / 1_000_000_000;
1607
1608 let footer_marker = VersionedBlockMarker::from_block_footer(BlockFooterV1 {
1610 bank_hash: Hash::new_unique(),
1611 block_producer_time_nanos: footer_time_nanos as u64,
1612 block_user_agent: vec![],
1613 block_final_cert: None,
1614 skip_reward_cert: None,
1615 notar_reward_cert: None,
1616 });
1617 processor
1618 .on_marker(
1619 bank.clone(),
1620 parent,
1621 shred_version,
1622 footer_marker,
1623 false,
1624 None,
1625 &migration_status,
1626 )
1627 .unwrap();
1628
1629 assert_eq!(bank.clock().unix_timestamp, expected_time_secs);
1631
1632 let result = processor.on_entry_batch(&migration_status, 1, &[], false);
1634 assert_matches!(
1635 result,
1636 Err(BlockComponentProcessorError::EntryBatchAfterBlockFooter)
1637 );
1638 }
1639
1640 #[test]
1641 fn test_footer_without_header_errors() {
1642 let my_pubkey = Pubkey::new_unique();
1643 let mut processor = BlockComponentProcessor::default();
1644 let (parent, bank_forks) = create_test_bank();
1645 let bank = create_child_bank(&bank_forks, &parent, 1);
1646 let shred_version = rand::rng().random();
1647
1648 let footer = VersionedBlockFooter::V1(BlockFooterV1 {
1649 bank_hash: Hash::new_unique(),
1650 block_producer_time_nanos: 1_000_000_000,
1651 block_user_agent: vec![],
1652 block_final_cert: None,
1653 skip_reward_cert: None,
1654 notar_reward_cert: None,
1655 });
1656
1657 let err = processor
1659 .on_footer(&my_pubkey, bank, parent, shred_version, footer, None)
1660 .unwrap_err();
1661 assert!(matches!(
1662 err,
1663 BlockComponentProcessorError::MissingParentMarker
1664 ));
1665 }
1666
1667 #[test]
1668 fn test_marker_with_footer_at_slot_full() {
1669 let migration_status = MigrationStatus::post_migration_status();
1670 let mut processor = processor_after_header();
1671 let (parent, bank_forks) = create_test_bank();
1672 let bank = create_child_bank(&bank_forks, &parent, 1);
1673 let shred_version = rand::rng().random();
1674
1675 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1677 let footer_time_nanos = parent_time_nanos + 600_000_000; let expected_time_secs = footer_time_nanos / 1_000_000_000;
1679
1680 let footer_marker = VersionedBlockMarker::from_block_footer(BlockFooterV1 {
1682 bank_hash: Hash::new_unique(),
1683 block_producer_time_nanos: footer_time_nanos as u64,
1684 block_user_agent: vec![],
1685 block_final_cert: None,
1686 skip_reward_cert: None,
1687 notar_reward_cert: None,
1688 });
1689
1690 processor
1692 .on_marker(
1693 bank.clone(),
1694 parent,
1695 shred_version,
1696 footer_marker,
1697 false,
1698 None,
1699 &migration_status,
1700 )
1701 .unwrap();
1702 assert_eq!(processor.stage, BlockComponentStage::AcceptingAlpentick);
1703
1704 assert_eq!(bank.clock().unix_timestamp, expected_time_secs);
1706 }
1707
1708 #[test]
1709 fn test_entry_batch_with_header_not_full_succeeds() {
1710 let migration_status = MigrationStatus::post_migration_status();
1711 let mut processor = processor_after_header();
1712
1713 let result = processor.on_entry_batch(&migration_status, 1, &[], false);
1715 assert!(result.is_ok());
1716 }
1717
1718 #[test]
1719 fn test_footer_sets_epoch_start_timestamp_on_epoch_change() {
1720 let my_pubkey = Pubkey::new_unique();
1721 let mut processor = processor_after_header();
1722 let shred_version = rand::rng().random();
1723
1724 let genesis_config_info = create_genesis_config(10_000);
1726 let (genesis_bank, bank_forks) =
1727 Bank::new_with_bank_forks_for_tests(&genesis_config_info.genesis_config);
1728
1729 let epoch_schedule = genesis_bank.epoch_schedule();
1731 let first_slot_in_epoch_1 = epoch_schedule.get_first_slot_in_epoch(1);
1732
1733 let mut parent = genesis_bank.clone();
1735 for slot in 1..first_slot_in_epoch_1 {
1736 parent = create_child_bank(&bank_forks, &parent, slot);
1737 }
1738
1739 let bank = create_child_bank(&bank_forks, &parent, first_slot_in_epoch_1);
1741
1742 assert_eq!(bank.epoch(), 1);
1744
1745 let parent_slot = parent.slot();
1747 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1748 let current_slot = bank.slot();
1749 let elapsed_slot_duration_nanos =
1750 bank.slot_range_duration_nanos(parent_slot.saturating_add(1), current_slot);
1751
1752 let (lower_bound, upper_bound) = BlockComponentProcessor::nanosecond_time_bounds(
1754 parent_time_nanos,
1755 elapsed_slot_duration_nanos,
1756 );
1757 let footer_time_nanos = (lower_bound + upper_bound) / 2;
1758 let expected_time_secs = footer_time_nanos / 1_000_000_000;
1759
1760 let footer = VersionedBlockFooter::V1(BlockFooterV1 {
1761 bank_hash: Hash::new_unique(),
1762 block_producer_time_nanos: footer_time_nanos as u64,
1763 block_user_agent: vec![],
1764 block_final_cert: None,
1765 skip_reward_cert: None,
1766 notar_reward_cert: None,
1767 });
1768
1769 processor
1770 .on_footer(
1771 &my_pubkey,
1772 bank.clone(),
1773 parent,
1774 shred_version,
1775 footer,
1776 None,
1777 )
1778 .unwrap();
1779
1780 assert_eq!(bank.clock().unix_timestamp, expected_time_secs);
1782
1783 assert_eq!(bank.clock().epoch_start_timestamp, expected_time_secs);
1785 }
1786
1787 fn test_clock_bounds_helper(
1789 slot_gap: u64,
1790 timestamp_fn: impl FnOnce(i64, i64, i64) -> i64,
1791 should_pass: bool,
1792 ) {
1793 let my_pubkey = Pubkey::new_unique();
1794 let mut processor = processor_after_header();
1795 let shred_version = rand::rng().random();
1796
1797 let (parent, bank_forks) = create_test_bank_alpenglow();
1798 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1799
1800 parent.update_clock_from_footer(parent_time_nanos);
1802
1803 let bank: Arc<Bank> = create_child_bank(&bank_forks, &parent, slot_gap);
1804 let elapsed_slot_duration_nanos = bank.slot_range_duration_nanos(1, slot_gap);
1805
1806 let (lower_bound, upper_bound) = BlockComponentProcessor::nanosecond_time_bounds(
1807 parent_time_nanos,
1808 elapsed_slot_duration_nanos,
1809 );
1810
1811 let footer_time_nanos = timestamp_fn(parent_time_nanos, lower_bound, upper_bound);
1812
1813 let footer = VersionedBlockFooter::V1(BlockFooterV1 {
1814 bank_hash: Hash::new_unique(),
1815 block_producer_time_nanos: footer_time_nanos as u64,
1816 block_user_agent: vec![],
1817 block_final_cert: None,
1818 skip_reward_cert: None,
1819 notar_reward_cert: None,
1820 });
1821
1822 let result = processor.on_footer(&my_pubkey, bank, parent, shred_version, footer, None);
1823 if should_pass {
1824 result.unwrap();
1825 } else {
1826 assert!(matches!(
1827 result.unwrap_err(),
1828 BlockComponentProcessorError::NanosecondClockOutOfBounds
1829 ));
1830 }
1831 }
1832
1833 #[test]
1834 fn test_clock_bounds_at_minimum() {
1835 test_clock_bounds_helper(1, |_, lower, _| lower, true);
1836 }
1837
1838 #[test]
1839 fn test_clock_bounds_at_maximum() {
1840 test_clock_bounds_helper(1, |_, _, upper| upper, true);
1841 }
1842
1843 #[test]
1844 fn test_clock_bounds_below_minimum() {
1845 test_clock_bounds_helper(1, |_, lower, _| lower - 1, false);
1846 }
1847
1848 #[test]
1849 fn test_clock_bounds_above_maximum() {
1850 test_clock_bounds_helper(1, |_, _, upper| upper + 1, false);
1851 }
1852
1853 #[test]
1854 fn test_clock_bounds_multi_slot_gap() {
1855 test_clock_bounds_helper(5, |_, lower, _| lower + 2_000_000_000, true);
1858 }
1859
1860 #[test]
1861 fn test_clock_bounds_multi_slot_gap_exceeds() {
1862 test_clock_bounds_helper(5, |_, _, upper| upper + 1_000_000_000, false);
1864 }
1865
1866 #[test]
1867 fn test_clock_bounds_timestamp_equals_parent() {
1868 test_clock_bounds_helper(1, |parent_time, _, _| parent_time, false);
1870 }
1871
1872 #[test]
1873 fn test_clock_bounds_without_parent_nanosecond_clock_rejects_out_of_bounds() {
1874 let my_pubkey = Pubkey::new_unique();
1875 let mut processor = processor_after_header();
1876 let shred_version = rand::rng().random();
1877
1878 let (parent, bank_forks) = create_test_bank_alpenglow();
1879 assert_eq!(parent.get_nanosecond_clock(), None);
1880
1881 let bank = create_child_bank(&bank_forks, &parent, 1);
1882 let parent_time_nanos = bank.clock().unix_timestamp.saturating_mul(1_000_000_000);
1883 let elapsed_slot_duration_nanos =
1884 bank.slot_range_duration_nanos(parent.slot().saturating_add(1), bank.slot());
1885 let (_, upper_bound) = BlockComponentProcessor::nanosecond_time_bounds(
1886 parent_time_nanos,
1887 elapsed_slot_duration_nanos,
1888 );
1889
1890 let footer = VersionedBlockFooter::V1(BlockFooterV1 {
1891 bank_hash: Hash::new_unique(),
1892 block_producer_time_nanos: u64::try_from(upper_bound.saturating_add(1)).unwrap(),
1893 block_user_agent: vec![],
1894 block_final_cert: None,
1895 skip_reward_cert: None,
1896 notar_reward_cert: None,
1897 });
1898
1899 assert!(matches!(
1900 processor
1901 .on_footer(&my_pubkey, bank, parent, shred_version, footer, None)
1902 .unwrap_err(),
1903 BlockComponentProcessorError::NanosecondClockOutOfBounds
1904 ));
1905 }
1906
1907 #[test]
1908 fn test_clock_bounds_rejects_timestamp_above_i64() {
1909 let my_pubkey = Pubkey::new_unique();
1910 let mut processor = processor_after_header();
1911 let shred_version = rand::rng().random();
1912
1913 let (parent, bank_forks) = create_test_bank_alpenglow();
1914 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1915 parent.update_clock_from_footer(parent_time_nanos);
1916 let bank = create_child_bank(&bank_forks, &parent, 1);
1917
1918 let footer = VersionedBlockFooter::V1(BlockFooterV1 {
1919 bank_hash: Hash::new_unique(),
1920 block_producer_time_nanos: u64::MAX,
1921 block_user_agent: vec![],
1922 block_final_cert: None,
1923 skip_reward_cert: None,
1924 notar_reward_cert: None,
1925 });
1926
1927 assert!(matches!(
1928 processor
1929 .on_footer(&my_pubkey, bank, parent, shred_version, footer, None)
1930 .unwrap_err(),
1931 BlockComponentProcessorError::NanosecondClockOutOfBounds
1932 ));
1933 }
1934
1935 fn test_nanosecond_time_bounds_helper(
1937 parent_time_nanos: i64,
1938 elapsed_slot_duration_nanos: u128,
1939 expected_lower: i64,
1940 expected_upper: i64,
1941 ) {
1942 let (lower, upper) = BlockComponentProcessor::nanosecond_time_bounds(
1943 parent_time_nanos,
1944 elapsed_slot_duration_nanos,
1945 );
1946
1947 assert_eq!(lower, expected_lower);
1948 assert_eq!(upper, expected_upper);
1949 }
1950
1951 #[test]
1952 fn test_nanosecond_time_bounds_calculation() {
1953 let parent_slot = 10;
1958 let parent_time = 1_000_000_000_000; let working_slot = 15;
1960 let slot_delta = working_slot - parent_slot;
1961 test_nanosecond_time_bounds_helper(
1962 parent_time,
1963 u128::from(slot_delta).saturating_mul(u128::from(DEFAULT_NS_PER_SLOT)),
1964 parent_time + 1,
1965 parent_time + (2 * DEFAULT_NS_PER_SLOT * slot_delta) as i64,
1966 );
1967 }
1968
1969 #[test]
1970 fn test_nanosecond_time_bounds_same_slot() {
1971 let parent_time = 1_000_000_000_000;
1978 test_nanosecond_time_bounds_helper(parent_time, 0, parent_time + 1, parent_time);
1979 }
1980
1981 #[test]
1982 fn test_nanosecond_time_bounds_saturates_upper_bound() {
1983 let parent_time = i64::MAX - 5;
1984 let (lower, upper) =
1985 BlockComponentProcessor::nanosecond_time_bounds(parent_time, u128::MAX);
1986
1987 assert_eq!(lower, parent_time + 1);
1988 assert_eq!(upper, i64::MAX);
1989 }
1990
1991 #[test]
1992 fn test_initial_up_reject() {
1993 let mut processor = BlockComponentProcessor::default();
1994 let update_parent = VersionedUpdateParent::V1(UpdateParentV1 {
1995 new_parent_slot: 0,
1996 new_parent_block_id: Hash::default(),
1997 });
1998
1999 assert!(matches!(
2000 processor.on_update_parent(4, &update_parent, false),
2001 Err(BlockComponentProcessorError::UnexpectedInitialUpdateParent)
2002 ));
2003 assert_eq!(processor.stage, BlockComponentStage::PreParentMarker);
2004 }
2005
2006 #[test]
2007 fn test_update_parent_rejects_non_first_leader_window_slot() {
2008 let mut processor = BlockComponentProcessor::default();
2009 let update_parent = VersionedUpdateParent::V1(UpdateParentV1 {
2010 new_parent_slot: 0,
2011 new_parent_block_id: Hash::default(),
2012 });
2013
2014 assert!(matches!(
2015 processor.on_update_parent(5, &update_parent, true),
2016 Err(BlockComponentProcessorError::UpdateParentNotFirstInLeaderWindow(5))
2017 ));
2018 assert_eq!(processor.stage, BlockComponentStage::PreParentMarker);
2019 }
2020
2021 #[test]
2022 fn test_initial_up_ok() {
2023 let mut processor = BlockComponentProcessor::default();
2024 let update_parent = VersionedUpdateParent::V1(UpdateParentV1 {
2025 new_parent_slot: 0,
2026 new_parent_block_id: Hash::default(),
2027 });
2028
2029 processor.on_update_parent(4, &update_parent, true).unwrap();
2030 assert_eq!(
2031 processor.stage,
2032 BlockComponentStage::AcceptingEntriesOrFooter {
2033 parent_marker: EntryParentMarker::UpdateParent,
2034 }
2035 );
2036 }
2037
2038 #[test]
2039 fn test_update_parent_after_header_abandoned_bank() {
2040 let mut processor = BlockComponentProcessor::default();
2041 processor
2042 .on_header(
2043 &VersionedBlockHeader::V1(BlockHeaderV1 {
2044 parent_slot: 0,
2045 parent_block_id: Hash::default(),
2046 }),
2047 0,
2048 )
2049 .unwrap();
2050
2051 let update_parent = VersionedUpdateParent::V1(UpdateParentV1 {
2052 new_parent_slot: 0,
2053 new_parent_block_id: Hash::default(),
2054 });
2055
2056 assert!(matches!(
2057 processor.on_update_parent(4, &update_parent, false),
2058 Err(BlockComponentProcessorError::AbandonedBank(_))
2059 ));
2060 }
2061
2062 #[test]
2063 fn test_update_parent_after_footer_error() {
2064 let mut processor = processor_after_footer();
2065 let update_parent = VersionedUpdateParent::V1(UpdateParentV1 {
2066 new_parent_slot: 0,
2067 new_parent_block_id: Hash::default(),
2068 });
2069
2070 assert_matches!(
2071 processor.on_update_parent(4, &update_parent, false),
2072 Err(BlockComponentProcessorError::SpuriousUpdateParent)
2073 );
2074 }
2075
2076 #[test]
2077 fn test_multiple_update_parents_error() {
2078 let mut processor = BlockComponentProcessor::default();
2079 let update_parent = VersionedUpdateParent::V1(UpdateParentV1 {
2080 new_parent_slot: 0,
2081 new_parent_block_id: Hash::default(),
2082 });
2083
2084 processor.on_update_parent(4, &update_parent, true).unwrap();
2086
2087 assert_matches!(
2089 processor.on_update_parent(4, &update_parent, true),
2090 Err(BlockComponentProcessorError::MultipleUpdateParents)
2091 );
2092 }
2093
2094 #[test]
2095 fn test_header_after_update_parent_error() {
2096 let mut processor = BlockComponentProcessor::default();
2097 processor
2098 .on_update_parent(
2099 4,
2100 &VersionedUpdateParent::V1(UpdateParentV1 {
2101 new_parent_slot: 0,
2102 new_parent_block_id: Hash::default(),
2103 }),
2104 true,
2105 )
2106 .unwrap();
2107
2108 let header = VersionedBlockHeader::V1(BlockHeaderV1 {
2109 parent_slot: 0,
2110 parent_block_id: Hash::default(),
2111 });
2112
2113 assert!(matches!(
2114 processor.on_header(&header, 0),
2115 Err(BlockComponentProcessorError::SpuriousUpdateParent)
2116 ));
2117 }
2118
2119 #[test]
2120 fn test_workflow_with_update_parent() {
2121 let migration_status = MigrationStatus::post_migration_status();
2122 let mut processor = BlockComponentProcessor::default();
2123 let (parent, bank_forks) = create_test_bank();
2124 let bank = create_child_bank(&bank_forks, &parent, 4);
2125 let slot = bank.slot();
2126 let shred_version = rand::rng().random();
2127
2128 processor
2129 .on_update_parent(
2130 slot,
2131 &VersionedUpdateParent::V1(UpdateParentV1 {
2132 new_parent_slot: 0,
2133 new_parent_block_id: Hash::default(),
2134 }),
2135 true,
2136 )
2137 .unwrap();
2138
2139 processor
2140 .on_entry_batch(&migration_status, slot, &[], false)
2141 .unwrap();
2142
2143 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
2144 let footer = VersionedBlockFooter::V1(BlockFooterV1 {
2145 bank_hash: Hash::new_unique(),
2146 block_producer_time_nanos: (parent_time_nanos + 100_000_000) as u64,
2147 block_user_agent: vec![],
2148 block_final_cert: None,
2149 skip_reward_cert: None,
2150 notar_reward_cert: None,
2151 });
2152 processor
2153 .on_footer(
2154 &migration_status.my_pubkey(),
2155 bank,
2156 parent,
2157 shred_version,
2158 footer,
2159 None,
2160 )
2161 .unwrap();
2162
2163 let good_alpentick = alpentick(1);
2164 processor
2165 .on_entry_batch(&migration_status, slot, &good_alpentick, true)
2166 .unwrap();
2167
2168 processor.on_final(&migration_status, slot, 0).unwrap();
2169 }
2170}