anvil_zksync_core/node/inner/
time.rs

1use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
2use zksync_error::anvil_zksync::{self, node::AnvilNodeResult};
3
4/// Read-only view on time.
5pub trait ReadTime: Send + Sync {
6    /// Alternative for [`Clone::clone`] that is object safe.
7    fn dyn_cloned(&self) -> Box<dyn ReadTime>;
8
9    /// Returns timestamp (in seconds) that the clock is currently on.
10    fn current_timestamp(&self) -> u64;
11}
12
13impl Clone for Box<dyn ReadTime> {
14    fn clone(&self) -> Self {
15        self.dyn_cloned()
16    }
17}
18
19#[derive(Debug, Clone)]
20pub(super) struct Time {
21    internal: Arc<RwLock<TimeState>>,
22}
23
24impl ReadTime for Time {
25    fn dyn_cloned(&self) -> Box<dyn ReadTime> {
26        Box::new(self.clone())
27    }
28
29    fn current_timestamp(&self) -> u64 {
30        self.get().current_timestamp
31    }
32}
33
34impl Time {
35    pub(super) fn new(current_timestamp: u64) -> Self {
36        let internal = Arc::new(RwLock::new(TimeState {
37            current_timestamp,
38            next_timestamp: None,
39            interval: None,
40        }));
41
42        Self { internal }
43    }
44
45    fn get(&self) -> RwLockReadGuard<TimeState> {
46        self.internal
47            .read()
48            .expect("TimestampWriter lock is poisoned")
49    }
50
51    fn get_mut(&self) -> RwLockWriteGuard<TimeState> {
52        self.internal
53            .write()
54            .expect("TimestampWriter lock is poisoned")
55    }
56
57    /// Sets last used timestamp (in seconds) to the provided value and returns the difference
58    /// between new value and old value (represented as a signed number of seconds).
59    pub(super) fn set_current_timestamp_unchecked(&self, timestamp: u64) -> i128 {
60        let mut this = self.get_mut();
61        let diff = (timestamp as i128).saturating_sub(this.current_timestamp as i128);
62        this.next_timestamp.take();
63        this.current_timestamp = timestamp;
64        diff
65    }
66
67    /// Forces clock to return provided value as the next timestamp. Time skip will not be performed
68    /// before the next invocation of `advance_timestamp`.
69    ///
70    /// Expects provided timestamp to be in the future, returns error otherwise.
71    pub(super) fn enforce_next_timestamp(&self, timestamp: u64) -> AnvilNodeResult<()> {
72        let mut this = self.get_mut();
73        if timestamp <= this.current_timestamp {
74            Err(anvil_zksync::node::TimestampBackwardsError {
75                timestamp_requested: timestamp.into(),
76                timestamp_now: this.current_timestamp.into(),
77            })
78        } else {
79            this.next_timestamp.replace(timestamp);
80            Ok(())
81        }
82    }
83
84    /// Fast-forwards time by the given amount of seconds.
85    pub(super) fn increase_time(&self, seconds: u64) -> u64 {
86        let mut this = self.get_mut();
87        let next = this.current_timestamp.saturating_add(seconds);
88        this.next_timestamp.take();
89        this.current_timestamp = next;
90        next
91    }
92
93    pub(super) fn get_block_timestamp_interval(&self) -> Option<u64> {
94        self.get().interval
95    }
96
97    /// Sets an interval to use when computing the next timestamp
98    ///
99    /// If an interval already exists, this will update the interval, otherwise a new interval will
100    /// be set starting with the current timestamp.
101    pub(super) fn set_block_timestamp_interval(&self, seconds: Option<u64>) {
102        self.get_mut().interval = seconds;
103    }
104
105    /// Removes the interval. Returns true if it existed before being removed, false otherwise.
106    pub(super) fn remove_block_timestamp_interval(&self) -> bool {
107        self.get_mut().interval.take().is_some()
108    }
109
110    /// Peek at what the next call to `advance_timestamp` will return.
111    pub(super) fn peek_next_timestamp(&self) -> u64 {
112        let internal = self.get();
113        internal.next_timestamp.unwrap_or_else(|| {
114            internal
115                .current_timestamp
116                .saturating_add(internal.interval())
117        })
118    }
119
120    /// Advances clock to the next timestamp and returns that timestamp in seconds.
121    ///
122    /// Subsequent calls to this method return monotonically increasing values. Time difference
123    /// between calls is implementation-specific.
124    pub(super) fn advance_timestamp(&self) -> u64 {
125        let mut internal = self.get_mut();
126        let next_timestamp = match internal.next_timestamp.take() {
127            Some(next_timestamp) => next_timestamp,
128            None => internal
129                .current_timestamp
130                .saturating_add(internal.interval()),
131        };
132
133        internal.current_timestamp = next_timestamp;
134        next_timestamp
135    }
136
137    /// Reset current timestamp to the provided value. WARNING: Moving clock to the past can cause
138    /// unexpected behavior.
139    pub(super) fn reset_to(&self, timestamp: u64) {
140        let mut internal = self.get_mut();
141        internal.next_timestamp.take();
142        internal.current_timestamp = timestamp;
143    }
144}
145
146#[derive(Debug, Default)]
147struct TimeState {
148    /// The current timestamp (in seconds). This timestamp is considered to be used already: there
149    /// might be a logical event that already happened on that timestamp (e.g. a block was sealed
150    /// with this timestamp).
151    current_timestamp: u64,
152    /// The next timestamp (in seconds) that the clock will be forced to advance to.
153    next_timestamp: Option<u64>,
154    /// The interval to use when determining the next timestamp to advance to.
155    interval: Option<u64>,
156}
157
158impl TimeState {
159    fn interval(&self) -> u64 {
160        self.interval.unwrap_or(1)
161    }
162}