Skip to content

Commit 3d4ff45

Browse files
authored
Merge branch 'main' into feat/rbac-registration
2 parents f10c230 + dbe3d4c commit 3d4ff45

File tree

2 files changed

+183
-4
lines changed

2 files changed

+183
-4
lines changed

rust/cardano-chain-follower/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "cardano-chain-follower"
3-
version = "0.0.4"
3+
version = "0.0.5"
44
edition.workspace = true
55
authors.workspace = true
66
homepage.workspace = true

rust/cardano-chain-follower/src/network.rs

+182-3
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,38 @@ impl Network {
170170
///
171171
/// The Slot does not have to be a valid slot present in the blockchain.
172172
#[must_use]
173-
pub fn time_to_slot(&self, _time: DateTime<Utc>) -> Option<u64> {
174-
// TODO: Implement this, for now just return None.
175-
None
173+
pub fn time_to_slot(&self, time: DateTime<Utc>) -> Option<u64> {
174+
let genesis = self.genesis_values();
175+
176+
let byron_start_time = i64::try_from(genesis.byron_known_time)
177+
.map(|time| DateTime::<Utc>::from_timestamp(time, 0))
178+
.ok()??;
179+
let shelley_start_time = i64::try_from(genesis.shelley_known_time)
180+
.map(|time| DateTime::<Utc>::from_timestamp(time, 0))
181+
.ok()??;
182+
183+
// determine if the given time is in Byron or Shelley era.
184+
if time < byron_start_time {
185+
return None;
186+
}
187+
188+
if time < shelley_start_time {
189+
// Byron era
190+
let time_diff = time - byron_start_time;
191+
let elapsed_slots = time_diff.num_seconds() / i64::from(genesis.byron_slot_length);
192+
193+
u64::try_from(elapsed_slots)
194+
.map(|elapsed_slots| Some(genesis.byron_known_slot + elapsed_slots))
195+
.ok()?
196+
} else {
197+
// Shelley era
198+
let time_diff = time - shelley_start_time;
199+
let elapsed_slots = time_diff.num_seconds() / i64::from(genesis.shelley_slot_length);
200+
201+
u64::try_from(elapsed_slots)
202+
.map(|elapsed_slots| Some(genesis.shelley_known_slot + elapsed_slots))
203+
.ok()?
204+
}
176205
}
177206
}
178207

@@ -191,6 +220,7 @@ mod tests {
191220
use std::str::FromStr;
192221

193222
use anyhow::Ok;
223+
use chrono::{TimeZone, Utc};
194224

195225
use super::*;
196226

@@ -214,4 +244,153 @@ mod tests {
214244

215245
Ok(())
216246
}
247+
248+
#[test]
249+
fn test_time_to_slot_before_blockchain() {
250+
let network = Network::Mainnet;
251+
let genesis = network.genesis_values();
252+
253+
let before_blockchain = Utc
254+
.timestamp_opt(i64::try_from(genesis.byron_known_time).unwrap() - 1, 0)
255+
.unwrap();
256+
257+
assert_eq!(network.time_to_slot(before_blockchain), None);
258+
}
259+
260+
#[test]
261+
fn test_time_to_slot_byron_era() {
262+
let network = Network::Mainnet;
263+
let genesis = network.genesis_values();
264+
265+
let byron_start_time = Utc
266+
.timestamp_opt(i64::try_from(genesis.byron_known_time).unwrap(), 0)
267+
.unwrap();
268+
let byron_slot_length = i64::from(genesis.byron_slot_length);
269+
270+
// a time in the middle of the Byron era.
271+
let time = byron_start_time + chrono::Duration::seconds(byron_slot_length * 100);
272+
let expected_slot = genesis.byron_known_slot + 100;
273+
274+
assert_eq!(network.time_to_slot(time), Some(expected_slot));
275+
}
276+
277+
#[test]
278+
fn test_time_to_slot_transition_to_shelley() {
279+
let network = Network::Mainnet;
280+
let genesis = network.genesis_values();
281+
282+
let shelley_start_time = Utc
283+
.timestamp_opt(i64::try_from(genesis.shelley_known_time).unwrap(), 0)
284+
.unwrap();
285+
let byron_slot_length = i64::from(genesis.byron_slot_length);
286+
287+
// a time just before Shelley era starts.
288+
let time = shelley_start_time - chrono::Duration::seconds(1);
289+
let elapsed_slots = (time
290+
- Utc
291+
.timestamp_opt(i64::try_from(genesis.byron_known_time).unwrap(), 0)
292+
.unwrap())
293+
.num_seconds()
294+
/ byron_slot_length;
295+
let expected_slot = genesis.byron_known_slot + u64::try_from(elapsed_slots).unwrap();
296+
297+
assert_eq!(network.time_to_slot(time), Some(expected_slot));
298+
}
299+
300+
#[test]
301+
fn test_time_to_slot_shelley_era() {
302+
let network = Network::Mainnet;
303+
let genesis = network.genesis_values();
304+
305+
let shelley_start_time = Utc
306+
.timestamp_opt(i64::try_from(genesis.shelley_known_time).unwrap(), 0)
307+
.unwrap();
308+
let shelley_slot_length = i64::from(genesis.shelley_slot_length);
309+
310+
// a time in the middle of the Shelley era.
311+
let time = shelley_start_time + chrono::Duration::seconds(shelley_slot_length * 200);
312+
let expected_slot = genesis.shelley_known_slot + 200;
313+
314+
assert_eq!(network.time_to_slot(time), Some(expected_slot));
315+
}
316+
317+
#[test]
318+
fn test_slot_to_time_to_slot_consistency() {
319+
let network = Network::Mainnet;
320+
321+
// a few arbitrary slots in different ranges.
322+
let slots_to_test = vec![0, 10_000, 1_000_000, 50_000_000];
323+
324+
for slot in slots_to_test {
325+
let time = network.slot_to_time(slot);
326+
let calculated_slot = network.time_to_slot(time);
327+
328+
assert_eq!(calculated_slot, Some(slot), "Failed for slot: {slot}");
329+
}
330+
}
331+
332+
#[test]
333+
#[allow(clippy::panic)]
334+
fn test_time_to_slot_to_time_consistency() {
335+
let network = Network::Mainnet;
336+
let genesis = network.genesis_values();
337+
338+
// Byron, Shelley, and Conway.
339+
let times_to_test = vec![
340+
Utc.timestamp_opt(i64::try_from(genesis.byron_known_time).unwrap() + 100, 0)
341+
.unwrap(),
342+
Utc.timestamp_opt(
343+
i64::try_from(genesis.shelley_known_time).unwrap() + 1_000,
344+
0,
345+
)
346+
.unwrap(),
347+
Utc.timestamp_opt(
348+
i64::try_from(genesis.shelley_known_time).unwrap() + 10_000_000,
349+
0,
350+
)
351+
.unwrap(),
352+
];
353+
354+
for time in times_to_test {
355+
if let Some(slot) = network.time_to_slot(time) {
356+
let calculated_time = network.slot_to_time(slot);
357+
358+
assert_eq!(
359+
calculated_time.timestamp(),
360+
time.timestamp(),
361+
"Failed for time: {time}"
362+
);
363+
} else {
364+
panic!("time_to_slot returned None for a valid time: {time}");
365+
}
366+
}
367+
}
368+
369+
#[test]
370+
fn test_conway_era_time_to_slot_and_back() {
371+
let network = Network::Mainnet;
372+
let genesis = network.genesis_values();
373+
374+
// a very late time, far in the Conway era.
375+
let conway_time = Utc
376+
.timestamp_opt(
377+
i64::try_from(genesis.shelley_known_time).unwrap() + 20_000_000,
378+
0,
379+
)
380+
.unwrap();
381+
382+
let slot = network.time_to_slot(conway_time);
383+
assert!(
384+
slot.is_some(),
385+
"Failed to calculate slot for Conway era time"
386+
);
387+
388+
let calculated_time = network.slot_to_time(slot.unwrap());
389+
390+
assert_eq!(
391+
calculated_time.timestamp(),
392+
conway_time.timestamp(),
393+
"Inconsistency for Conway era time"
394+
);
395+
}
217396
}

0 commit comments

Comments
 (0)