Skip to main content

anvil/eth/backend/
time.rs

1//! Manages the block time
2
3use crate::eth::error::BlockchainError;
4use chrono::{DateTime, Utc};
5use parking_lot::RwLock;
6use std::{sync::Arc, time::Duration};
7
8/// Manages block time
9#[derive(Clone, Debug)]
10pub struct TimeManager {
11    state: Arc<RwLock<TimeState>>,
12}
13
14/// Timestamp controls that must be read and committed atomically.
15#[derive(Debug, Default)]
16struct TimeState {
17    offset: i128,
18    /// Explicit increases not yet committed, separate from the wall-clock offset.
19    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/// A timestamp prepared for a candidate block but not yet committed.
35#[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/// Time controls captured by an in-memory state snapshot.
46#[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/// A temporary additive time increase that can be rolled back after failed manual mining.
55#[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    /// Resets the current time manager to the given timestamp, resetting the offsets and
69    /// next block timestamp option
70    pub fn reset(&self, start_timestamp: u64) {
71        self.reset_timestamp(start_timestamp, true, None, None, 0);
72    }
73
74    /// Sets the current timestamp without changing when the current head was installed.
75    pub fn set_time(&self, timestamp: u64) {
76        self.reset_timestamp(timestamp, false, None, None, 0);
77    }
78
79    /// Restores the timestamp and offset captured by a state snapshot.
80    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    /// Returns the UNIX wall time in milliseconds when the current head was installed.
130    pub fn last_block_wall_time(&self) -> u64 {
131        self.state.read().last_block_wall_time
132    }
133
134    /// Records that a new latest block was created.
135    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    /// Adds the given `offset` to the already tracked offset and returns the result
141    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    /// Jumps forward in time by the given seconds
151    ///
152    /// This will apply a permanent offset to the natural UNIX Epoch timestamp
153    pub fn increase_time(&self, seconds: u64) -> i128 {
154        self.add_offset(seconds)
155    }
156
157    /// Applies a temporary increase that can be rolled back if mining fails.
158    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    /// Reverts a temporary increase unless an absolute time reset superseded it.
166    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    /// Sets the exact timestamp to use in the next block.
175    /// Fails if it's before the last timestamp.
176    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    /// Sets an interval to use when computing the next timestamp
191    ///
192    /// If an interval already exists, this will update the interval, otherwise a new interval will
193    /// be set starting with the current timestamp.
194    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    /// Returns the configured block timestamp interval.
200    pub(crate) fn block_timestamp_interval(&self) -> Option<u64> {
201        self.state.read().interval
202    }
203
204    /// Removes the interval if it exists
205    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    /// Computes the next timestamp without updating internals
215    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            // A burst of blocks within one wall-clock second leaves `last_timestamp` ahead of
229            // `wall + offset`; keep a pending explicit increase on top of the logical clock so the
230            // monotonicity clamp below does not swallow it.
231            let wall = current.saturating_add(state.offset) as u64;
232            (wall.max(last_timestamp.saturating_add(state.time_increase)), false)
233        };
234        // Equal timestamps are only allowed when explicitly requested (exact override, an
235        // interval, or a zero-interval mine such as `anvil_mine(n, 0)`). On the default path
236        // timestamps must strictly increase. The zero-interval flag only allows equality: the
237        // timestamp itself still comes from the wall clock or the configured interval.
238        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    /// Prepares the next timestamp without consuming a one-shot override.
252    pub(crate) fn prepare_next_timestamp(&self, pin_timestamp: bool) -> PendingBlockTimestamp {
253        self.prepare_next_timestamp_inner(None, pin_timestamp)
254    }
255
256    /// Prepares a logical block clock, using explicit controls instead of elapsed wall time.
257    ///
258    /// `increment` is the whole-second carry supplied by the chain's block schedule. The
259    /// minimum keeps a sub-second rollover monotonic even when an override repeats the parent.
260    #[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    /// Commits a timestamp after its candidate block finalized successfully.
309    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    /// Returns the current timestamp and updates the underlying offset and interval accordingly
329    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    /// Returns `now + offset` without the next-block monotonicity floor.
336    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    /// Returns the current timestamp for a call that does _not_ update the value
342    pub fn current_call_timestamp(&self) -> u64 {
343        self.prepare_next_timestamp(false).timestamp
344    }
345}
346
347/// Returns the `Utc` datetime for the given seconds since unix epoch
348pub fn utc_from_secs(secs: u64) -> DateTime<Utc> {
349    DateTime::from_timestamp(secs as i64, 0).unwrap_or(DateTime::<Utc>::MAX_UTC)
350}
351
352/// Returns the current duration since unix epoch.
353pub 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        // A burst within one wall-clock second leaves `last_timestamp` ahead of wall time.
438        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        // the increase is consumed: a plain block advances by one second again
459        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        // Move the wall-clock fallback without issuing an explicit time increase.
519        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}