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) {
330 return Ok(());
331 }
332
333 if Self::requires_genesis_certificate_marker(migration_status, parent_slot)
334 && !self.has_genesis_certificate_marker
335 {
336 return Err(BlockComponentProcessorError::MissingGenesisCertificateMarker);
337 }
338
339 self.stage.on_final()
340 }
341
342 fn requires_genesis_certificate_marker(
348 migration_status: &MigrationStatus,
349 parent_slot: Slot,
350 ) -> bool {
351 migration_status
352 .genesis_block()
353 .is_some_and(|genesis_block| {
354 genesis_block.slot != 0 && parent_slot == genesis_block.slot
355 })
356 }
357
358 pub fn on_entry_batch(
364 &mut self,
365 migration_status: &MigrationStatus,
366 slot: Slot,
367 entries: &[Entry],
368 is_final_component: bool,
369 ) -> Result<(), BlockComponentProcessorError> {
370 if !migration_status.should_allow_block_markers(slot) {
371 return Ok(());
372 }
373
374 let is_alpentick = is_final_component
378 && matches!(entries, [entry] if entry.is_tick() && entry.num_hashes == 1);
379
380 if is_alpentick {
381 self.stage.on_alpentick()
382 } else {
383 self.stage.on_entry_batch()
384 }
385 }
386
387 pub fn on_marker(
394 &mut self,
395 bank: Arc<Bank>,
396 parent_bank: Arc<Bank>,
397 shred_version: u16,
398 marker: VersionedBlockMarker,
399 allow_initial_update_parent: bool,
400 finalization_cert_sender: Option<&Sender<SmallVec<[Certificate; 2]>>>,
401 migration_status: &MigrationStatus,
402 ) -> Result<(), BlockComponentProcessorError> {
403 let slot = bank.slot();
404 let VersionedBlockMarker::V1(marker) = marker;
405
406 let markers_fully_enabled = migration_status.should_allow_block_markers(slot);
407 let in_migration = migration_status.is_in_migration();
408 let fast_leader_handover_active =
409 bank.feature_set.snapshot().alpenglow_fast_leader_handover;
410
411 match marker {
412 BlockMarkerV1::BlockHeader(header) if markers_fully_enabled || in_migration => {
416 self.on_header(header.inner(), bank.parent_slot())
417 }
418 BlockMarkerV1::GenesisCertificate(genesis_cert_block_marker)
419 if markers_fully_enabled || in_migration =>
420 {
421 self.on_genesis_cert_block_marker(
422 bank,
423 shred_version,
424 genesis_cert_block_marker.into_inner(),
425 migration_status,
426 )
427 }
428
429 BlockMarkerV1::BlockFooter(footer) if markers_fully_enabled => self.on_footer(
431 &migration_status.my_pubkey(),
432 bank,
433 parent_bank,
434 shred_version,
435 footer.into_inner(),
436 finalization_cert_sender,
437 ),
438
439 BlockMarkerV1::UpdateParent(update_parent) if markers_fully_enabled => {
440 if fast_leader_handover_active {
441 self.on_update_parent(slot, update_parent.inner(), allow_initial_update_parent)
442 } else {
443 Err(BlockComponentProcessorError::SpuriousUpdateParent)
444 }
445 }
446
447 _ => Err(BlockComponentProcessorError::BlockComponentPreMigration),
449 }
450 }
451
452 pub fn on_genesis_cert_block_marker(
454 &mut self,
455 bank: Arc<Bank>,
456 shred_version: u16,
457 genesis_block_marker: GenesisCertBlockMarker,
458 migration_status: &MigrationStatus,
459 ) -> Result<(), BlockComponentProcessorError> {
460 self.stage.on_genesis_certificate()?;
461 self.process_unvalidated_genesis_cert_block_marker(
462 bank,
463 genesis_block_marker,
464 migration_status,
465 Some(shred_version),
466 )?;
467 Ok(())
468 }
469
470 pub fn on_genesis_cert_block_marker_leader(
472 &mut self,
473 bank: Arc<Bank>,
474 genesis_block_marker: GenesisCertBlockMarker,
475 migration_status: &MigrationStatus,
476 ) -> Result<(), BlockComponentProcessorError> {
477 self.process_unvalidated_genesis_cert_block_marker(
478 bank,
479 genesis_block_marker,
480 migration_status,
481 None,
482 )?;
483 Ok(())
484 }
485
486 fn process_unvalidated_genesis_cert_block_marker(
488 &mut self,
489 bank: Arc<Bank>,
490 genesis_block_marker: GenesisCertBlockMarker,
491 migration_status: &MigrationStatus,
492 shred_version: Option<u16>,
493 ) -> Result<(), BlockComponentProcessorError> {
494 if bank.parent_slot() == 0 {
496 return Err(BlockComponentProcessorError::GenesisCertificateInAlpenglowCluster);
497 }
498
499 let parent_block_id = bank
500 .parent_block_id()
501 .expect("Block id is populated for all slots > 0");
502 if (bank.parent_slot(), parent_block_id)
503 != (genesis_block_marker.slot, genesis_block_marker.block_id)
504 {
505 return Err(BlockComponentProcessorError::GenesisCertificateOnNonChild);
506 }
507
508 if bank.get_alpenglow_genesis_certificate().is_some() {
509 return Err(BlockComponentProcessorError::GenesisCertificateAlreadyPopulated);
510 }
511
512 let genesis_cert = GenesisCert {
513 block: Block {
514 slot: genesis_block_marker.slot,
515 block_id: genesis_block_marker.block_id,
516 },
517 signature: CertSignature {
518 signature: genesis_block_marker.bls_signature,
519 bitmap: genesis_block_marker.bitmap,
520 },
521 };
522 if let Some(shred_version) = shred_version {
523 Self::verify_genesis_certificate(&bank, &genesis_cert, shred_version)?;
524 }
525
526 bank.set_alpenglow_genesis_certificate(&genesis_cert);
527 self.has_genesis_certificate_marker = true;
528
529 if migration_status.is_alpenglow_enabled() {
530 bank.set_hashes_per_tick(None);
532 return Ok(());
533 }
534
535 warn!(
541 "{}: Alpenglow genesis marker processed during replay of {}. Transitioning Alpenglow \
542 to ReadyToEnable",
543 migration_status.my_pubkey(),
544 bank.slot()
545 );
546 migration_status.set_genesis_block(genesis_cert.block);
547 migration_status.set_genesis_certificate(Arc::new(genesis_cert));
548 assert!(migration_status.is_ready_to_enable());
549
550 Err(BlockComponentProcessorError::AlpenglowMigrationTransition)
553 }
554
555 fn verify_genesis_certificate(
556 bank: &Bank,
557 cert: &GenesisCert,
558 shred_version: u16,
559 ) -> Result<(), BlockComponentProcessorError> {
560 let cert_slot = cert.block.slot;
561 let unverified_cert = UnverifiedCertificate {
562 cert_type: CertificateType::Genesis(cert.block),
563 signature: cert.signature.signature,
564 bitmap: cert.signature.bitmap.clone(),
565 shred_version,
566 };
567 bank.verify_certificate(unverified_cert).map_err(|_| {
568 warn!(
569 "Failed to verify genesis certificate for slot {cert_slot} in bank slot {}",
570 bank.slot()
571 );
572 BlockComponentProcessorError::GenesisCertificateFailedVerification
573 })?;
574
575 Ok(())
576 }
577
578 fn on_footer(
579 &mut self,
580 my_pubkey: &Pubkey,
581 bank: Arc<Bank>,
582 parent_bank: Arc<Bank>,
583 shred_version: u16,
584 footer: VersionedBlockFooter,
585 finalization_cert_sender: Option<&Sender<SmallVec<[Certificate; 2]>>>,
586 ) -> Result<(), BlockComponentProcessorError> {
587 self.stage.on_footer()?;
588
589 let VersionedBlockFooter::V1(footer) = footer;
590
591 Self::enforce_nanosecond_clock_bounds(&bank, &parent_bank, &footer)?;
592
593 let BlockFooterV1 {
594 bank_hash,
595 block_producer_time_nanos,
596 block_user_agent: _,
597 block_final_cert,
598 skip_reward_cert,
599 notar_reward_cert,
600 } = footer;
601
602 let reward_cert = ValidatedRewardCert::try_new(
603 &bank,
604 shred_version,
605 &skip_reward_cert,
606 ¬ar_reward_cert,
607 )?;
608 let block_producer_time_nanos =
609 Self::block_producer_time_nanos_as_i64(block_producer_time_nanos)?;
610 let final_cert = block_final_cert
611 .map(|final_cert| {
612 ValidatedBlockFinalizationCert::try_from_footer(final_cert, &bank, shred_version)
613 .map_err(BlockComponentProcessorError::InvalidFinalizationCertificate)
614 })
615 .transpose()?;
616
617 let (footer_input, pool_input) = match final_cert {
618 None => (None, None),
619 Some(cert) => {
620 let (signers, finalize_cert, notarize_cert) = cert.into_parts();
621 let final_slot = finalize_cert.cert_type.slot();
622 (
623 Some((signers, final_slot)),
624 Some((finalize_cert, notarize_cert)),
625 )
626 }
627 };
628
629 Self::update_bank_with_footer_fields(
630 &bank,
631 block_producer_time_nanos,
632 Some(bank_hash),
633 reward_cert,
634 footer_input
635 .as_ref()
636 .map(|(validators, slot)| (validators, *slot)),
637 )?;
638
639 if let Some((finalize_cert, notarize_cert)) = pool_input
641 && let Some(sender) = finalization_cert_sender
642 {
643 let channel_name = "finalization_cert_sender";
644 let certs = match notarize_cert {
645 None => smallvec![finalize_cert],
646 Some(c) => smallvec![finalize_cert, c],
647 };
648 match sender.try_send(certs) {
649 Ok(()) => (),
650 Err(TrySendError::Full(_)) => {
651 warn!("{my_pubkey}: channel \"{channel_name}\" is full, dropping msg")
652 }
653 Err(TrySendError::Disconnected(_)) => {
654 warn!("{my_pubkey}: channel \"{channel_name}\" disconnected")
655 }
656 }
657 }
658
659 Ok(())
660 }
661
662 fn on_header(
663 &mut self,
664 header: &VersionedBlockHeader,
665 bank_parent_slot: Slot,
666 ) -> Result<(), BlockComponentProcessorError> {
667 self.stage.on_header()?;
668
669 let VersionedBlockHeader::V1(header) = header;
670 if header.parent_slot != bank_parent_slot {
671 return Err(BlockComponentProcessorError::HeaderParentSlotMismatch {
672 header_parent_slot: header.parent_slot,
673 bank_parent_slot,
674 });
675 }
676 Ok(())
677 }
678
679 fn on_update_parent(
680 &mut self,
681 slot: Slot,
682 update_parent: &VersionedUpdateParent,
683 allow_initial_update_parent: bool,
684 ) -> Result<(), BlockComponentProcessorError> {
685 if leader_slot_index(slot) != 0 {
686 return Err(BlockComponentProcessorError::UpdateParentNotFirstInLeaderWindow(slot));
687 }
688
689 self.stage
690 .on_update_parent(update_parent, allow_initial_update_parent)
691 }
692
693 fn enforce_nanosecond_clock_bounds(
694 bank: &Bank,
695 parent_bank: &Bank,
696 footer: &BlockFooterV1,
697 ) -> Result<(), BlockComponentProcessorError> {
698 let parent_time_nanos = parent_bank
701 .get_nanosecond_clock()
702 .unwrap_or_else(|| bank.clock().unix_timestamp.saturating_mul(1_000_000_000));
703
704 let parent_slot = parent_bank.slot();
705 let current_time_nanos =
706 Self::block_producer_time_nanos_as_i64(footer.block_producer_time_nanos)?;
707 let current_slot = bank.slot();
708 let elapsed_slot_duration_nanos =
709 bank.slot_range_duration_nanos(parent_slot.saturating_add(1), current_slot);
710
711 let (lower_bound_nanos, upper_bound_nanos) =
712 Self::nanosecond_time_bounds(parent_time_nanos, elapsed_slot_duration_nanos);
713
714 let is_valid =
715 lower_bound_nanos <= current_time_nanos && current_time_nanos <= upper_bound_nanos;
716
717 match is_valid {
718 true => Ok(()),
719 false => Err(BlockComponentProcessorError::NanosecondClockOutOfBounds),
720 }
721 }
722
723 fn block_producer_time_nanos_as_i64(
730 block_producer_time_nanos: u64,
731 ) -> Result<i64, BlockComponentProcessorError> {
732 i64::try_from(block_producer_time_nanos)
733 .map_err(|_| BlockComponentProcessorError::NanosecondClockOutOfBounds)
734 }
735
736 pub fn nanosecond_time_bounds(
749 parent_time_nanos: i64,
750 elapsed_slot_duration_nanos: u128,
751 ) -> (i64, i64) {
752 let min_working_bank_time = parent_time_nanos.saturating_add(1);
753 let max_working_bank_time_offset = elapsed_slot_duration_nanos
754 .saturating_mul(2)
755 .min(i64::MAX as u128) as i64;
756 let max_working_bank_time = parent_time_nanos.saturating_add(max_working_bank_time_offset);
757
758 (min_working_bank_time, max_working_bank_time)
759 }
760
761 pub fn update_bank_with_footer_fields(
762 bank: &Bank,
763 block_producer_time_nanos: i64,
764 bank_hash: Option<Hash>,
765 reward_cert: Option<ValidatedRewardCert>,
766 final_cert_input: Option<(&HashSet<Pubkey>, Slot)>,
767 ) -> Result<(), BankFooterError> {
768 bank.update_clock_from_footer(block_producer_time_nanos);
769 calc_vote_rewards_update_vote_states(
770 bank,
771 reward_cert,
772 final_cert_input,
773 block_producer_time_nanos,
774 )?;
775
776 if let Some(hash) = bank_hash {
777 bank.set_expected_bank_hash(hash);
779 }
780 Ok(())
781 }
782}
783
784#[cfg(test)]
785mod tests {
786 use {
787 super::*,
788 crate::{
789 bank::{Bank, SlotLeader},
790 bank_forks::BankForks,
791 genesis_utils::{activate_all_features_alpenglow, create_genesis_config},
792 },
793 rand::Rng,
794 solana_bls_signatures::{BLS_SIGNATURE_AFFINE_SIZE, Signature as BLSSignature},
795 solana_clock::DEFAULT_MS_PER_SLOT,
796 solana_entry::{
797 block_component::{
798 BlockFooterV1, BlockHeaderV1, UpdateParentV1, VersionedUpdateParent,
799 },
800 entry::Entry,
801 },
802 solana_hash::Hash,
803 std::{
804 assert_matches,
805 sync::{Arc, RwLock},
806 },
807 };
808
809 const DEFAULT_NS_PER_SLOT: u64 = DEFAULT_MS_PER_SLOT * 1_000_000;
810
811 fn create_test_bank() -> (Arc<Bank>, Arc<RwLock<BankForks>>) {
812 let genesis_config_info = create_genesis_config(10_000);
813 Bank::new_with_bank_forks_for_tests(&genesis_config_info.genesis_config)
814 }
815
816 fn create_test_bank_alpenglow() -> (Arc<Bank>, Arc<RwLock<BankForks>>) {
817 let mut genesis_config_info = create_genesis_config(10_000);
818 activate_all_features_alpenglow(&mut genesis_config_info.genesis_config);
819 Bank::new_with_bank_forks_for_tests(&genesis_config_info.genesis_config)
820 }
821
822 fn create_child_bank(
823 bank_forks: &RwLock<BankForks>,
824 parent: &Arc<Bank>,
825 slot: u64,
826 ) -> Arc<Bank> {
827 Bank::new_from_parent_with_bank_forks(
828 bank_forks,
829 parent.clone(),
830 SlotLeader::new_unique(),
831 slot,
832 )
833 }
834
835 fn test_genesis_cert_marker() -> GenesisCertBlockMarker {
836 GenesisCertBlockMarker {
837 slot: 0,
838 block_id: Hash::default(),
839 bls_signature: BLSSignature([0; BLS_SIGNATURE_AFFINE_SIZE]),
840 bitmap: vec![],
841 }
842 }
843
844 fn post_migration_status_with_genesis_slot(genesis_slot: Slot) -> MigrationStatus {
845 let migration_status = MigrationStatus::default();
846 let migration_slot = migration_status.record_feature_activation(0);
847 assert!(genesis_slot < migration_slot);
848
849 let genesis_block = Block {
850 slot: genesis_slot,
851 block_id: Hash::default(),
852 };
853 migration_status.set_genesis_block(genesis_block);
854 let cert = Arc::new(GenesisCert {
855 block: genesis_block,
856 signature: CertSignature {
857 signature: BLSSignature([0; BLS_SIGNATURE_AFFINE_SIZE]),
858 bitmap: vec![],
859 },
860 });
861 migration_status.set_genesis_certificate(cert);
862 migration_status.enable_alpenglow_during_startup();
863
864 migration_status
865 }
866
867 fn processor_after_header() -> BlockComponentProcessor {
868 BlockComponentProcessor {
869 stage: BlockComponentStage::AcceptingGenesisOrEntries,
870 ..BlockComponentProcessor::default()
871 }
872 }
873
874 fn processor_after_footer() -> BlockComponentProcessor {
875 BlockComponentProcessor {
876 stage: BlockComponentStage::AcceptingAlpentick,
877 ..BlockComponentProcessor::default()
878 }
879 }
880
881 fn processor_done() -> BlockComponentProcessor {
882 BlockComponentProcessor {
883 stage: BlockComponentStage::Done,
884 ..BlockComponentProcessor::default()
885 }
886 }
887
888 fn alpentick(num_hashes: u64) -> [Entry; 1] {
889 [Entry::new(&Hash::default(), num_hashes, vec![])]
890 }
891
892 #[test]
893 fn test_missing_header_error_on_entry_batch() {
894 let migration_status = MigrationStatus::post_migration_status();
895 let mut processor = BlockComponentProcessor::default();
896
897 let result = processor.on_entry_batch(&migration_status, 1, &[], false);
899 assert!(matches!(
900 result,
901 Err(BlockComponentProcessorError::MissingParentMarker)
902 ));
903 }
904
905 #[test]
906 fn test_missing_header_error_on_genesis_certificate() {
907 let migration_status = MigrationStatus::post_migration_status();
908 let mut processor = BlockComponentProcessor::default();
909 let marker =
910 VersionedBlockMarker::from_genesis_cert_block_marker(test_genesis_cert_marker());
911
912 let (parent, bank_forks) = create_test_bank();
913 let bank = create_child_bank(&bank_forks, &parent, 1);
914 let shred_version = rand::rng().random();
915
916 let result = processor.on_marker(
917 bank,
918 parent,
919 shred_version,
920 marker,
921 false,
922 None,
923 &migration_status,
924 );
925 assert!(matches!(
926 result,
927 Err(BlockComponentProcessorError::MissingParentMarker)
928 ));
929 }
930
931 #[test]
932 fn test_genesis_certificate_after_entry_batch_errors() {
933 let migration_status = MigrationStatus::post_migration_status();
934 let mut processor = BlockComponentProcessor::default();
935 let (parent, bank_forks) = create_test_bank();
936 let bank = create_child_bank(&bank_forks, &parent, 1);
937 let shred_version = rand::rng().random();
938
939 let header = VersionedBlockMarker::from_block_header(BlockHeaderV1 {
940 parent_slot: 0,
941 parent_block_id: Hash::default(),
942 });
943 processor
944 .on_marker(
945 bank.clone(),
946 parent.clone(),
947 shred_version,
948 header,
949 false,
950 None,
951 &migration_status,
952 )
953 .unwrap();
954 processor
955 .on_entry_batch(&migration_status, bank.slot(), &[], false)
956 .unwrap();
957
958 let marker =
959 VersionedBlockMarker::from_genesis_cert_block_marker(test_genesis_cert_marker());
960 let result = processor.on_marker(
961 bank,
962 parent,
963 shred_version,
964 marker,
965 false,
966 None,
967 &migration_status,
968 );
969 assert!(matches!(
970 result,
971 Err(BlockComponentProcessorError::GenesisCertificateOutOfOrder)
972 ));
973 }
974
975 #[test]
976 fn test_genesis_certificate_immediately_after_header_passes_order_check() {
977 let migration_status = MigrationStatus::post_migration_status();
978 let mut processor = BlockComponentProcessor::default();
979 let (parent, bank_forks) = create_test_bank();
980 let bank = create_child_bank(&bank_forks, &parent, 1);
981 let shred_version = rand::rng().random();
982
983 let header = VersionedBlockMarker::from_block_header(BlockHeaderV1 {
984 parent_slot: 0,
985 parent_block_id: Hash::default(),
986 });
987 processor
988 .on_marker(
989 bank.clone(),
990 parent.clone(),
991 shred_version,
992 header,
993 false,
994 None,
995 &migration_status,
996 )
997 .unwrap();
998
999 let marker =
1000 VersionedBlockMarker::from_genesis_cert_block_marker(test_genesis_cert_marker());
1001 let result = processor.on_marker(
1002 bank,
1003 parent,
1004 shred_version,
1005 marker,
1006 false,
1007 None,
1008 &migration_status,
1009 );
1010 assert!(matches!(
1011 result,
1012 Err(BlockComponentProcessorError::GenesisCertificateInAlpenglowCluster)
1013 ));
1014 }
1015
1016 #[test]
1017 fn test_missing_footer_error_on_slot_full() {
1018 let migration_status = MigrationStatus::post_migration_status();
1019 let processor = processor_after_header();
1020
1021 let result = processor.on_final(&migration_status, 1, 0);
1023 assert!(matches!(
1024 result,
1025 Err(BlockComponentProcessorError::MissingBlockFooter)
1026 ));
1027 }
1028
1029 #[test]
1030 fn test_first_alpenglow_block_requires_genesis_certificate_marker() {
1031 let migration_status = post_migration_status_with_genesis_slot(1);
1032 let processor = processor_after_footer();
1033
1034 let result = processor.on_final(&migration_status, 2, 1);
1035 assert!(matches!(
1036 result,
1037 Err(BlockComponentProcessorError::MissingGenesisCertificateMarker)
1038 ));
1039 }
1040
1041 #[test]
1042 fn test_first_alpenglow_block_with_genesis_certificate_marker_succeeds() {
1043 let migration_status = post_migration_status_with_genesis_slot(1);
1044 let (genesis_bank, bank_forks) = create_test_bank();
1045 let parent = create_child_bank(&bank_forks, &genesis_bank, 1);
1046 let parent_block_id = Hash::new_unique();
1047 parent.set_block_id(Some(parent_block_id));
1048 let bank = create_child_bank(&bank_forks, &parent, 2);
1049 let genesis_marker = GenesisCertBlockMarker {
1050 slot: parent.slot(),
1051 block_id: parent_block_id,
1052 bls_signature: BLSSignature([0; BLS_SIGNATURE_AFFINE_SIZE]),
1053 bitmap: vec![],
1054 };
1055 let mut processor = processor_after_header();
1056 bank.set_hashes_per_tick(Some(42));
1057 assert!(bank.hashes_per_tick().is_some());
1058
1059 processor
1060 .on_genesis_cert_block_marker_leader(bank.clone(), genesis_marker, &migration_status)
1061 .unwrap();
1062 assert!(bank.hashes_per_tick().is_none());
1063 processor.stage = BlockComponentStage::Done;
1064 assert!(processor.on_final(&migration_status, 2, 1).is_ok());
1065 }
1066
1067 #[test]
1068 fn test_genesis_certificate_marker_aborts_tower_bank_during_migration() {
1069 let migration_status = MigrationStatus::default();
1070 migration_status.record_feature_activation(0);
1071 let (genesis_bank, bank_forks) = create_test_bank();
1072 let parent = create_child_bank(&bank_forks, &genesis_bank, 1);
1073 let parent_block_id = Hash::new_unique();
1074 parent.set_block_id(Some(parent_block_id));
1075 let bank = create_child_bank(&bank_forks, &parent, 2);
1076 let genesis_marker = GenesisCertBlockMarker {
1077 slot: parent.slot(),
1078 block_id: parent_block_id,
1079 bls_signature: BLSSignature([0; BLS_SIGNATURE_AFFINE_SIZE]),
1080 bitmap: vec![],
1081 };
1082 let mut processor = processor_after_header();
1083 bank.set_hashes_per_tick(Some(42));
1084 let tower_hashes_per_tick = bank.hashes_per_tick();
1085 assert!(tower_hashes_per_tick.is_some());
1086
1087 assert_matches!(
1088 processor.on_genesis_cert_block_marker_leader(
1089 bank.clone(),
1090 genesis_marker,
1091 &migration_status,
1092 ),
1093 Err(BlockComponentProcessorError::AlpenglowMigrationTransition)
1094 );
1095
1096 assert!(migration_status.is_ready_to_enable());
1097 assert_eq!(bank.hashes_per_tick(), tower_hashes_per_tick);
1098 assert!(bank.get_alpenglow_genesis_certificate().is_some());
1099 }
1100
1101 #[test]
1102 fn test_first_alpenglow_block_genesis_slot_zero_skips_genesis_certificate_marker_check() {
1103 let migration_status = MigrationStatus::post_migration_status();
1104 let processor = processor_done();
1105
1106 assert!(processor.on_final(&migration_status, 1, 0).is_ok());
1107 }
1108
1109 #[test]
1110 fn test_multiple_headers_error() {
1111 let mut processor = BlockComponentProcessor::default();
1112 let header = VersionedBlockHeader::V1(BlockHeaderV1 {
1113 parent_slot: 0,
1114 parent_block_id: Hash::default(),
1115 });
1116
1117 assert!(processor.on_header(&header, 0).is_ok());
1119
1120 let result = processor.on_header(&header, 0);
1122 assert!(matches!(
1123 result,
1124 Err(BlockComponentProcessorError::MultipleBlockHeaders)
1125 ));
1126 }
1127
1128 #[test]
1129 fn test_multiple_footers_error() {
1130 let my_pubkey = Pubkey::new_unique();
1131 let mut processor = processor_after_header();
1132
1133 let (parent, bank_forks) = create_test_bank();
1134 let bank = create_child_bank(&bank_forks, &parent, 1);
1135 let shred_version = rand::rng().random();
1136
1137 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1139 let footer_time_nanos = parent_time_nanos + 400_000_000; let footer = VersionedBlockFooter::V1(BlockFooterV1 {
1142 bank_hash: Hash::new_unique(),
1143 block_producer_time_nanos: footer_time_nanos as u64,
1144 block_user_agent: vec![],
1145 block_final_cert: None,
1146 skip_reward_cert: None,
1147 notar_reward_cert: None,
1148 });
1149
1150 processor
1152 .on_footer(
1153 &my_pubkey,
1154 bank.clone(),
1155 parent.clone(),
1156 shred_version,
1157 footer.clone(),
1158 None,
1159 )
1160 .unwrap();
1161
1162 let err = processor
1164 .on_footer(&my_pubkey, bank, parent, shred_version, footer, None)
1165 .unwrap_err();
1166 assert!(matches!(
1167 err,
1168 BlockComponentProcessorError::MultipleBlockFooters
1169 ));
1170 }
1171
1172 #[test]
1173 fn test_on_footer_sets_timestamp() {
1174 let my_pubkey = Pubkey::new_unique();
1175 let mut processor = processor_after_header();
1176
1177 let (parent, bank_forks) = create_test_bank();
1178 let bank = create_child_bank(&bank_forks, &parent, 1);
1179 let shred_version = rand::rng().random();
1180
1181 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1183 let footer_time_nanos = parent_time_nanos + 200_000_000; let expected_time_secs = footer_time_nanos / 1_000_000_000;
1185
1186 let footer = VersionedBlockFooter::V1(BlockFooterV1 {
1187 bank_hash: Hash::new_unique(),
1188 block_producer_time_nanos: footer_time_nanos as u64,
1189 block_user_agent: vec![],
1190 block_final_cert: None,
1191 skip_reward_cert: None,
1192 notar_reward_cert: None,
1193 });
1194
1195 processor
1196 .on_footer(
1197 &my_pubkey,
1198 bank.clone(),
1199 parent,
1200 shred_version,
1201 footer,
1202 None,
1203 )
1204 .unwrap();
1205
1206 assert_eq!(processor.stage, BlockComponentStage::AcceptingAlpentick);
1207
1208 assert_eq!(bank.clock().unix_timestamp, expected_time_secs);
1210 }
1211
1212 #[test]
1213 fn test_on_header_sets_flag() {
1214 let mut processor = BlockComponentProcessor::default();
1215 let header = VersionedBlockHeader::V1(BlockHeaderV1 {
1216 parent_slot: 0,
1217 parent_block_id: Hash::default(),
1218 });
1219
1220 processor.on_header(&header, 0).unwrap();
1221 assert_eq!(
1222 processor.stage,
1223 BlockComponentStage::AcceptingGenesisOrEntries
1224 );
1225 }
1226
1227 #[test]
1228 fn test_on_header_parent_slot_mismatch_error() {
1229 let mut processor = BlockComponentProcessor::default();
1230 let header = VersionedBlockHeader::V1(BlockHeaderV1 {
1231 parent_slot: 2,
1232 parent_block_id: Hash::default(),
1233 });
1234
1235 assert!(matches!(
1236 processor.on_header(&header, 0),
1237 Err(BlockComponentProcessorError::HeaderParentSlotMismatch {
1238 header_parent_slot: 2,
1239 bank_parent_slot: 0,
1240 })
1241 ));
1242 }
1243
1244 #[test]
1245 fn test_on_marker_processes_header() {
1246 let migration_status = MigrationStatus::post_migration_status();
1247 let mut processor = BlockComponentProcessor::default();
1248 let marker = VersionedBlockMarker::from_block_header(BlockHeaderV1 {
1249 parent_slot: 0,
1250 parent_block_id: Hash::default(),
1251 });
1252
1253 let (parent, bank_forks) = create_test_bank();
1254 let bank = create_child_bank(&bank_forks, &parent, 1);
1255 let shred_version = rand::rng().random();
1256
1257 processor
1258 .on_marker(
1259 bank,
1260 parent,
1261 shred_version,
1262 marker,
1263 false,
1264 None,
1265 &migration_status,
1266 )
1267 .unwrap();
1268 assert_eq!(
1269 processor.stage,
1270 BlockComponentStage::AcceptingGenesisOrEntries
1271 );
1272 }
1273
1274 #[test]
1275 fn test_on_marker_rejects_header_parent_slot_mismatch() {
1276 let migration_status = MigrationStatus::post_migration_status();
1277 let mut processor = BlockComponentProcessor::default();
1278 let marker = VersionedBlockMarker::from_block_header(BlockHeaderV1 {
1279 parent_slot: 7, parent_block_id: Hash::default(),
1281 });
1282
1283 let (parent, bank_forks) = create_test_bank();
1284 let bank = create_child_bank(&bank_forks, &parent, 1);
1285 let shred_version = rand::rng().random();
1286
1287 assert!(matches!(
1288 processor.on_marker(
1289 bank,
1290 parent,
1291 shred_version,
1292 marker,
1293 false,
1294 None,
1295 &migration_status
1296 ),
1297 Err(BlockComponentProcessorError::HeaderParentSlotMismatch {
1298 header_parent_slot: 7,
1299 bank_parent_slot: 0,
1300 })
1301 ));
1302 }
1303
1304 #[test]
1305 fn test_on_marker_processes_footer() {
1306 let migration_status = MigrationStatus::post_migration_status();
1307 let mut processor = processor_after_header();
1308
1309 let (parent, bank_forks) = create_test_bank();
1310 let bank = create_child_bank(&bank_forks, &parent, 1);
1311 let shred_version = rand::rng().random();
1312
1313 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1315 let footer_time_nanos = parent_time_nanos + 300_000_000; let expected_time_secs = footer_time_nanos / 1_000_000_000;
1317
1318 let marker = VersionedBlockMarker::from_block_footer(BlockFooterV1 {
1319 bank_hash: Hash::new_unique(),
1320 block_producer_time_nanos: footer_time_nanos as u64,
1321 block_user_agent: vec![],
1322 block_final_cert: None,
1323 skip_reward_cert: None,
1324 notar_reward_cert: None,
1325 });
1326
1327 processor
1328 .on_marker(
1329 bank.clone(),
1330 parent,
1331 shred_version,
1332 marker,
1333 false,
1334 None,
1335 &migration_status,
1336 )
1337 .unwrap();
1338 assert_eq!(processor.stage, BlockComponentStage::AcceptingAlpentick);
1339
1340 assert_eq!(bank.clock().unix_timestamp, expected_time_secs);
1342 }
1343
1344 #[test]
1345 fn test_complete_workflow_success() {
1346 let migration_status = MigrationStatus::post_migration_status();
1347 let mut processor = BlockComponentProcessor::default();
1348 let (parent, bank_forks) = create_test_bank();
1349 let bank = create_child_bank(&bank_forks, &parent, 1);
1350 let shred_version = rand::rng().random();
1351
1352 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1354 let footer_time_nanos = parent_time_nanos + 100_000_000; let expected_time_secs = footer_time_nanos / 1_000_000_000;
1356
1357 let header = VersionedBlockHeader::V1(BlockHeaderV1 {
1359 parent_slot: 0,
1360 parent_block_id: Hash::default(),
1361 });
1362 processor.on_header(&header, bank.parent_slot()).unwrap();
1363
1364 processor
1366 .on_entry_batch(&migration_status, 1, &[], false)
1367 .unwrap();
1368
1369 let footer = VersionedBlockFooter::V1(BlockFooterV1 {
1371 bank_hash: Hash::new_unique(),
1372 block_producer_time_nanos: footer_time_nanos as u64,
1373 block_user_agent: vec![],
1374 block_final_cert: None,
1375 skip_reward_cert: None,
1376 notar_reward_cert: None,
1377 });
1378 processor
1379 .on_footer(
1380 &migration_status.my_pubkey(),
1381 bank.clone(),
1382 parent.clone(),
1383 shred_version,
1384 footer,
1385 None,
1386 )
1387 .unwrap();
1388
1389 assert_eq!(bank.clock().unix_timestamp, expected_time_secs);
1391
1392 let result = processor.on_entry_batch(&migration_status, 1, &[], false);
1394 assert_matches!(
1395 result,
1396 Err(BlockComponentProcessorError::EntryBatchAfterBlockFooter)
1397 );
1398 }
1399
1400 #[test]
1401 fn test_alpentick_position_validation() {
1402 let migration_status = MigrationStatus::post_migration_status();
1403 let mut processor = processor_after_footer();
1404 let good_alpentick = alpentick(1);
1405
1406 processor
1407 .on_entry_batch(&migration_status, 1, &good_alpentick, true)
1408 .unwrap();
1409 assert_matches!(
1410 processor.on_entry_batch(&migration_status, 1, &good_alpentick, true),
1411 Err(BlockComponentProcessorError::InvalidAlpentickPosition)
1412 );
1413
1414 let mut processor = BlockComponentProcessor::default();
1415 assert_matches!(
1416 processor.on_entry_batch(&migration_status, 1, &good_alpentick, true),
1417 Err(BlockComponentProcessorError::MissingParentMarker)
1418 );
1419
1420 let mut processor = processor_after_footer();
1421 let bad_alpentick = alpentick(2);
1422 assert_matches!(
1423 processor.on_entry_batch(&migration_status, 1, &bad_alpentick, true),
1424 Err(BlockComponentProcessorError::EntryBatchAfterBlockFooter)
1425 );
1426
1427 let migration_status = MigrationStatus::default();
1428 let mut processor = BlockComponentProcessor::default();
1429 processor
1430 .on_entry_batch(&migration_status, 1, &good_alpentick, true)
1431 .unwrap();
1432 }
1433
1434 #[test]
1435 fn test_block_marker_detected_pre_migration() {
1436 let migration_status = MigrationStatus::default();
1437 let mut processor = BlockComponentProcessor::default();
1438 let (parent, bank_forks) = create_test_bank();
1439 let bank = create_child_bank(&bank_forks, &parent, 1);
1440 let shred_version = rand::rng().random();
1441
1442 let marker = VersionedBlockMarker::from_block_header(BlockHeaderV1 {
1444 parent_slot: 0,
1445 parent_block_id: Hash::default(),
1446 });
1447
1448 let err = processor
1449 .on_marker(
1450 bank,
1451 parent,
1452 shred_version,
1453 marker,
1454 false,
1455 None,
1456 &migration_status,
1457 )
1458 .unwrap_err();
1459 assert!(matches!(
1460 err,
1461 BlockComponentProcessorError::BlockComponentPreMigration
1462 ));
1463 }
1464
1465 #[test]
1466 fn test_footer_and_update_parent_rejected_pre_migration() {
1467 let migration_status = MigrationStatus::default();
1468 let (parent, bank_forks) = create_test_bank();
1469 let bank = create_child_bank(&bank_forks, &parent, 1);
1470 let shred_version = rand::rng().random();
1471
1472 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1473 let footer_marker = VersionedBlockMarker::from_block_footer(BlockFooterV1 {
1474 bank_hash: Hash::new_unique(),
1475 block_producer_time_nanos: (parent_time_nanos + 500_000_000) as u64,
1476 block_user_agent: vec![],
1477 block_final_cert: None,
1478 skip_reward_cert: None,
1479 notar_reward_cert: None,
1480 });
1481
1482 let mut processor = BlockComponentProcessor::default();
1483 assert!(matches!(
1484 processor
1485 .on_marker(
1486 bank.clone(),
1487 parent.clone(),
1488 shred_version,
1489 footer_marker,
1490 false,
1491 None,
1492 &migration_status
1493 )
1494 .unwrap_err(),
1495 BlockComponentProcessorError::BlockComponentPreMigration
1496 ));
1497
1498 let update_parent_marker = VersionedBlockMarker::from_update_parent(UpdateParentV1 {
1499 new_parent_slot: 0,
1500 new_parent_block_id: Hash::default(),
1501 });
1502
1503 let mut processor = BlockComponentProcessor::default();
1504 assert!(matches!(
1505 processor
1506 .on_marker(
1507 bank,
1508 parent,
1509 shred_version,
1510 update_parent_marker,
1511 false,
1512 None,
1513 &migration_status
1514 )
1515 .unwrap_err(),
1516 BlockComponentProcessorError::BlockComponentPreMigration
1517 ));
1518 }
1519
1520 #[test]
1521 fn test_entry_batch_pre_migration_succeeds() {
1522 let migration_status = MigrationStatus::default();
1523 let mut processor = BlockComponentProcessor::default();
1524
1525 let result = processor.on_entry_batch(&migration_status, 1, &[], false);
1527 assert!(result.is_ok());
1528
1529 let result = processor.on_entry_batch(&migration_status, 1, &[], false);
1531 assert!(result.is_ok());
1532 }
1533
1534 #[test]
1535 fn test_complete_workflow_post_migration() {
1536 let migration_status = MigrationStatus::post_migration_status();
1537 let mut processor = BlockComponentProcessor::default();
1538 let (parent, bank_forks) = create_test_bank();
1539 let bank = create_child_bank(&bank_forks, &parent, 1);
1540 let shred_version = rand::rng().random();
1541
1542 let header_marker = VersionedBlockMarker::from_block_header(BlockHeaderV1 {
1544 parent_slot: 0,
1545 parent_block_id: Hash::default(),
1546 });
1547 processor
1548 .on_marker(
1549 bank.clone(),
1550 parent.clone(),
1551 shred_version,
1552 header_marker,
1553 false,
1554 None,
1555 &migration_status,
1556 )
1557 .unwrap();
1558
1559 processor
1561 .on_entry_batch(&migration_status, 1, &[], false)
1562 .unwrap();
1563
1564 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1566 let footer_time_nanos = parent_time_nanos + 500_000_000; let expected_time_secs = footer_time_nanos / 1_000_000_000;
1568
1569 let footer_marker = VersionedBlockMarker::from_block_footer(BlockFooterV1 {
1571 bank_hash: Hash::new_unique(),
1572 block_producer_time_nanos: footer_time_nanos as u64,
1573 block_user_agent: vec![],
1574 block_final_cert: None,
1575 skip_reward_cert: None,
1576 notar_reward_cert: None,
1577 });
1578 processor
1579 .on_marker(
1580 bank.clone(),
1581 parent,
1582 shred_version,
1583 footer_marker,
1584 false,
1585 None,
1586 &migration_status,
1587 )
1588 .unwrap();
1589
1590 assert_eq!(bank.clock().unix_timestamp, expected_time_secs);
1592
1593 let result = processor.on_entry_batch(&migration_status, 1, &[], false);
1595 assert_matches!(
1596 result,
1597 Err(BlockComponentProcessorError::EntryBatchAfterBlockFooter)
1598 );
1599 }
1600
1601 #[test]
1602 fn test_footer_without_header_errors() {
1603 let my_pubkey = Pubkey::new_unique();
1604 let mut processor = BlockComponentProcessor::default();
1605 let (parent, bank_forks) = create_test_bank();
1606 let bank = create_child_bank(&bank_forks, &parent, 1);
1607 let shred_version = rand::rng().random();
1608
1609 let footer = VersionedBlockFooter::V1(BlockFooterV1 {
1610 bank_hash: Hash::new_unique(),
1611 block_producer_time_nanos: 1_000_000_000,
1612 block_user_agent: vec![],
1613 block_final_cert: None,
1614 skip_reward_cert: None,
1615 notar_reward_cert: None,
1616 });
1617
1618 let err = processor
1620 .on_footer(&my_pubkey, bank, parent, shred_version, footer, None)
1621 .unwrap_err();
1622 assert!(matches!(
1623 err,
1624 BlockComponentProcessorError::MissingParentMarker
1625 ));
1626 }
1627
1628 #[test]
1629 fn test_marker_with_footer_at_slot_full() {
1630 let migration_status = MigrationStatus::post_migration_status();
1631 let mut processor = processor_after_header();
1632 let (parent, bank_forks) = create_test_bank();
1633 let bank = create_child_bank(&bank_forks, &parent, 1);
1634 let shred_version = rand::rng().random();
1635
1636 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1638 let footer_time_nanos = parent_time_nanos + 600_000_000; let expected_time_secs = footer_time_nanos / 1_000_000_000;
1640
1641 let footer_marker = VersionedBlockMarker::from_block_footer(BlockFooterV1 {
1643 bank_hash: Hash::new_unique(),
1644 block_producer_time_nanos: footer_time_nanos as u64,
1645 block_user_agent: vec![],
1646 block_final_cert: None,
1647 skip_reward_cert: None,
1648 notar_reward_cert: None,
1649 });
1650
1651 processor
1653 .on_marker(
1654 bank.clone(),
1655 parent,
1656 shred_version,
1657 footer_marker,
1658 false,
1659 None,
1660 &migration_status,
1661 )
1662 .unwrap();
1663 assert_eq!(processor.stage, BlockComponentStage::AcceptingAlpentick);
1664
1665 assert_eq!(bank.clock().unix_timestamp, expected_time_secs);
1667 }
1668
1669 #[test]
1670 fn test_entry_batch_with_header_not_full_succeeds() {
1671 let migration_status = MigrationStatus::post_migration_status();
1672 let mut processor = processor_after_header();
1673
1674 let result = processor.on_entry_batch(&migration_status, 1, &[], false);
1676 assert!(result.is_ok());
1677 }
1678
1679 #[test]
1680 fn test_footer_sets_epoch_start_timestamp_on_epoch_change() {
1681 let my_pubkey = Pubkey::new_unique();
1682 let mut processor = processor_after_header();
1683 let shred_version = rand::rng().random();
1684
1685 let genesis_config_info = create_genesis_config(10_000);
1687 let (genesis_bank, bank_forks) =
1688 Bank::new_with_bank_forks_for_tests(&genesis_config_info.genesis_config);
1689
1690 let epoch_schedule = genesis_bank.epoch_schedule();
1692 let first_slot_in_epoch_1 = epoch_schedule.get_first_slot_in_epoch(1);
1693
1694 let mut parent = genesis_bank.clone();
1696 for slot in 1..first_slot_in_epoch_1 {
1697 parent = create_child_bank(&bank_forks, &parent, slot);
1698 }
1699
1700 let bank = create_child_bank(&bank_forks, &parent, first_slot_in_epoch_1);
1702
1703 assert_eq!(bank.epoch(), 1);
1705
1706 let parent_slot = parent.slot();
1708 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1709 let current_slot = bank.slot();
1710 let elapsed_slot_duration_nanos =
1711 bank.slot_range_duration_nanos(parent_slot.saturating_add(1), current_slot);
1712
1713 let (lower_bound, upper_bound) = BlockComponentProcessor::nanosecond_time_bounds(
1715 parent_time_nanos,
1716 elapsed_slot_duration_nanos,
1717 );
1718 let footer_time_nanos = (lower_bound + upper_bound) / 2;
1719 let expected_time_secs = footer_time_nanos / 1_000_000_000;
1720
1721 let footer = VersionedBlockFooter::V1(BlockFooterV1 {
1722 bank_hash: Hash::new_unique(),
1723 block_producer_time_nanos: footer_time_nanos as u64,
1724 block_user_agent: vec![],
1725 block_final_cert: None,
1726 skip_reward_cert: None,
1727 notar_reward_cert: None,
1728 });
1729
1730 processor
1731 .on_footer(
1732 &my_pubkey,
1733 bank.clone(),
1734 parent,
1735 shred_version,
1736 footer,
1737 None,
1738 )
1739 .unwrap();
1740
1741 assert_eq!(bank.clock().unix_timestamp, expected_time_secs);
1743
1744 assert_eq!(bank.clock().epoch_start_timestamp, expected_time_secs);
1746 }
1747
1748 fn test_clock_bounds_helper(
1750 slot_gap: u64,
1751 timestamp_fn: impl FnOnce(i64, i64, i64) -> i64,
1752 should_pass: bool,
1753 ) {
1754 let my_pubkey = Pubkey::new_unique();
1755 let mut processor = processor_after_header();
1756 let shred_version = rand::rng().random();
1757
1758 let (parent, bank_forks) = create_test_bank_alpenglow();
1759 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1760
1761 parent.update_clock_from_footer(parent_time_nanos);
1763
1764 let bank: Arc<Bank> = create_child_bank(&bank_forks, &parent, slot_gap);
1765 let elapsed_slot_duration_nanos = bank.slot_range_duration_nanos(1, slot_gap);
1766
1767 let (lower_bound, upper_bound) = BlockComponentProcessor::nanosecond_time_bounds(
1768 parent_time_nanos,
1769 elapsed_slot_duration_nanos,
1770 );
1771
1772 let footer_time_nanos = timestamp_fn(parent_time_nanos, lower_bound, upper_bound);
1773
1774 let footer = VersionedBlockFooter::V1(BlockFooterV1 {
1775 bank_hash: Hash::new_unique(),
1776 block_producer_time_nanos: footer_time_nanos as u64,
1777 block_user_agent: vec![],
1778 block_final_cert: None,
1779 skip_reward_cert: None,
1780 notar_reward_cert: None,
1781 });
1782
1783 let result = processor.on_footer(&my_pubkey, bank, parent, shred_version, footer, None);
1784 if should_pass {
1785 result.unwrap();
1786 } else {
1787 assert!(matches!(
1788 result.unwrap_err(),
1789 BlockComponentProcessorError::NanosecondClockOutOfBounds
1790 ));
1791 }
1792 }
1793
1794 #[test]
1795 fn test_clock_bounds_at_minimum() {
1796 test_clock_bounds_helper(1, |_, lower, _| lower, true);
1797 }
1798
1799 #[test]
1800 fn test_clock_bounds_at_maximum() {
1801 test_clock_bounds_helper(1, |_, _, upper| upper, true);
1802 }
1803
1804 #[test]
1805 fn test_clock_bounds_below_minimum() {
1806 test_clock_bounds_helper(1, |_, lower, _| lower - 1, false);
1807 }
1808
1809 #[test]
1810 fn test_clock_bounds_above_maximum() {
1811 test_clock_bounds_helper(1, |_, _, upper| upper + 1, false);
1812 }
1813
1814 #[test]
1815 fn test_clock_bounds_multi_slot_gap() {
1816 test_clock_bounds_helper(5, |_, lower, _| lower + 2_000_000_000, true);
1819 }
1820
1821 #[test]
1822 fn test_clock_bounds_multi_slot_gap_exceeds() {
1823 test_clock_bounds_helper(5, |_, _, upper| upper + 1_000_000_000, false);
1825 }
1826
1827 #[test]
1828 fn test_clock_bounds_timestamp_equals_parent() {
1829 test_clock_bounds_helper(1, |parent_time, _, _| parent_time, false);
1831 }
1832
1833 #[test]
1834 fn test_clock_bounds_without_parent_nanosecond_clock_rejects_out_of_bounds() {
1835 let my_pubkey = Pubkey::new_unique();
1836 let mut processor = processor_after_header();
1837 let shred_version = rand::rng().random();
1838
1839 let (parent, bank_forks) = create_test_bank_alpenglow();
1840 assert_eq!(parent.get_nanosecond_clock(), None);
1841
1842 let bank = create_child_bank(&bank_forks, &parent, 1);
1843 let parent_time_nanos = bank.clock().unix_timestamp.saturating_mul(1_000_000_000);
1844 let elapsed_slot_duration_nanos =
1845 bank.slot_range_duration_nanos(parent.slot().saturating_add(1), bank.slot());
1846 let (_, upper_bound) = BlockComponentProcessor::nanosecond_time_bounds(
1847 parent_time_nanos,
1848 elapsed_slot_duration_nanos,
1849 );
1850
1851 let footer = VersionedBlockFooter::V1(BlockFooterV1 {
1852 bank_hash: Hash::new_unique(),
1853 block_producer_time_nanos: u64::try_from(upper_bound.saturating_add(1)).unwrap(),
1854 block_user_agent: vec![],
1855 block_final_cert: None,
1856 skip_reward_cert: None,
1857 notar_reward_cert: None,
1858 });
1859
1860 assert!(matches!(
1861 processor
1862 .on_footer(&my_pubkey, bank, parent, shred_version, footer, None)
1863 .unwrap_err(),
1864 BlockComponentProcessorError::NanosecondClockOutOfBounds
1865 ));
1866 }
1867
1868 #[test]
1869 fn test_clock_bounds_rejects_timestamp_above_i64() {
1870 let my_pubkey = Pubkey::new_unique();
1871 let mut processor = processor_after_header();
1872 let shred_version = rand::rng().random();
1873
1874 let (parent, bank_forks) = create_test_bank_alpenglow();
1875 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
1876 parent.update_clock_from_footer(parent_time_nanos);
1877 let bank = create_child_bank(&bank_forks, &parent, 1);
1878
1879 let footer = VersionedBlockFooter::V1(BlockFooterV1 {
1880 bank_hash: Hash::new_unique(),
1881 block_producer_time_nanos: u64::MAX,
1882 block_user_agent: vec![],
1883 block_final_cert: None,
1884 skip_reward_cert: None,
1885 notar_reward_cert: None,
1886 });
1887
1888 assert!(matches!(
1889 processor
1890 .on_footer(&my_pubkey, bank, parent, shred_version, footer, None)
1891 .unwrap_err(),
1892 BlockComponentProcessorError::NanosecondClockOutOfBounds
1893 ));
1894 }
1895
1896 fn test_nanosecond_time_bounds_helper(
1898 parent_time_nanos: i64,
1899 elapsed_slot_duration_nanos: u128,
1900 expected_lower: i64,
1901 expected_upper: i64,
1902 ) {
1903 let (lower, upper) = BlockComponentProcessor::nanosecond_time_bounds(
1904 parent_time_nanos,
1905 elapsed_slot_duration_nanos,
1906 );
1907
1908 assert_eq!(lower, expected_lower);
1909 assert_eq!(upper, expected_upper);
1910 }
1911
1912 #[test]
1913 fn test_nanosecond_time_bounds_calculation() {
1914 let parent_slot = 10;
1919 let parent_time = 1_000_000_000_000; let working_slot = 15;
1921 let slot_delta = working_slot - parent_slot;
1922 test_nanosecond_time_bounds_helper(
1923 parent_time,
1924 u128::from(slot_delta).saturating_mul(u128::from(DEFAULT_NS_PER_SLOT)),
1925 parent_time + 1,
1926 parent_time + (2 * DEFAULT_NS_PER_SLOT * slot_delta) as i64,
1927 );
1928 }
1929
1930 #[test]
1931 fn test_nanosecond_time_bounds_same_slot() {
1932 let parent_time = 1_000_000_000_000;
1939 test_nanosecond_time_bounds_helper(parent_time, 0, parent_time + 1, parent_time);
1940 }
1941
1942 #[test]
1943 fn test_nanosecond_time_bounds_saturates_upper_bound() {
1944 let parent_time = i64::MAX - 5;
1945 let (lower, upper) =
1946 BlockComponentProcessor::nanosecond_time_bounds(parent_time, u128::MAX);
1947
1948 assert_eq!(lower, parent_time + 1);
1949 assert_eq!(upper, i64::MAX);
1950 }
1951
1952 #[test]
1953 fn test_initial_up_reject() {
1954 let mut processor = BlockComponentProcessor::default();
1955 let update_parent = VersionedUpdateParent::V1(UpdateParentV1 {
1956 new_parent_slot: 0,
1957 new_parent_block_id: Hash::default(),
1958 });
1959
1960 assert!(matches!(
1961 processor.on_update_parent(4, &update_parent, false),
1962 Err(BlockComponentProcessorError::UnexpectedInitialUpdateParent)
1963 ));
1964 assert_eq!(processor.stage, BlockComponentStage::PreParentMarker);
1965 }
1966
1967 #[test]
1968 fn test_update_parent_rejects_non_first_leader_window_slot() {
1969 let mut processor = BlockComponentProcessor::default();
1970 let update_parent = VersionedUpdateParent::V1(UpdateParentV1 {
1971 new_parent_slot: 0,
1972 new_parent_block_id: Hash::default(),
1973 });
1974
1975 assert!(matches!(
1976 processor.on_update_parent(5, &update_parent, true),
1977 Err(BlockComponentProcessorError::UpdateParentNotFirstInLeaderWindow(5))
1978 ));
1979 assert_eq!(processor.stage, BlockComponentStage::PreParentMarker);
1980 }
1981
1982 #[test]
1983 fn test_initial_up_ok() {
1984 let mut processor = BlockComponentProcessor::default();
1985 let update_parent = VersionedUpdateParent::V1(UpdateParentV1 {
1986 new_parent_slot: 0,
1987 new_parent_block_id: Hash::default(),
1988 });
1989
1990 processor.on_update_parent(4, &update_parent, true).unwrap();
1991 assert_eq!(
1992 processor.stage,
1993 BlockComponentStage::AcceptingEntriesOrFooter {
1994 parent_marker: EntryParentMarker::UpdateParent,
1995 }
1996 );
1997 }
1998
1999 #[test]
2000 fn test_update_parent_after_header_abandoned_bank() {
2001 let mut processor = BlockComponentProcessor::default();
2002 processor
2003 .on_header(
2004 &VersionedBlockHeader::V1(BlockHeaderV1 {
2005 parent_slot: 0,
2006 parent_block_id: Hash::default(),
2007 }),
2008 0,
2009 )
2010 .unwrap();
2011
2012 let update_parent = VersionedUpdateParent::V1(UpdateParentV1 {
2013 new_parent_slot: 0,
2014 new_parent_block_id: Hash::default(),
2015 });
2016
2017 assert!(matches!(
2018 processor.on_update_parent(4, &update_parent, false),
2019 Err(BlockComponentProcessorError::AbandonedBank(_))
2020 ));
2021 }
2022
2023 #[test]
2024 fn test_update_parent_after_footer_error() {
2025 let mut processor = processor_after_footer();
2026 let update_parent = VersionedUpdateParent::V1(UpdateParentV1 {
2027 new_parent_slot: 0,
2028 new_parent_block_id: Hash::default(),
2029 });
2030
2031 assert_matches!(
2032 processor.on_update_parent(4, &update_parent, false),
2033 Err(BlockComponentProcessorError::SpuriousUpdateParent)
2034 );
2035 }
2036
2037 #[test]
2038 fn test_multiple_update_parents_error() {
2039 let mut processor = BlockComponentProcessor::default();
2040 let update_parent = VersionedUpdateParent::V1(UpdateParentV1 {
2041 new_parent_slot: 0,
2042 new_parent_block_id: Hash::default(),
2043 });
2044
2045 processor.on_update_parent(4, &update_parent, true).unwrap();
2047
2048 assert_matches!(
2050 processor.on_update_parent(4, &update_parent, true),
2051 Err(BlockComponentProcessorError::MultipleUpdateParents)
2052 );
2053 }
2054
2055 #[test]
2056 fn test_header_after_update_parent_error() {
2057 let mut processor = BlockComponentProcessor::default();
2058 processor
2059 .on_update_parent(
2060 4,
2061 &VersionedUpdateParent::V1(UpdateParentV1 {
2062 new_parent_slot: 0,
2063 new_parent_block_id: Hash::default(),
2064 }),
2065 true,
2066 )
2067 .unwrap();
2068
2069 let header = VersionedBlockHeader::V1(BlockHeaderV1 {
2070 parent_slot: 0,
2071 parent_block_id: Hash::default(),
2072 });
2073
2074 assert!(matches!(
2075 processor.on_header(&header, 0),
2076 Err(BlockComponentProcessorError::SpuriousUpdateParent)
2077 ));
2078 }
2079
2080 #[test]
2081 fn test_workflow_with_update_parent() {
2082 let migration_status = MigrationStatus::post_migration_status();
2083 let mut processor = BlockComponentProcessor::default();
2084 let (parent, bank_forks) = create_test_bank();
2085 let bank = create_child_bank(&bank_forks, &parent, 4);
2086 let slot = bank.slot();
2087 let shred_version = rand::rng().random();
2088
2089 processor
2090 .on_update_parent(
2091 slot,
2092 &VersionedUpdateParent::V1(UpdateParentV1 {
2093 new_parent_slot: 0,
2094 new_parent_block_id: Hash::default(),
2095 }),
2096 true,
2097 )
2098 .unwrap();
2099
2100 processor
2101 .on_entry_batch(&migration_status, slot, &[], false)
2102 .unwrap();
2103
2104 let parent_time_nanos = parent.clock().unix_timestamp.saturating_mul(1_000_000_000);
2105 let footer = VersionedBlockFooter::V1(BlockFooterV1 {
2106 bank_hash: Hash::new_unique(),
2107 block_producer_time_nanos: (parent_time_nanos + 100_000_000) as u64,
2108 block_user_agent: vec![],
2109 block_final_cert: None,
2110 skip_reward_cert: None,
2111 notar_reward_cert: None,
2112 });
2113 processor
2114 .on_footer(
2115 &migration_status.my_pubkey(),
2116 bank,
2117 parent,
2118 shred_version,
2119 footer,
2120 None,
2121 )
2122 .unwrap();
2123
2124 let good_alpentick = alpentick(1);
2125 processor
2126 .on_entry_batch(&migration_status, slot, &good_alpentick, true)
2127 .unwrap();
2128
2129 processor.on_final(&migration_status, slot, 0).unwrap();
2130 }
2131}