1use crate::eth::error::BlockchainError;
4use chrono::{DateTime, Utc};
5use parking_lot::RwLock;
6use std::{sync::Arc, time::Duration};
7
8#[derive(Clone, Debug)]
10pub struct TimeManager {
11 state: Arc<RwLock<TimeState>>,
12}
13
14#[derive(Debug, Default)]
16struct TimeState {
17 offset: i128,
18 time_increase: u64,
20 offset_reset_generation: u64,
21 last_timestamp: u64,
22 last_block_wall_time: u64,
23 next_exact_timestamp: Option<TimestampOverride>,
24 interval: Option<u64>,
25 next_override_generation: u64,
26}
27
28#[derive(Clone, Copy, Debug)]
29struct TimestampOverride {
30 timestamp: u64,
31 generation: u64,
32}
33
34#[derive(Clone, Copy, Debug)]
36pub(crate) struct PendingBlockTimestamp {
37 pub(crate) timestamp: u64,
38 exact_generation: Option<u64>,
39 prepared_offset: i128,
40 offset_reset_generation: u64,
41 next_offset: Option<i128>,
42 time_increase: u64,
43}
44
45#[derive(Clone, Copy, Debug)]
47pub(crate) struct TimeSnapshot {
48 offset: i128,
49 last_timestamp: u64,
50 next_block_timestamp: Option<u64>,
51 time_increase: u64,
52}
53
54#[derive(Clone, Copy, Debug)]
56pub(crate) struct PendingTimeIncrease {
57 seconds: u64,
58 offset_reset_generation: u64,
59}
60
61impl TimeManager {
62 pub fn new(start_timestamp: u64) -> Self {
63 let time_manager = Self { state: Default::default() };
64 time_manager.reset(start_timestamp);
65 time_manager
66 }
67
68 pub fn reset(&self, start_timestamp: u64) {
71 self.reset_timestamp(start_timestamp, true, None, None, 0);
72 }
73
74 pub fn set_time(&self, timestamp: u64) {
76 self.reset_timestamp(timestamp, false, None, None, 0);
77 }
78
79 pub(crate) fn restore(&self, snapshot: TimeSnapshot) {
81 self.reset_timestamp(
82 snapshot.last_timestamp,
83 true,
84 Some(snapshot.offset),
85 snapshot.next_block_timestamp,
86 snapshot.time_increase,
87 );
88 }
89
90 fn reset_timestamp(
91 &self,
92 start_timestamp: u64,
93 mark_new_head: bool,
94 offset: Option<i128>,
95 next_block_timestamp: Option<u64>,
96 time_increase: u64,
97 ) {
98 let current = duration_since_unix_epoch();
99 let mut state = self.state.write();
100 state.last_timestamp = start_timestamp;
101 state.time_increase = time_increase;
102 if mark_new_head {
103 state.last_block_wall_time = current.as_millis().try_into().unwrap_or(u64::MAX);
104 }
105 state.offset =
106 offset.unwrap_or_else(|| (start_timestamp as i128) - current.as_secs() as i128);
107 state.offset_reset_generation = state.offset_reset_generation.wrapping_add(1);
108 state.next_override_generation = state.next_override_generation.wrapping_add(1);
109 state.next_exact_timestamp = next_block_timestamp.map(|timestamp| TimestampOverride {
110 timestamp,
111 generation: state.next_override_generation,
112 });
113 }
114
115 pub fn offset(&self) -> i128 {
116 self.state.read().offset
117 }
118
119 pub(crate) fn snapshot(&self) -> TimeSnapshot {
120 let state = self.state.read();
121 TimeSnapshot {
122 offset: state.offset,
123 last_timestamp: state.last_timestamp,
124 next_block_timestamp: state.next_exact_timestamp.map(|override_| override_.timestamp),
125 time_increase: state.time_increase,
126 }
127 }
128
129 pub fn last_block_wall_time(&self) -> u64 {
131 self.state.read().last_block_wall_time
132 }
133
134 pub(crate) fn mark_block_created(&self) {
136 self.state.write().last_block_wall_time =
137 duration_since_unix_epoch().as_millis().try_into().unwrap_or(u64::MAX);
138 }
139
140 fn add_offset(&self, offset: u64) -> i128 {
142 let mut state = self.state.write();
143 let next = state.offset.saturating_add(offset as i128);
144 trace!(target: "time", "adding timestamp offset={}, total={}", offset, next);
145 state.offset = next;
146 state.time_increase = state.time_increase.saturating_add(offset);
147 next
148 }
149
150 pub fn increase_time(&self, seconds: u64) -> i128 {
154 self.add_offset(seconds)
155 }
156
157 pub(crate) fn apply_time_increase(&self, seconds: u64) -> PendingTimeIncrease {
159 let mut state = self.state.write();
160 state.offset = state.offset.saturating_add(seconds as i128);
161 state.time_increase = state.time_increase.saturating_add(seconds);
162 PendingTimeIncrease { seconds, offset_reset_generation: state.offset_reset_generation }
163 }
164
165 pub(crate) fn revert_time_increase(&self, pending: PendingTimeIncrease) {
167 let mut state = self.state.write();
168 if state.offset_reset_generation == pending.offset_reset_generation {
169 state.offset = state.offset.saturating_sub(pending.seconds as i128);
170 state.time_increase = state.time_increase.saturating_sub(pending.seconds);
171 }
172 }
173
174 pub fn set_next_block_timestamp(&self, timestamp: u64) -> Result<(), BlockchainError> {
177 trace!(target: "time", "override next timestamp {}", timestamp);
178 let mut state = self.state.write();
179 if timestamp < state.last_timestamp {
180 return Err(BlockchainError::TimestampError(format!(
181 "{timestamp} is lower than previous block's timestamp"
182 )));
183 }
184 state.next_override_generation = state.next_override_generation.wrapping_add(1);
185 state.next_exact_timestamp =
186 Some(TimestampOverride { timestamp, generation: state.next_override_generation });
187 Ok(())
188 }
189
190 pub fn set_block_timestamp_interval(&self, interval: u64) {
195 trace!(target: "time", "set interval {}", interval);
196 self.state.write().interval = Some(interval);
197 }
198
199 pub(crate) fn block_timestamp_interval(&self) -> Option<u64> {
201 self.state.read().interval
202 }
203
204 pub fn remove_block_timestamp_interval(&self) -> bool {
206 if self.state.write().interval.take().is_some() {
207 trace!(target: "time", "removed interval");
208 true
209 } else {
210 false
211 }
212 }
213
214 fn compute_next_timestamp(
216 state: &TimeState,
217 current: i128,
218 pin_timestamp: bool,
219 ) -> (u64, Option<u64>, Option<i128>) {
220 let exact_timestamp = state.next_exact_timestamp;
221 let last_timestamp = state.last_timestamp;
222
223 let (mut next_timestamp, update_offset) = if let Some(next) = exact_timestamp {
224 (next.timestamp, true)
225 } else if let Some(interval) = state.interval {
226 (last_timestamp.saturating_add(interval), false)
227 } else {
228 let wall = current.saturating_add(state.offset) as u64;
232 (wall.max(last_timestamp.saturating_add(state.time_increase)), false)
233 };
234 let allow_equal = exact_timestamp.is_some() || pin_timestamp || state.interval.is_some();
239 let too_low = if allow_equal {
240 next_timestamp < last_timestamp
241 } else {
242 next_timestamp <= last_timestamp
243 };
244 if too_low {
245 next_timestamp = last_timestamp.saturating_add(1);
246 }
247 let next_offset = update_offset.then_some((next_timestamp as i128) - current);
248 (next_timestamp, exact_timestamp.map(|exact| exact.generation), next_offset)
249 }
250
251 pub(crate) fn prepare_next_timestamp(&self, pin_timestamp: bool) -> PendingBlockTimestamp {
253 self.prepare_next_timestamp_inner(None, pin_timestamp)
254 }
255
256 #[cfg(any(feature = "base", test))]
261 pub(crate) fn prepare_next_timestamp_with_increment(
262 &self,
263 increment: u64,
264 minimum: u64,
265 ) -> PendingBlockTimestamp {
266 self.prepare_next_timestamp_inner(Some((increment, minimum)), false)
267 }
268
269 fn prepare_next_timestamp_inner(
270 &self,
271 schedule: Option<(u64, u64)>,
272 pin_timestamp: bool,
273 ) -> PendingBlockTimestamp {
274 let current = duration_since_unix_epoch().as_secs() as i128;
275 let state = self.state.read();
276 let (timestamp, exact_generation, next_offset) =
277 if let Some((increment, minimum)) = schedule {
278 let timestamp = state
279 .next_exact_timestamp
280 .map_or_else(
281 || {
282 state
283 .last_timestamp
284 .saturating_add(state.interval.unwrap_or(increment))
285 .saturating_add(state.time_increase)
286 },
287 |next| next.timestamp,
288 )
289 .max(minimum);
290 (
291 timestamp,
292 state.next_exact_timestamp.map(|next| next.generation),
293 Some(timestamp as i128 - current),
294 )
295 } else {
296 Self::compute_next_timestamp(&state, current, pin_timestamp)
297 };
298 PendingBlockTimestamp {
299 timestamp,
300 exact_generation,
301 prepared_offset: state.offset,
302 offset_reset_generation: state.offset_reset_generation,
303 next_offset,
304 time_increase: state.time_increase,
305 }
306 }
307
308 pub(crate) fn commit_next_timestamp(&self, pending: PendingBlockTimestamp) {
310 let mut state = self.state.write();
311 if pending.exact_generation.is_some_and(|generation| {
312 state.next_exact_timestamp.is_some_and(|exact| exact.generation == generation)
313 }) {
314 state.next_exact_timestamp = None;
315 }
316 if let Some(next_offset) = pending.next_offset
317 && state.offset_reset_generation == pending.offset_reset_generation
318 {
319 let concurrent_offset = state.offset.saturating_sub(pending.prepared_offset);
320 state.offset = next_offset.saturating_add(concurrent_offset);
321 }
322 if state.offset_reset_generation == pending.offset_reset_generation {
323 state.time_increase = state.time_increase.saturating_sub(pending.time_increase);
324 state.last_timestamp = pending.timestamp;
325 }
326 }
327
328 pub fn next_timestamp(&self) -> u64 {
330 let pending = self.prepare_next_timestamp(false);
331 self.commit_next_timestamp(pending);
332 pending.timestamp
333 }
334
335 pub(crate) fn current_wall_timestamp(&self) -> u64 {
337 let current = duration_since_unix_epoch().as_secs() as i128;
338 current.saturating_add(self.state.read().offset) as u64
339 }
340
341 pub fn current_call_timestamp(&self) -> u64 {
343 self.prepare_next_timestamp(false).timestamp
344 }
345}
346
347pub fn utc_from_secs(secs: u64) -> DateTime<Utc> {
349 DateTime::from_timestamp(secs as i64, 0).unwrap_or(DateTime::<Utc>::MAX_UTC)
350}
351
352pub fn duration_since_unix_epoch() -> Duration {
354 use std::time::SystemTime;
355 let now = SystemTime::now();
356 now.duration_since(SystemTime::UNIX_EPOCH)
357 .unwrap_or_else(|err| panic!("Current time {now:?} is invalid: {err:?}"))
358}
359
360#[cfg(test)]
361mod tests {
362 use super::*;
363
364 #[test]
365 fn candidate_consumes_only_its_timestamp_override() {
366 let time = TimeManager::new(1);
367 time.set_next_block_timestamp(100).unwrap();
368 let pending = time.prepare_next_timestamp(false);
369
370 time.set_next_block_timestamp(100).unwrap();
371 time.commit_next_timestamp(pending);
372
373 let state = time.state.read();
374 assert_eq!(state.last_timestamp, 100);
375 assert_eq!(state.next_exact_timestamp.unwrap().timestamp, 100);
376 }
377
378 #[test]
379 fn candidate_commit_preserves_concurrent_time_increase() {
380 let time = TimeManager::new(1);
381 time.set_next_block_timestamp(100).unwrap();
382 let pending = time.prepare_next_timestamp(false);
383
384 time.increase_time(10);
385 time.commit_next_timestamp(pending);
386
387 let state = time.state.read();
388 assert_eq!(state.last_timestamp, 100);
389 assert_eq!(state.offset, pending.next_offset.unwrap() + 10);
390 }
391
392 #[test]
393 fn candidate_commit_preserves_concurrent_time_reset() {
394 let time = TimeManager::new(1);
395 time.set_next_block_timestamp(100).unwrap();
396 let pending = time.prepare_next_timestamp(false);
397
398 time.reset(1_000);
399 let reset_offset = time.offset();
400 time.commit_next_timestamp(pending);
401
402 let state = time.state.read();
403 assert_eq!(state.last_timestamp, 1_000);
404 assert_eq!(state.offset, reset_offset);
405 }
406
407 #[test]
408 fn failed_temporary_increase_preserves_concurrent_time_reset() {
409 let time = TimeManager::new(1);
410 let pending = time.apply_time_increase(60);
411
412 time.reset(1_000);
413 let reset_offset = time.offset();
414 time.revert_time_increase(pending);
415
416 assert_eq!(time.offset(), reset_offset);
417 }
418
419 #[test]
420 fn default_path_timestamps_strictly_increase() {
421 let state = TimeState { last_timestamp: 1_000, ..Default::default() };
422
423 assert_eq!(TimeManager::compute_next_timestamp(&state, 999, false).0, 1_001);
424 assert_eq!(TimeManager::compute_next_timestamp(&state, 1_000, false).0, 1_001);
425 assert_eq!(TimeManager::compute_next_timestamp(&state, 1_002, false).0, 1_002);
426 }
427
428 #[test]
429 fn default_path_advances_after_time_increase() {
430 let state = TimeState { offset: 10_000, last_timestamp: 11_000, ..Default::default() };
431
432 assert_eq!(TimeManager::compute_next_timestamp(&state, 1_000, false).0, 11_001);
433 }
434
435 #[test]
436 fn default_path_preserves_time_increase_after_burst() {
437 let state = TimeState {
439 offset: 10,
440 last_timestamp: 1_099,
441 time_increase: 10,
442 ..Default::default()
443 };
444
445 assert_eq!(TimeManager::compute_next_timestamp(&state, 1_000, false).0, 1_109);
446 }
447
448 #[test]
449 fn burst_then_increase_time_keeps_full_increase() {
450 let time = TimeManager::new(1_000);
451 for _ in 0..100 {
452 time.next_timestamp();
453 }
454 let last = time.next_timestamp();
455
456 let _ = time.apply_time_increase(10);
457 assert_eq!(time.next_timestamp(), last + 10);
458 assert_eq!(time.next_timestamp(), last + 11);
460 }
461
462 #[test]
463 fn wall_timestamp_has_no_next_block_floor() {
464 let time = TimeManager::new(1_000);
465 time.state.write().last_timestamp = 5_000;
466
467 assert_eq!(time.current_call_timestamp(), 5_001);
468 assert!(time.current_wall_timestamp() < 1_100);
469 }
470
471 #[test]
472 fn explicit_zero_interval_honours_pending_time_increase() {
473 let state = TimeState { last_timestamp: 1_000, time_increase: 3_600, ..Default::default() };
474
475 assert_eq!(TimeManager::compute_next_timestamp(&state, 1_001, true).0, 4_600);
476 }
477
478 #[test]
479 fn explicit_zero_interval_allows_equal_timestamp() {
480 let state = TimeState { last_timestamp: 1_000, ..Default::default() };
481
482 assert_eq!(TimeManager::compute_next_timestamp(&state, 1_000, true).0, 1_000);
483 assert_eq!(TimeManager::compute_next_timestamp(&state, 1_000, false).0, 1_001);
484 }
485
486 #[test]
487 fn explicit_zero_interval_keeps_wall_clock_progress() {
488 let state = TimeState { last_timestamp: 1_000, ..Default::default() };
489
490 assert_eq!(TimeManager::compute_next_timestamp(&state, 1_002, true).0, 1_002);
491 }
492
493 #[test]
494 fn explicit_zero_interval_keeps_persistent_interval() {
495 let state = TimeState { last_timestamp: 1_000, interval: Some(12), ..Default::default() };
496
497 assert_eq!(TimeManager::compute_next_timestamp(&state, 1_000, true).0, 1_012);
498 }
499
500 #[test]
501 fn zero_interval_still_allows_equal_timestamps() {
502 let time = TimeManager::new(1_000);
503 time.set_block_timestamp_interval(0);
504 assert_eq!(time.next_timestamp(), 1_000);
505 }
506
507 #[test]
508 fn exact_next_timestamp_may_equal_last() {
509 let time = TimeManager::new(1_000);
510 time.set_next_block_timestamp(1_000).unwrap();
511 assert_eq!(time.next_timestamp(), 1_000);
512 assert!(time.next_timestamp() > 1_000);
513 }
514
515 #[test]
516 fn logical_clock_ignores_wall_time_and_preserves_explicit_controls() {
517 let time = TimeManager::new(100);
518 time.state.write().offset += 1_000;
520 assert!(time.current_call_timestamp() >= 1_100);
521 assert_eq!(time.prepare_next_timestamp_with_increment(0, 100).timestamp, 100);
522
523 time.increase_time(10);
524 let pending = time.prepare_next_timestamp_with_increment(0, 100);
525 assert_eq!(pending.timestamp, 110);
526 assert_eq!(time.prepare_next_timestamp_with_increment(0, 100).timestamp, 110);
527 time.increase_time(7);
528 time.commit_next_timestamp(pending);
529 let pending = time.prepare_next_timestamp_with_increment(1, 111);
530 assert_eq!(pending.timestamp, 118);
531 time.commit_next_timestamp(pending);
532 assert_eq!(time.prepare_next_timestamp_with_increment(0, 118).timestamp, 118);
533
534 time.set_block_timestamp_interval(2);
535 assert_eq!(time.prepare_next_timestamp_with_increment(0, 118).timestamp, 120);
536 time.set_next_block_timestamp(200).unwrap();
537 let pending = time.prepare_next_timestamp_with_increment(0, 118);
538 assert_eq!(pending.timestamp, 200);
539 time.set_next_block_timestamp(300).unwrap();
540 time.commit_next_timestamp(pending);
541 assert_eq!(time.prepare_next_timestamp_with_increment(0, 200).timestamp, 300);
542 }
543
544 #[test]
545 fn logical_clock_restores_pending_increases_and_absolute_resets() {
546 let time = TimeManager::new(100);
547 time.set_time(500);
548 time.increase_time(10);
549 let snapshot = time.snapshot();
550 time.commit_next_timestamp(time.prepare_next_timestamp_with_increment(0, 100));
551 time.restore(snapshot);
552 assert_eq!(time.prepare_next_timestamp_with_increment(0, 100).timestamp, 510);
553
554 let pending = time.prepare_next_timestamp_with_increment(0, 100);
555 time.set_time(1_000);
556 time.increase_time(20);
557 time.commit_next_timestamp(pending);
558 assert_eq!(time.prepare_next_timestamp_with_increment(0, 510).timestamp, 1_020);
559 }
560
561 #[test]
562 fn logical_clock_reverts_failed_manual_time_increase() {
563 let time = TimeManager::new(100);
564 let increase = time.apply_time_increase(60);
565 assert_eq!(time.prepare_next_timestamp_with_increment(0, 100).timestamp, 160);
566 time.revert_time_increase(increase);
567 assert_eq!(time.prepare_next_timestamp_with_increment(0, 100).timestamp, 100);
568 time.set_next_block_timestamp(100).unwrap();
569 assert_eq!(time.prepare_next_timestamp_with_increment(1, 101).timestamp, 101);
570 }
571}