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 ) -> (u64, Option<u64>, Option<i128>) {
219 let exact_timestamp = state.next_exact_timestamp;
220 let last_timestamp = state.last_timestamp;
221
222 let (mut next_timestamp, update_offset) = if let Some(next) = exact_timestamp {
223 (next.timestamp, true)
224 } else if let Some(interval) = state.interval {
225 (last_timestamp.saturating_add(interval), false)
226 } else {
227 (current.saturating_add(state.offset) as u64, false)
228 };
229 if next_timestamp < last_timestamp {
231 next_timestamp = last_timestamp + 1;
232 }
233 let next_offset = update_offset.then_some((next_timestamp as i128) - current);
234 (next_timestamp, exact_timestamp.map(|exact| exact.generation), next_offset)
235 }
236
237 pub(crate) fn prepare_next_timestamp(&self) -> PendingBlockTimestamp {
239 self.prepare_next_timestamp_inner(None)
240 }
241
242 #[cfg(any(feature = "base", test))]
247 pub(crate) fn prepare_next_timestamp_with_increment(
248 &self,
249 increment: u64,
250 minimum: u64,
251 ) -> PendingBlockTimestamp {
252 self.prepare_next_timestamp_inner(Some((increment, minimum)))
253 }
254
255 fn prepare_next_timestamp_inner(&self, schedule: Option<(u64, u64)>) -> PendingBlockTimestamp {
256 let current = duration_since_unix_epoch().as_secs() as i128;
257 let state = self.state.read();
258 let (timestamp, exact_generation, next_offset) =
259 if let Some((increment, minimum)) = schedule {
260 let timestamp = state
261 .next_exact_timestamp
262 .map_or_else(
263 || {
264 state
265 .last_timestamp
266 .saturating_add(state.interval.unwrap_or(increment))
267 .saturating_add(state.time_increase)
268 },
269 |next| next.timestamp,
270 )
271 .max(minimum);
272 (
273 timestamp,
274 state.next_exact_timestamp.map(|next| next.generation),
275 Some(timestamp as i128 - current),
276 )
277 } else {
278 Self::compute_next_timestamp(&state, current)
279 };
280 PendingBlockTimestamp {
281 timestamp,
282 exact_generation,
283 prepared_offset: state.offset,
284 offset_reset_generation: state.offset_reset_generation,
285 next_offset,
286 time_increase: state.time_increase,
287 }
288 }
289
290 pub(crate) fn commit_next_timestamp(&self, pending: PendingBlockTimestamp) {
292 let mut state = self.state.write();
293 if pending.exact_generation.is_some_and(|generation| {
294 state.next_exact_timestamp.is_some_and(|exact| exact.generation == generation)
295 }) {
296 state.next_exact_timestamp = None;
297 }
298 if let Some(next_offset) = pending.next_offset
299 && state.offset_reset_generation == pending.offset_reset_generation
300 {
301 let concurrent_offset = state.offset.saturating_sub(pending.prepared_offset);
302 state.offset = next_offset.saturating_add(concurrent_offset);
303 }
304 if state.offset_reset_generation == pending.offset_reset_generation {
305 state.time_increase = state.time_increase.saturating_sub(pending.time_increase);
306 state.last_timestamp = pending.timestamp;
307 }
308 }
309
310 pub fn next_timestamp(&self) -> u64 {
312 let pending = self.prepare_next_timestamp();
313 self.commit_next_timestamp(pending);
314 pending.timestamp
315 }
316
317 pub fn current_call_timestamp(&self) -> u64 {
319 self.prepare_next_timestamp().timestamp
320 }
321}
322
323pub fn utc_from_secs(secs: u64) -> DateTime<Utc> {
325 DateTime::from_timestamp(secs as i64, 0).unwrap_or(DateTime::<Utc>::MAX_UTC)
326}
327
328pub fn duration_since_unix_epoch() -> Duration {
330 use std::time::SystemTime;
331 let now = SystemTime::now();
332 now.duration_since(SystemTime::UNIX_EPOCH)
333 .unwrap_or_else(|err| panic!("Current time {now:?} is invalid: {err:?}"))
334}
335
336#[cfg(test)]
337mod tests {
338 use super::*;
339
340 #[test]
341 fn candidate_consumes_only_its_timestamp_override() {
342 let time = TimeManager::new(1);
343 time.set_next_block_timestamp(100).unwrap();
344 let pending = time.prepare_next_timestamp();
345
346 time.set_next_block_timestamp(100).unwrap();
347 time.commit_next_timestamp(pending);
348
349 let state = time.state.read();
350 assert_eq!(state.last_timestamp, 100);
351 assert_eq!(state.next_exact_timestamp.unwrap().timestamp, 100);
352 }
353
354 #[test]
355 fn candidate_commit_preserves_concurrent_time_increase() {
356 let time = TimeManager::new(1);
357 time.set_next_block_timestamp(100).unwrap();
358 let pending = time.prepare_next_timestamp();
359
360 time.increase_time(10);
361 time.commit_next_timestamp(pending);
362
363 let state = time.state.read();
364 assert_eq!(state.last_timestamp, 100);
365 assert_eq!(state.offset, pending.next_offset.unwrap() + 10);
366 }
367
368 #[test]
369 fn candidate_commit_preserves_concurrent_time_reset() {
370 let time = TimeManager::new(1);
371 time.set_next_block_timestamp(100).unwrap();
372 let pending = time.prepare_next_timestamp();
373
374 time.reset(1_000);
375 let reset_offset = time.offset();
376 time.commit_next_timestamp(pending);
377
378 let state = time.state.read();
379 assert_eq!(state.last_timestamp, 1_000);
380 assert_eq!(state.offset, reset_offset);
381 }
382
383 #[test]
384 fn failed_temporary_increase_preserves_concurrent_time_reset() {
385 let time = TimeManager::new(1);
386 let pending = time.apply_time_increase(60);
387
388 time.reset(1_000);
389 let reset_offset = time.offset();
390 time.revert_time_increase(pending);
391
392 assert_eq!(time.offset(), reset_offset);
393 }
394
395 #[test]
396 fn logical_clock_ignores_wall_time_and_preserves_explicit_controls() {
397 let time = TimeManager::new(100);
398 time.state.write().offset += 1_000;
400 assert!(time.current_call_timestamp() >= 1_100);
401 assert_eq!(time.prepare_next_timestamp_with_increment(0, 100).timestamp, 100);
402
403 time.increase_time(10);
404 let pending = time.prepare_next_timestamp_with_increment(0, 100);
405 assert_eq!(pending.timestamp, 110);
406 assert_eq!(time.prepare_next_timestamp_with_increment(0, 100).timestamp, 110);
407 time.increase_time(7);
408 time.commit_next_timestamp(pending);
409 let pending = time.prepare_next_timestamp_with_increment(1, 111);
410 assert_eq!(pending.timestamp, 118);
411 time.commit_next_timestamp(pending);
412 assert_eq!(time.prepare_next_timestamp_with_increment(0, 118).timestamp, 118);
413
414 time.set_block_timestamp_interval(2);
415 assert_eq!(time.prepare_next_timestamp_with_increment(0, 118).timestamp, 120);
416 time.set_next_block_timestamp(200).unwrap();
417 let pending = time.prepare_next_timestamp_with_increment(0, 118);
418 assert_eq!(pending.timestamp, 200);
419 time.set_next_block_timestamp(300).unwrap();
420 time.commit_next_timestamp(pending);
421 assert_eq!(time.prepare_next_timestamp_with_increment(0, 200).timestamp, 300);
422 }
423
424 #[test]
425 fn logical_clock_restores_pending_increases_and_absolute_resets() {
426 let time = TimeManager::new(100);
427 time.set_time(500);
428 time.increase_time(10);
429 let snapshot = time.snapshot();
430 time.commit_next_timestamp(time.prepare_next_timestamp_with_increment(0, 100));
431 time.restore(snapshot);
432 assert_eq!(time.prepare_next_timestamp_with_increment(0, 100).timestamp, 510);
433
434 let pending = time.prepare_next_timestamp_with_increment(0, 100);
435 time.set_time(1_000);
436 time.increase_time(20);
437 time.commit_next_timestamp(pending);
438 assert_eq!(time.prepare_next_timestamp_with_increment(0, 510).timestamp, 1_020);
439 }
440
441 #[test]
442 fn logical_clock_reverts_failed_manual_time_increase() {
443 let time = TimeManager::new(100);
444 let increase = time.apply_time_increase(60);
445 assert_eq!(time.prepare_next_timestamp_with_increment(0, 100).timestamp, 160);
446 time.revert_time_increase(increase);
447 assert_eq!(time.prepare_next_timestamp_with_increment(0, 100).timestamp, 100);
448 time.set_next_block_timestamp(100).unwrap();
449 assert_eq!(time.prepare_next_timestamp_with_increment(1, 101).timestamp, 101);
450 }
451}