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