1use massa_time::MassaTime;
6use std::convert::TryInto;
7
8use crate::{error::ModelsError, slot::Slot};
9
10pub fn slot_count_in_range(a: Slot, b: Slot, thread_count: u8) -> Result<u64, ModelsError> {
17 b.period
18 .checked_sub(a.period)
19 .ok_or(ModelsError::TimeOverflowError)?
20 .checked_mul(thread_count as u64)
21 .ok_or(ModelsError::TimeOverflowError)?
22 .checked_add(b.thread as u64)
23 .ok_or(ModelsError::TimeOverflowError)?
24 .checked_sub(a.thread as u64)
25 .ok_or(ModelsError::TimeOverflowError)
26}
27
28pub fn get_block_slot_timestamp(
35 thread_count: u8,
36 t0: MassaTime,
37 genesis_timestamp: MassaTime,
38 slot: Slot,
39) -> Result<MassaTime, ModelsError> {
40 let base: MassaTime = t0
41 .checked_div_u64(thread_count as u64)
42 .map_err(|_| ModelsError::TimeOverflowError)?
43 .checked_mul(slot.thread as u64)
44 .map_err(|_| ModelsError::TimeOverflowError)?;
45 let shift: MassaTime = t0
46 .checked_mul(slot.period)
47 .map_err(|_| ModelsError::TimeOverflowError)?;
48 genesis_timestamp
49 .checked_add(base)
50 .map_err(|_| ModelsError::TimeOverflowError)?
51 .checked_add(shift)
52 .map_err(|_| ModelsError::TimeOverflowError)
53}
54
55pub fn get_latest_block_slot_at_timestamp(
63 thread_count: u8,
64 t0: MassaTime,
65 genesis_timestamp: MassaTime,
66 timestamp: MassaTime,
67) -> Result<Option<Slot>, ModelsError> {
68 if let Ok(time_since_genesis) = timestamp.checked_sub(genesis_timestamp) {
69 let thread: u8 = time_since_genesis
70 .checked_rem_time(t0)?
71 .checked_div_time(t0.checked_div_u64(thread_count as u64)?)?
72 as u8;
73 return Ok(Some(Slot::new(
74 time_since_genesis.checked_div_time(t0)?,
75 thread,
76 )));
77 }
78 Ok(None)
79}
80
81pub fn get_current_latest_block_slot(
88 thread_count: u8,
89 t0: MassaTime,
90 genesis_timestamp: MassaTime,
91) -> Result<Option<Slot>, ModelsError> {
92 get_latest_block_slot_at_timestamp(thread_count, t0, genesis_timestamp, MassaTime::now())
93}
94
95pub fn time_range_to_slot_range(
107 thread_count: u8,
108 t0: MassaTime,
109 genesis_timestamp: MassaTime,
110 start_time: Option<MassaTime>,
111 end_time: Option<MassaTime>,
112) -> Result<(Option<Slot>, Option<Slot>), ModelsError> {
113 let start_slot = match start_time {
114 None => None,
115 Some(t) => {
116 let inter_slot = t0.checked_div_u64(thread_count as u64)?;
117 let slot_number: u64 = t
118 .saturating_sub(genesis_timestamp)
119 .checked_add(inter_slot)?
120 .saturating_sub(MassaTime::EPSILON)
121 .checked_div_time(inter_slot)?;
122 Some(Slot::new(
123 slot_number
124 .checked_div(thread_count as u64)
125 .ok_or(ModelsError::TimeOverflowError)?,
126 slot_number
127 .checked_rem(thread_count as u64)
128 .ok_or(ModelsError::TimeOverflowError)?
129 .try_into()
130 .map_err(|_| ModelsError::ThreadOverflowError)?,
131 ))
132 }
133 };
134
135 let end_slot = match end_time {
136 None => None,
137 Some(t) => {
138 let inter_slot = t0.checked_div_u64(thread_count as u64)?;
139 let slot_number: u64 = t
140 .saturating_sub(genesis_timestamp)
141 .checked_add(inter_slot)?
142 .saturating_sub(MassaTime::EPSILON)
143 .checked_div_time(inter_slot)?;
144 Some(Slot::new(
145 slot_number
146 .checked_div(thread_count as u64)
147 .ok_or(ModelsError::TimeOverflowError)?,
148 slot_number
149 .checked_rem(thread_count as u64)
150 .ok_or(ModelsError::TimeOverflowError)?
151 .try_into()
152 .map_err(|_| ModelsError::ThreadOverflowError)?,
153 ))
154 }
155 };
156
157 Ok((start_slot, end_slot))
158}
159
160pub fn get_closest_slot_to_timestamp(
162 thread_count: u8,
163 t0: MassaTime,
164 genesis_timestamp: MassaTime,
165 timestamp: MassaTime,
166) -> Slot {
167 let latest_past_slot =
169 match get_latest_block_slot_at_timestamp(thread_count, t0, genesis_timestamp, timestamp)
170 .unwrap()
171 {
172 None => return Slot::new(0, 0), Some(s) => s,
174 };
175
176 let t_latest = get_block_slot_timestamp(
178 thread_count,
179 t0,
180 genesis_timestamp,
181 latest_past_slot
182 ).expect("t_latest computation failed but it should be computable because that slot was obtained with get_latest_block_slot_at_timestamp");
183
184 let delta_t = timestamp.saturating_sub(t_latest);
186
187 if delta_t
189 .checked_mul(2)
190 .expect("delta_t should be multiplicate by 2")
191 <= t0
192 .checked_div_u64(thread_count as u64)
193 .expect("thread_count should not be 0")
194 {
195 latest_past_slot
196 } else {
197 latest_past_slot
198 .get_next_slot(thread_count)
199 .unwrap_or(latest_past_slot)
200 }
201}
202
203#[cfg(test)]
204mod tests {
205 use super::*;
206 use serial_test::serial;
207
208 #[test]
209 #[serial]
210 fn test_slot_count_in_range() {
211 assert_eq!(
212 slot_count_in_range(Slot::new(100, 3), Slot::new(100, 3), 32).unwrap(),
213 0
214 );
215 assert_eq!(
216 slot_count_in_range(Slot::new(100, 3), Slot::new(100, 5), 32).unwrap(),
217 2
218 );
219 assert_eq!(
220 slot_count_in_range(Slot::new(100, 4), Slot::new(103, 13), 32).unwrap(),
221 105
222 );
223 assert_eq!(
224 slot_count_in_range(Slot::new(100, 13), Slot::new(103, 4), 32).unwrap(),
225 87
226 );
227 }
228
229 #[test]
230 #[serial]
231 fn test_time_range_to_slot_range() {
232 let thread_count = 3u8;
233 let t0: MassaTime = MassaTime::from_millis(30);
234 let genesis_timestamp: MassaTime = MassaTime::from_millis(100);
235 let (out_start, out_end) = time_range_to_slot_range(
241 thread_count,
242 t0,
243 genesis_timestamp,
244 Some(MassaTime::from_millis(111)),
245 Some(MassaTime::from_millis(115)),
246 )
247 .unwrap();
248 assert_eq!(out_start, out_end);
249
250 let (out_start, out_end) = time_range_to_slot_range(
252 thread_count,
253 t0,
254 genesis_timestamp,
255 Some(MassaTime::from_millis(10)),
256 Some(MassaTime::from_millis(100)),
257 )
258 .unwrap();
259 assert_eq!(out_start, out_end);
260
261 let (out_start, out_end) = time_range_to_slot_range(
263 thread_count,
264 t0,
265 genesis_timestamp,
266 Some(MassaTime::from_millis(115)),
267 Some(MassaTime::from_millis(145)),
268 )
269 .unwrap();
270 assert_eq!(out_start, Some(Slot::new(0, 2)));
271 assert_eq!(out_end, Some(Slot::new(1, 2)));
272
273 let (out_start, out_end) = time_range_to_slot_range(
275 thread_count,
276 t0,
277 genesis_timestamp,
278 Some(MassaTime::from_millis(110)),
279 Some(MassaTime::from_millis(160)),
280 )
281 .unwrap();
282 assert_eq!(out_start, Some(Slot::new(0, 1)));
283 assert_eq!(out_end, Some(Slot::new(2, 0)));
284 }
285
286 #[test]
287 #[serial]
288 fn test_get_closest_slot_to_timestamp() {
289 let thread_count = 3u8;
290 let t0: MassaTime = MassaTime::from_millis(30);
291 let genesis_timestamp: MassaTime = MassaTime::from_millis(100);
292 let out_slot = get_closest_slot_to_timestamp(
296 thread_count,
297 t0,
298 genesis_timestamp,
299 MassaTime::from_millis(150),
300 );
301 assert_eq!(out_slot, Slot::new(1, 2));
302 }
303}