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