11use {
22 crate :: bank:: Bank ,
33 agave_votor_messages:: migration:: MigrationStatus ,
4+ solana_clock:: { Slot , DEFAULT_MS_PER_SLOT } ,
45 solana_entry:: block_component:: {
56 BlockFooterV1 , BlockMarkerV1 , VersionedBlockFooter , VersionedBlockHeader ,
67 VersionedBlockMarker ,
@@ -21,6 +22,8 @@ pub enum BlockComponentProcessorError {
2122 MultipleBlockHeaders ,
2223 #[ error( "BlockComponent detected pre-migration" ) ]
2324 BlockComponentPreMigration ,
25+ #[ error( "Nanosecond clock out of bounds" ) ]
26+ NanosecondClockOutOfBounds ,
2427}
2528
2629#[ derive( Default ) ]
@@ -67,6 +70,7 @@ impl BlockComponentProcessor {
6770 pub fn on_marker (
6871 & mut self ,
6972 bank : Arc < Bank > ,
73+ parent_bank : Arc < Bank > ,
7074 marker : & VersionedBlockMarker ,
7175 migration_status : & MigrationStatus ,
7276 is_final : bool ,
@@ -79,7 +83,9 @@ impl BlockComponentProcessor {
7983 let VersionedBlockMarker :: V1 ( marker) = marker;
8084
8185 match marker {
82- BlockMarkerV1 :: BlockFooter ( footer) => self . on_footer ( bank, footer. inner ( ) ) ,
86+ BlockMarkerV1 :: BlockFooter ( footer) => {
87+ self . on_footer ( bank, parent_bank, footer. inner ( ) )
88+ }
8389 BlockMarkerV1 :: BlockHeader ( header) => self . on_header ( header. inner ( ) ) ,
8490 // We process UpdateParent messages on shred ingest, so no callback needed here.
8591 BlockMarkerV1 :: UpdateParent ( _) => Ok ( ( ) ) ,
@@ -96,6 +102,7 @@ impl BlockComponentProcessor {
96102 fn on_footer (
97103 & mut self ,
98104 bank : Arc < Bank > ,
105+ parent_bank : Arc < Bank > ,
99106 footer : & VersionedBlockFooter ,
100107 ) -> Result < ( ) , BlockComponentProcessorError > {
101108 // The block header must be the first component of each block.
@@ -108,6 +115,8 @@ impl BlockComponentProcessor {
108115 }
109116
110117 let VersionedBlockFooter :: V1 ( footer) = footer;
118+
119+ Self :: enforce_nanosecond_clock_bounds ( bank. clone ( ) , parent_bank, footer) ?;
111120 Self :: update_bank_with_footer ( bank, footer) ;
112121
113122 self . has_footer = true ;
@@ -126,9 +135,53 @@ impl BlockComponentProcessor {
126135 Ok ( ( ) )
127136 }
128137
138+ fn enforce_nanosecond_clock_bounds (
139+ bank : Arc < Bank > ,
140+ parent_bank : Arc < Bank > ,
141+ footer : & BlockFooterV1 ,
142+ ) -> Result < ( ) , BlockComponentProcessorError > {
143+ // If nanosecond clock hasn't been populated, don't enforce bounds yet.
144+ let Some ( parent_time_nanos) = parent_bank. get_nanosecond_clock ( ) else {
145+ return Ok ( ( ) ) ;
146+ } ;
147+
148+ let parent_slot = parent_bank. slot ( ) ;
149+ let current_time_nanos = i64:: try_from ( footer. block_producer_time_nanos ) . unwrap_or ( i64:: MAX ) ;
150+ let current_slot = bank. slot ( ) ;
151+
152+ let ( lower_bound_nanos, upper_bound_nanos) =
153+ Self :: nanosecond_time_bounds ( parent_slot, parent_time_nanos, current_slot) ;
154+
155+ if lower_bound_nanos <= current_time_nanos && current_time_nanos <= upper_bound_nanos {
156+ Ok ( ( ) )
157+ } else {
158+ Err ( BlockComponentProcessorError :: NanosecondClockOutOfBounds )
159+ }
160+ }
161+
162+ /// Given the parent slot, parent time, and slot, calculate the inclusive
163+ /// bounds for the block producer timestamp.
164+ pub fn nanosecond_time_bounds (
165+ parent_slot : Slot ,
166+ parent_time_nanos : i64 ,
167+ slot : Slot ,
168+ ) -> ( i64 , i64 ) {
169+ let default_ns_per_slot = i64:: try_from ( DEFAULT_MS_PER_SLOT )
170+ . unwrap_or ( i64:: MAX )
171+ . saturating_mul ( 1_000_000 ) ;
172+ let diff_slots = i64:: try_from ( slot. saturating_sub ( parent_slot) ) . unwrap_or ( i64:: MAX ) ;
173+
174+ let min_working_bank_time = parent_time_nanos. saturating_add ( 1 ) ;
175+ let max_working_bank_time = parent_time_nanos
176+ . saturating_add ( diff_slots. saturating_mul ( 2 ) . saturating_mul ( default_ns_per_slot) ) ;
177+
178+ ( min_working_bank_time, max_working_bank_time)
179+ }
180+
129181 pub fn update_bank_with_footer ( bank : Arc < Bank > , footer : & BlockFooterV1 ) {
130182 // Update clock sysvar from footer timestamp.
131- bank. update_clock_from_footer ( footer. block_producer_time_nanos as i64 ) ;
183+ let unix_timestamp_nanos = i64:: try_from ( footer. block_producer_time_nanos ) . unwrap_or ( i64:: MAX ) ;
184+ bank. update_clock_from_footer ( unix_timestamp_nanos) ;
132185
133186 // TODO: rewards
134187 }
0 commit comments