stratus/eth/primitives/
unix_time.rs1use alloy_primitives::U256;
2use chrono::DateTime;
3use chrono::Utc;
4use derive_more::Deref;
5use display_json::DebugAsJson;
6#[cfg(test)]
7use fake::Dummy;
8#[cfg(test)]
9use fake::Faker;
10
11use crate::ext::InfallibleExt;
12
13#[derive(DebugAsJson, Clone, Copy, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize, Deref, Hash)]
14pub struct UnixTime(#[deref] u64);
15
16impl UnixTime {
17 pub const ZERO: UnixTime = UnixTime(0u64);
18
19 #[cfg(not(feature = "dev"))]
20 pub fn now() -> Self {
21 Self(Utc::now().timestamp() as u64)
22 }
23
24 #[cfg(feature = "dev")]
25 pub fn now() -> Self {
26 offset::now()
27 }
28
29 #[cfg(feature = "dev")]
30 pub fn set_offset(current_block_timestamp: UnixTime, new_block_timestamp: UnixTime) -> anyhow::Result<()> {
31 offset::set(current_block_timestamp, new_block_timestamp)
32 }
33
34 #[cfg(feature = "dev")]
35 pub fn evm_set_next_block_timestamp_was_called() -> bool {
36 offset::evm_set_next_block_timestamp_was_called()
37 }
38}
39
40#[cfg(test)]
41impl Dummy<Faker> for UnixTime {
42 fn dummy_with_rng<R: rand::Rng + ?Sized>(_: &Faker, rng: &mut R) -> Self {
43 rng.next_u64().into()
44 }
45}
46
47impl From<u64> for UnixTime {
52 fn from(value: u64) -> Self {
53 UnixTime(value)
54 }
55}
56
57impl From<UnixTime> for U256 {
62 fn from(value: UnixTime) -> Self {
63 Self::from_limbs([value.0, 0, 0, 0])
64 }
65}
66
67impl From<UnixTime> for DateTime<Utc> {
68 fn from(value: UnixTime) -> Self {
69 DateTime::from_timestamp(value.0 as i64, 0).expect_infallible()
70 }
71}
72
73impl std::fmt::Display for UnixTime {
74 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75 write!(f, "{}", self.0)
76 }
77}
78
79#[cfg(feature = "dev")]
80mod offset {
81 use std::sync::atomic::AtomicBool;
82 use std::sync::atomic::AtomicI64;
83 use std::sync::atomic::AtomicU64;
84 use std::sync::atomic::Ordering::Acquire;
85 use std::sync::atomic::Ordering::SeqCst;
86
87 use super::UnixTime;
88 use super::Utc;
89
90 pub static NEW_TIMESTAMP_DIFF: AtomicI64 = AtomicI64::new(0);
93
94 pub static NEW_TIMESTAMP: AtomicU64 = AtomicU64::new(0);
97
98 pub static EVM_SET_NEXT_BLOCK_TIMESTAMP_WAS_CALLED: AtomicBool = AtomicBool::new(false);
100
101 pub static LAST_BLOCK_TIMESTAMP: AtomicI64 = AtomicI64::new(0);
104
105 pub fn set(current_block_timestamp: UnixTime, new_block_timestamp: UnixTime) -> anyhow::Result<()> {
124 use crate::log_and_err;
125
126 if *new_block_timestamp == 0 {
127 LAST_BLOCK_TIMESTAMP.store(*current_block_timestamp as i64, SeqCst);
128 }
129
130 if *new_block_timestamp != 0 && *new_block_timestamp < *current_block_timestamp {
131 return log_and_err!("timestamp can't be before the latest block");
132 }
133
134 let current_time = Utc::now().timestamp();
135 let diff: i64 = if *new_block_timestamp == 0 {
136 0
137 } else {
138 (*new_block_timestamp as i64).saturating_sub(current_time)
139 };
140
141 NEW_TIMESTAMP.store(*new_block_timestamp, SeqCst);
142 NEW_TIMESTAMP_DIFF.store(diff, SeqCst);
143 EVM_SET_NEXT_BLOCK_TIMESTAMP_WAS_CALLED.store(true, SeqCst);
144 Ok(())
145 }
146
147 pub fn now() -> UnixTime {
167 let new_timestamp = NEW_TIMESTAMP.load(Acquire);
168 let new_timestamp_diff = NEW_TIMESTAMP_DIFF.load(Acquire);
169 let was_evm_timestamp_set = EVM_SET_NEXT_BLOCK_TIMESTAMP_WAS_CALLED.load(Acquire);
170 let last_block_timestamp = LAST_BLOCK_TIMESTAMP.load(Acquire);
171 let current_time = Utc::now().timestamp();
172
173 let result = if !was_evm_timestamp_set {
174 let last_timestamp = if new_timestamp != 0 {
175 new_timestamp as i64
176 } else {
177 std::cmp::max(current_time + new_timestamp_diff, last_block_timestamp)
178 };
179
180 UnixTime((last_timestamp + 1) as u64)
181 } else {
182 EVM_SET_NEXT_BLOCK_TIMESTAMP_WAS_CALLED.store(false, SeqCst);
183 NEW_TIMESTAMP.store(0, SeqCst);
184
185 if new_timestamp == 0 {
186 UnixTime((last_block_timestamp + 1) as u64)
187 } else {
188 UnixTime(new_timestamp)
189 }
190 };
191
192 LAST_BLOCK_TIMESTAMP.store(*result as i64, SeqCst);
193 result
194 }
195
196 pub fn evm_set_next_block_timestamp_was_called() -> bool {
198 EVM_SET_NEXT_BLOCK_TIMESTAMP_WAS_CALLED.load(Acquire)
199 }
200}