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