stratus/eth/primitives/
unix_time.rs1use std::ops::Deref;
2
3use alloy_primitives::U256;
4use chrono::DateTime;
5use chrono::Utc;
6use display_json::DebugAsJson;
7use fake::Dummy;
8use fake::Faker;
9
10use crate::ext::InfallibleExt;
11
12#[derive(DebugAsJson, Clone, Copy, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
13pub struct UnixTime(u64);
14
15impl UnixTime {
16 pub const ZERO: UnixTime = UnixTime(0u64);
17
18 #[cfg(not(feature = "dev"))]
19 pub fn now() -> Self {
20 Self(Utc::now().timestamp() as u64)
21 }
22
23 #[cfg(feature = "dev")]
24 pub fn now() -> Self {
25 offset::now()
26 }
27
28 #[cfg(feature = "dev")]
29 pub fn set_offset(current_block_timestamp: UnixTime, new_block_timestamp: UnixTime) -> anyhow::Result<()> {
30 offset::set(current_block_timestamp, new_block_timestamp)
31 }
32
33 #[cfg(feature = "dev")]
34 pub fn evm_set_next_block_timestamp_was_called() -> bool {
35 offset::evm_set_next_block_timestamp_was_called()
36 }
37}
38
39impl Deref for UnixTime {
40 type Target = u64;
41
42 fn deref(&self) -> &Self::Target {
43 &self.0
44 }
45}
46
47impl Dummy<Faker> for UnixTime {
48 fn dummy_with_rng<R: rand::Rng + ?Sized>(_: &Faker, rng: &mut R) -> Self {
49 rng.next_u64().into()
50 }
51}
52
53impl From<u64> for UnixTime {
58 fn from(value: u64) -> Self {
59 UnixTime(value)
60 }
61}
62
63impl From<UnixTime> for U256 {
68 fn from(value: UnixTime) -> Self {
69 Self::from_limbs([value.0, 0, 0, 0])
70 }
71}
72
73impl From<UnixTime> for DateTime<Utc> {
74 fn from(value: UnixTime) -> Self {
75 DateTime::from_timestamp(value.0 as i64, 0).expect_infallible()
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}