anvil/eth/backend/
time.rs1use 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 offset_reset_generation: u64,
19 last_timestamp: u64,
20 last_block_wall_time: u64,
21 next_exact_timestamp: Option<TimestampOverride>,
22 interval: Option<u64>,
23 next_override_generation: u64,
24}
25
26#[derive(Clone, Copy, Debug)]
27struct TimestampOverride {
28 timestamp: u64,
29 generation: u64,
30}
31
32#[derive(Clone, Copy, Debug)]
34pub(crate) struct PendingBlockTimestamp {
35 pub(crate) timestamp: u64,
36 exact_generation: Option<u64>,
37 prepared_offset: i128,
38 offset_reset_generation: u64,
39 next_offset: Option<i128>,
40}
41
42#[derive(Clone, Copy, Debug)]
44pub(crate) struct PendingTimeIncrease {
45 seconds: u64,
46 offset_reset_generation: u64,
47}
48
49impl TimeManager {
50 pub fn new(start_timestamp: u64) -> Self {
51 let time_manager = Self { state: Default::default() };
52 time_manager.reset(start_timestamp);
53 time_manager
54 }
55
56 pub fn reset(&self, start_timestamp: u64) {
59 self.reset_timestamp(start_timestamp, true, None);
60 }
61
62 pub fn set_time(&self, timestamp: u64) {
64 self.reset_timestamp(timestamp, false, None);
65 }
66
67 pub(crate) fn reset_with_offset(&self, start_timestamp: u64, offset: i128) {
69 self.reset_timestamp(start_timestamp, true, Some(offset));
70 }
71
72 fn reset_timestamp(&self, start_timestamp: u64, mark_new_head: bool, offset: Option<i128>) {
73 let current = duration_since_unix_epoch();
74 let mut state = self.state.write();
75 state.last_timestamp = start_timestamp;
76 if mark_new_head {
77 state.last_block_wall_time = current.as_millis().try_into().unwrap_or(u64::MAX);
78 }
79 state.offset =
80 offset.unwrap_or_else(|| (start_timestamp as i128) - current.as_secs() as i128);
81 state.offset_reset_generation = state.offset_reset_generation.wrapping_add(1);
82 state.next_exact_timestamp = None;
83 state.next_override_generation = state.next_override_generation.wrapping_add(1);
84 }
85
86 pub fn offset(&self) -> i128 {
87 self.state.read().offset
88 }
89
90 pub fn last_block_wall_time(&self) -> u64 {
92 self.state.read().last_block_wall_time
93 }
94
95 pub(crate) fn mark_block_created(&self) {
97 self.state.write().last_block_wall_time =
98 duration_since_unix_epoch().as_millis().try_into().unwrap_or(u64::MAX);
99 }
100
101 fn add_offset(&self, offset: i128) -> i128 {
103 let mut state = self.state.write();
104 let next = state.offset.saturating_add(offset);
105 trace!(target: "time", "adding timestamp offset={}, total={}", offset, next);
106 state.offset = next;
107 next
108 }
109
110 pub fn increase_time(&self, seconds: u64) -> i128 {
114 self.add_offset(seconds as i128)
115 }
116
117 pub(crate) fn apply_time_increase(&self, seconds: u64) -> PendingTimeIncrease {
119 let mut state = self.state.write();
120 state.offset = state.offset.saturating_add(seconds as i128);
121 PendingTimeIncrease { seconds, offset_reset_generation: state.offset_reset_generation }
122 }
123
124 pub(crate) fn revert_time_increase(&self, pending: PendingTimeIncrease) {
126 let mut state = self.state.write();
127 if state.offset_reset_generation == pending.offset_reset_generation {
128 state.offset = state.offset.saturating_sub(pending.seconds as i128);
129 }
130 }
131
132 pub fn set_next_block_timestamp(&self, timestamp: u64) -> Result<(), BlockchainError> {
135 trace!(target: "time", "override next timestamp {}", timestamp);
136 let mut state = self.state.write();
137 if timestamp < state.last_timestamp {
138 return Err(BlockchainError::TimestampError(format!(
139 "{timestamp} is lower than previous block's timestamp"
140 )));
141 }
142 state.next_override_generation = state.next_override_generation.wrapping_add(1);
143 state.next_exact_timestamp =
144 Some(TimestampOverride { timestamp, generation: state.next_override_generation });
145 Ok(())
146 }
147
148 pub fn set_block_timestamp_interval(&self, interval: u64) {
153 trace!(target: "time", "set interval {}", interval);
154 self.state.write().interval = Some(interval);
155 }
156
157 pub(crate) fn block_timestamp_interval(&self) -> Option<u64> {
159 self.state.read().interval
160 }
161
162 pub fn remove_block_timestamp_interval(&self) -> bool {
164 if self.state.write().interval.take().is_some() {
165 trace!(target: "time", "removed interval");
166 true
167 } else {
168 false
169 }
170 }
171
172 fn compute_next_timestamp(
174 state: &TimeState,
175 current: i128,
176 ) -> (u64, Option<u64>, Option<i128>) {
177 let exact_timestamp = state.next_exact_timestamp;
178 let last_timestamp = state.last_timestamp;
179
180 let (mut next_timestamp, update_offset) = if let Some(next) = exact_timestamp {
181 (next.timestamp, true)
182 } else if let Some(interval) = state.interval {
183 (last_timestamp.saturating_add(interval), false)
184 } else {
185 (current.saturating_add(state.offset) as u64, false)
186 };
187 if next_timestamp < last_timestamp {
189 next_timestamp = last_timestamp + 1;
190 }
191 let next_offset = update_offset.then_some((next_timestamp as i128) - current);
192 (next_timestamp, exact_timestamp.map(|exact| exact.generation), next_offset)
193 }
194
195 pub(crate) fn prepare_next_timestamp(&self) -> PendingBlockTimestamp {
197 let current = duration_since_unix_epoch().as_secs() as i128;
198 let state = self.state.read();
199 let (timestamp, exact_generation, next_offset) =
200 Self::compute_next_timestamp(&state, current);
201 PendingBlockTimestamp {
202 timestamp,
203 exact_generation,
204 prepared_offset: state.offset,
205 offset_reset_generation: state.offset_reset_generation,
206 next_offset,
207 }
208 }
209
210 pub(crate) fn commit_next_timestamp(&self, pending: PendingBlockTimestamp) {
212 let mut state = self.state.write();
213 if pending.exact_generation.is_some_and(|generation| {
214 state.next_exact_timestamp.is_some_and(|exact| exact.generation == generation)
215 }) {
216 state.next_exact_timestamp = None;
217 }
218 if let Some(next_offset) = pending.next_offset
219 && state.offset_reset_generation == pending.offset_reset_generation
220 {
221 let concurrent_offset = state.offset.saturating_sub(pending.prepared_offset);
222 state.offset = next_offset.saturating_add(concurrent_offset);
223 }
224 state.last_timestamp = pending.timestamp;
225 }
226
227 pub fn next_timestamp(&self) -> u64 {
229 let pending = self.prepare_next_timestamp();
230 self.commit_next_timestamp(pending);
231 pending.timestamp
232 }
233
234 pub fn current_call_timestamp(&self) -> u64 {
236 self.prepare_next_timestamp().timestamp
237 }
238}
239
240pub fn utc_from_secs(secs: u64) -> DateTime<Utc> {
242 DateTime::from_timestamp(secs as i64, 0).unwrap_or(DateTime::<Utc>::MAX_UTC)
243}
244
245pub fn duration_since_unix_epoch() -> Duration {
247 use std::time::SystemTime;
248 let now = SystemTime::now();
249 now.duration_since(SystemTime::UNIX_EPOCH)
250 .unwrap_or_else(|err| panic!("Current time {now:?} is invalid: {err:?}"))
251}
252
253#[cfg(test)]
254mod tests {
255 use super::*;
256
257 #[test]
258 fn candidate_consumes_only_its_timestamp_override() {
259 let time = TimeManager::new(1);
260 time.set_next_block_timestamp(100).unwrap();
261 let pending = time.prepare_next_timestamp();
262
263 time.set_next_block_timestamp(100).unwrap();
264 time.commit_next_timestamp(pending);
265
266 let state = time.state.read();
267 assert_eq!(state.last_timestamp, 100);
268 assert_eq!(state.next_exact_timestamp.unwrap().timestamp, 100);
269 }
270
271 #[test]
272 fn candidate_commit_preserves_concurrent_time_increase() {
273 let time = TimeManager::new(1);
274 time.set_next_block_timestamp(100).unwrap();
275 let pending = time.prepare_next_timestamp();
276
277 time.increase_time(10);
278 time.commit_next_timestamp(pending);
279
280 let state = time.state.read();
281 assert_eq!(state.last_timestamp, 100);
282 assert_eq!(state.offset, pending.next_offset.unwrap() + 10);
283 }
284
285 #[test]
286 fn candidate_commit_preserves_concurrent_time_reset() {
287 let time = TimeManager::new(1);
288 time.set_next_block_timestamp(100).unwrap();
289 let pending = time.prepare_next_timestamp();
290
291 time.reset(1_000);
292 let reset_offset = time.offset();
293 time.commit_next_timestamp(pending);
294
295 let state = time.state.read();
296 assert_eq!(state.last_timestamp, 100);
297 assert_eq!(state.offset, reset_offset);
298 }
299
300 #[test]
301 fn failed_temporary_increase_preserves_concurrent_time_reset() {
302 let time = TimeManager::new(1);
303 let pending = time.apply_time_increase(60);
304
305 time.reset(1_000);
306 let reset_offset = time.offset();
307 time.revert_time_increase(pending);
308
309 assert_eq!(time.offset(), reset_offset);
310 }
311}