1use alloy_evm::env::BlockEnvironment;
2use alloy_primitives::{Address, B256, U256, uint};
3use revm::{
4 context::{Block, BlockEnv},
5 context_interface::block::BlobExcessGasAndPrice,
6};
7
8#[derive(Debug, Clone, Default, PartialEq, derive_more::Deref, derive_more::DerefMut)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub struct TempoBlockEnv {
12 #[deref]
14 #[deref_mut]
15 pub inner: BlockEnv,
16
17 pub timestamp_millis_part: u64,
19}
20
21impl TempoBlockEnv {
22 pub fn timestamp_millis(&self) -> U256 {
24 self.inner
25 .timestamp
26 .saturating_mul(uint!(1000_U256))
27 .saturating_add(U256::from(self.timestamp_millis_part))
28 }
29}
30
31impl Block for TempoBlockEnv {
32 #[inline]
33 fn number(&self) -> U256 {
34 self.inner.number()
35 }
36
37 #[inline]
38 fn beneficiary(&self) -> Address {
39 self.inner.beneficiary()
40 }
41
42 #[inline]
43 fn timestamp(&self) -> U256 {
44 self.inner.timestamp()
45 }
46
47 #[inline]
48 fn gas_limit(&self) -> u64 {
49 self.inner.gas_limit()
50 }
51
52 #[inline]
53 fn basefee(&self) -> u64 {
54 self.inner.basefee()
55 }
56
57 #[inline]
58 fn difficulty(&self) -> U256 {
59 self.inner.difficulty()
60 }
61
62 #[inline]
63 fn prevrandao(&self) -> Option<B256> {
64 self.inner.prevrandao()
65 }
66
67 #[inline]
68 fn blob_excess_gas_and_price(&self) -> Option<BlobExcessGasAndPrice> {
69 self.inner.blob_excess_gas_and_price()
70 }
71}
72
73impl BlockEnvironment for TempoBlockEnv {
74 fn inner_mut(&mut self) -> &mut BlockEnv {
75 &mut self.inner
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82 use proptest::prelude::*;
83
84 fn make_block_env(timestamp: U256, millis_part: u64) -> TempoBlockEnv {
86 TempoBlockEnv {
87 inner: BlockEnv {
88 timestamp,
89 ..Default::default()
90 },
91 timestamp_millis_part: millis_part,
92 }
93 }
94
95 fn arb_u256() -> impl Strategy<Value = U256> {
97 any::<[u64; 4]>().prop_map(U256::from_limbs)
98 }
99
100 proptest! {
101 #![proptest_config(ProptestConfig::with_cases(500))]
102
103 #[test]
105 fn proptest_timestamp_millis_no_panic(
106 timestamp in arb_u256(),
107 millis_part in any::<u64>(),
108 ) {
109 let block = make_block_env(timestamp, millis_part);
110 let _ = block.timestamp_millis();
111 }
112
113 #[test]
115 fn proptest_timestamp_millis_ge_scaled_timestamp(
116 timestamp in arb_u256(),
117 millis_part in any::<u64>(),
118 ) {
119 let block = make_block_env(timestamp, millis_part);
120 let result = block.timestamp_millis();
121 let scaled = timestamp.saturating_mul(uint!(1000_U256));
122
123 prop_assert!(result >= scaled,
124 "timestamp_millis ({}) should be >= timestamp * 1000 ({})",
125 result, scaled);
126 }
127
128 #[test]
130 fn proptest_timestamp_millis_exact_for_small_values(
131 timestamp in 0u64..u64::MAX / 1000,
132 millis_part in 0u64..1000,
133 ) {
134 let block = make_block_env(U256::from(timestamp), millis_part);
135 let expected = U256::from(timestamp) * uint!(1000_U256) + U256::from(millis_part);
136 prop_assert_eq!(block.timestamp_millis(), expected);
137 }
138
139 #[test]
141 fn proptest_timestamp_millis_monotonicity(
142 ts1 in 0u64..u64::MAX / 1000,
143 ts2 in 0u64..u64::MAX / 1000,
144 mp1 in 0u64..1000,
145 mp2 in 0u64..1000,
146 ) {
147 let block1 = make_block_env(U256::from(ts1), mp1);
148 let block2 = make_block_env(U256::from(ts2), mp2);
149
150 let result1 = block1.timestamp_millis();
151 let result2 = block2.timestamp_millis();
152
153 if ts1 < ts2 || (ts1 == ts2 && mp1 <= mp2) {
154 prop_assert!(result1 <= result2,
155 "Monotonicity violated: ts1={}, mp1={}, result1={}, ts2={}, mp2={}, result2={}",
156 ts1, mp1, result1, ts2, mp2, result2);
157 }
158 }
159
160 #[test]
162 fn proptest_timestamp_millis_sub_second(
163 timestamp in 0u64..u64::MAX / 1000,
164 millis_part in 0u64..1000,
165 ) {
166 let block = make_block_env(U256::from(timestamp), millis_part);
167 let result = block.timestamp_millis();
168 let next_second = U256::from(timestamp + 1) * uint!(1000_U256);
169
170 prop_assert!(result < next_second,
171 "result ({}) should be < next_second ({})",
172 result, next_second);
173 }
174
175 #[test]
181 fn proptest_timestamp_millis_large_millis_part(
182 timestamp in 0u64..u64::MAX / 1000,
183 millis_part in 1000u64..u64::MAX,
184 ) {
185 let block = make_block_env(U256::from(timestamp), millis_part);
186 let result = block.timestamp_millis();
187
188 let scaled = U256::from(timestamp).saturating_mul(uint!(1000_U256));
190 let expected = scaled.saturating_add(U256::from(millis_part));
191
192 prop_assert_eq!(result, expected,
193 "timestamp={}, millis_part={}, result={}, expected={}",
194 timestamp, millis_part, result, expected);
195 }
196
197 #[test]
203 fn proptest_timestamp_millis_large_millis_breaks_monotonicity(
204 ts in 0u64..u64::MAX / 2000,
205 large_mp in 1000u64..u64::MAX,
206 ) {
207 let block1 = make_block_env(U256::from(ts), large_mp);
209 let block2 = make_block_env(U256::from(ts + 1), 0);
211
212 let result1 = block1.timestamp_millis();
213 let result2 = block2.timestamp_millis();
214
215 let expected1 = U256::from(ts).saturating_mul(uint!(1000_U256))
219 .saturating_add(U256::from(large_mp));
220 let expected2 = U256::from(ts + 1).saturating_mul(uint!(1000_U256));
221
222 prop_assert_eq!(result1, expected1);
223 prop_assert_eq!(result2, expected2);
224 }
225 }
226}