anvil/eth/backend/
time.rs1use crate::eth::error::BlockchainError;
4use chrono::{DateTime, Utc};
5use parking_lot::RwLock;
6use std::{sync::Arc, time::Duration};
7
8pub fn utc_from_secs(secs: u64) -> DateTime<Utc> {
10 DateTime::from_timestamp(secs as i64, 0).unwrap_or(DateTime::<Utc>::MAX_UTC)
11}
12
13#[derive(Clone, Debug)]
15pub struct TimeManager {
16 state: Arc<RwLock<TimeState>>,
17}
18
19#[derive(Debug, Default)]
21struct TimeState {
22 offset: i128,
23 offset_reset_generation: u64,
24 last_timestamp: u64,
25 next_exact_timestamp: Option<TimestampOverride>,
26 interval: Option<u64>,
27 next_override_generation: u64,
28}
29
30#[derive(Clone, Copy, Debug)]
31struct TimestampOverride {
32 timestamp: u64,
33 generation: u64,
34}
35
36#[derive(Clone, Copy, Debug)]
38pub(crate) struct PendingBlockTimestamp {
39 pub(crate) timestamp: u64,
40 exact_generation: Option<u64>,
41 prepared_offset: i128,
42 offset_reset_generation: u64,
43 next_offset: Option<i128>,
44}
45
46#[derive(Clone, Copy, Debug)]
48pub(crate) struct PendingTimeIncrease {
49 seconds: u64,
50 offset_reset_generation: u64,
51}
52
53impl TimeManager {
54 pub fn new(start_timestamp: u64) -> Self {
55 let time_manager = Self { state: Default::default() };
56 time_manager.reset(start_timestamp);
57 time_manager
58 }
59
60 pub fn reset(&self, start_timestamp: u64) {
63 let current = duration_since_unix_epoch().as_secs() as i128;
64 let mut state = self.state.write();
65 state.last_timestamp = start_timestamp;
66 state.offset = (start_timestamp as i128) - current;
67 state.offset_reset_generation = state.offset_reset_generation.wrapping_add(1);
68 state.next_exact_timestamp = None;
69 state.next_override_generation = state.next_override_generation.wrapping_add(1);
70 }
71
72 pub fn offset(&self) -> i128 {
73 self.state.read().offset
74 }
75
76 fn add_offset(&self, offset: i128) -> i128 {
78 let mut state = self.state.write();
79 let next = state.offset.saturating_add(offset);
80 trace!(target: "time", "adding timestamp offset={}, total={}", offset, next);
81 state.offset = next;
82 next
83 }
84
85 pub fn increase_time(&self, seconds: u64) -> i128 {
89 self.add_offset(seconds as i128)
90 }
91
92 pub(crate) fn apply_time_increase(&self, seconds: u64) -> PendingTimeIncrease {
94 let mut state = self.state.write();
95 state.offset = state.offset.saturating_add(seconds as i128);
96 PendingTimeIncrease { seconds, offset_reset_generation: state.offset_reset_generation }
97 }
98
99 pub(crate) fn revert_time_increase(&self, pending: PendingTimeIncrease) {
101 let mut state = self.state.write();
102 if state.offset_reset_generation == pending.offset_reset_generation {
103 state.offset = state.offset.saturating_sub(pending.seconds as i128);
104 }
105 }
106
107 pub fn set_next_block_timestamp(&self, timestamp: u64) -> Result<(), BlockchainError> {
110 trace!(target: "time", "override next timestamp {}", timestamp);
111 let mut state = self.state.write();
112 if timestamp < state.last_timestamp {
113 return Err(BlockchainError::TimestampError(format!(
114 "{timestamp} is lower than previous block's timestamp"
115 )));
116 }
117 state.next_override_generation = state.next_override_generation.wrapping_add(1);
118 state.next_exact_timestamp =
119 Some(TimestampOverride { timestamp, generation: state.next_override_generation });
120 Ok(())
121 }
122
123 pub fn set_block_timestamp_interval(&self, interval: u64) {
128 trace!(target: "time", "set interval {}", interval);
129 self.state.write().interval = Some(interval);
130 }
131
132 pub(crate) fn block_timestamp_interval(&self) -> Option<u64> {
134 self.state.read().interval
135 }
136
137 pub fn remove_block_timestamp_interval(&self) -> bool {
139 if self.state.write().interval.take().is_some() {
140 trace!(target: "time", "removed interval");
141 true
142 } else {
143 false
144 }
145 }
146
147 fn compute_next_timestamp(
149 state: &TimeState,
150 current: i128,
151 ) -> (u64, Option<u64>, Option<i128>) {
152 let exact_timestamp = state.next_exact_timestamp;
153 let last_timestamp = state.last_timestamp;
154
155 let (mut next_timestamp, update_offset) = if let Some(next) = exact_timestamp {
156 (next.timestamp, true)
157 } else if let Some(interval) = state.interval {
158 (last_timestamp.saturating_add(interval), false)
159 } else {
160 (current.saturating_add(state.offset) as u64, false)
161 };
162 if next_timestamp < last_timestamp {
164 next_timestamp = last_timestamp + 1;
165 }
166 let next_offset = update_offset.then_some((next_timestamp as i128) - current);
167 (next_timestamp, exact_timestamp.map(|exact| exact.generation), next_offset)
168 }
169
170 pub(crate) fn prepare_next_timestamp(&self) -> PendingBlockTimestamp {
172 let current = duration_since_unix_epoch().as_secs() as i128;
173 let state = self.state.read();
174 let (timestamp, exact_generation, next_offset) =
175 Self::compute_next_timestamp(&state, current);
176 PendingBlockTimestamp {
177 timestamp,
178 exact_generation,
179 prepared_offset: state.offset,
180 offset_reset_generation: state.offset_reset_generation,
181 next_offset,
182 }
183 }
184
185 pub(crate) fn commit_next_timestamp(&self, pending: PendingBlockTimestamp) {
187 let mut state = self.state.write();
188 if pending.exact_generation.is_some_and(|generation| {
189 state.next_exact_timestamp.is_some_and(|exact| exact.generation == generation)
190 }) {
191 state.next_exact_timestamp = None;
192 }
193 if let Some(next_offset) = pending.next_offset
194 && state.offset_reset_generation == pending.offset_reset_generation
195 {
196 let concurrent_offset = state.offset.saturating_sub(pending.prepared_offset);
197 state.offset = next_offset.saturating_add(concurrent_offset);
198 }
199 state.last_timestamp = pending.timestamp;
200 }
201
202 pub fn next_timestamp(&self) -> u64 {
204 let pending = self.prepare_next_timestamp();
205 self.commit_next_timestamp(pending);
206 pending.timestamp
207 }
208
209 pub fn current_call_timestamp(&self) -> u64 {
211 self.prepare_next_timestamp().timestamp
212 }
213}
214
215pub fn duration_since_unix_epoch() -> Duration {
217 use std::time::SystemTime;
218 let now = SystemTime::now();
219 now.duration_since(SystemTime::UNIX_EPOCH)
220 .unwrap_or_else(|err| panic!("Current time {now:?} is invalid: {err:?}"))
221}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226
227 #[test]
228 fn candidate_consumes_only_its_timestamp_override() {
229 let time = TimeManager::new(1);
230 time.set_next_block_timestamp(100).unwrap();
231 let pending = time.prepare_next_timestamp();
232
233 time.set_next_block_timestamp(100).unwrap();
234 time.commit_next_timestamp(pending);
235
236 let state = time.state.read();
237 assert_eq!(state.last_timestamp, 100);
238 assert_eq!(state.next_exact_timestamp.unwrap().timestamp, 100);
239 }
240
241 #[test]
242 fn candidate_commit_preserves_concurrent_time_increase() {
243 let time = TimeManager::new(1);
244 time.set_next_block_timestamp(100).unwrap();
245 let pending = time.prepare_next_timestamp();
246
247 time.increase_time(10);
248 time.commit_next_timestamp(pending);
249
250 let state = time.state.read();
251 assert_eq!(state.last_timestamp, 100);
252 assert_eq!(state.offset, pending.next_offset.unwrap() + 10);
253 }
254
255 #[test]
256 fn candidate_commit_preserves_concurrent_time_reset() {
257 let time = TimeManager::new(1);
258 time.set_next_block_timestamp(100).unwrap();
259 let pending = time.prepare_next_timestamp();
260
261 time.reset(1_000);
262 let reset_offset = time.offset();
263 time.commit_next_timestamp(pending);
264
265 let state = time.state.read();
266 assert_eq!(state.last_timestamp, 100);
267 assert_eq!(state.offset, reset_offset);
268 }
269
270 #[test]
271 fn failed_temporary_increase_preserves_concurrent_time_reset() {
272 let time = TimeManager::new(1);
273 let pending = time.apply_time_increase(60);
274
275 time.reset(1_000);
276 let reset_offset = time.offset();
277 time.revert_time_increase(pending);
278
279 assert_eq!(time.offset(), reset_offset);
280 }
281}