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