whisky_common/utils/
time.rs

1use crate::Network;
2use serde::{Deserialize, Serialize};
3
4/// Slot configuration for a Cardano network.
5/// Contains the parameters needed to convert between slots and POSIX time.
6#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
7#[serde(rename_all = "camelCase")]
8pub struct SlotConfig {
9    /// The POSIX time (in milliseconds) at the zero slot
10    pub zero_time: u64,
11    /// The slot number at zero time
12    pub zero_slot: u64,
13    /// The length of each slot in milliseconds
14    pub slot_length: u64,
15    /// The starting epoch number
16    pub start_epoch: u64,
17    /// The number of slots in each epoch
18    pub epoch_length: u64,
19}
20
21impl SlotConfig {
22    /// Create a new SlotConfig with custom parameters
23    pub fn new(
24        zero_time: u64,
25        zero_slot: u64,
26        slot_length: u64,
27        start_epoch: u64,
28        epoch_length: u64,
29    ) -> Self {
30        Self {
31            zero_time,
32            zero_slot,
33            slot_length,
34            start_epoch,
35            epoch_length,
36        }
37    }
38
39    /// Get the slot configuration for Cardano mainnet (starting at Shelley era)
40    pub fn mainnet() -> Self {
41        Self {
42            zero_time: 1596059091000,
43            zero_slot: 4492800,
44            slot_length: 1000,
45            start_epoch: 208,
46            epoch_length: 432000,
47        }
48    }
49
50    /// Get the slot configuration for Cardano preview testnet
51    pub fn preview() -> Self {
52        Self {
53            zero_time: 1666656000000,
54            zero_slot: 0,
55            slot_length: 1000,
56            start_epoch: 0,
57            epoch_length: 86400,
58        }
59    }
60
61    /// Get the slot configuration for Cardano preprod testnet
62    pub fn preprod() -> Self {
63        Self {
64            zero_time: 1654041600000 + 1728000000,
65            zero_slot: 86400,
66            slot_length: 1000,
67            start_epoch: 4,
68            epoch_length: 432000,
69        }
70    }
71}
72
73impl Default for SlotConfig {
74    fn default() -> Self {
75        Self::mainnet()
76    }
77}
78
79/// Get the slot configuration for a specific network.
80///
81/// # Arguments
82/// * `network` - The Cardano network
83///
84/// # Returns
85/// The slot configuration for the network, or None for Custom networks
86pub fn get_slot_config(network: &Network) -> Option<SlotConfig> {
87    match network {
88        Network::Mainnet => Some(SlotConfig::mainnet()),
89        Network::Preview => Some(SlotConfig::preview()),
90        Network::Preprod => Some(SlotConfig::preprod()),
91        Network::Custom(_) => None,
92    }
93}
94
95/// Convert a slot number to the beginning POSIX time (in milliseconds).
96///
97/// # Arguments
98/// * `slot` - The slot number
99/// * `slot_config` - The slot configuration for the network
100///
101/// # Returns
102/// The POSIX time in milliseconds at the beginning of the slot
103pub fn slot_to_begin_unix_time(slot: u64, slot_config: &SlotConfig) -> u64 {
104    let ms_after_begin = (slot - slot_config.zero_slot) * slot_config.slot_length;
105    slot_config.zero_time + ms_after_begin
106}
107
108/// Convert a POSIX time to the enclosing slot number.
109///
110/// # Arguments
111/// * `unix_time` - The POSIX time in milliseconds
112/// * `slot_config` - The slot configuration for the network
113///
114/// # Returns
115/// The slot number that contains the given time
116pub fn unix_time_to_enclosing_slot(unix_time: u64, slot_config: &SlotConfig) -> u64 {
117    let time_passed = unix_time - slot_config.zero_time;
118    let slots_passed = time_passed / slot_config.slot_length;
119    slots_passed + slot_config.zero_slot
120}
121
122/// Resolve the slot number for a network at a given time.
123///
124/// # Arguments
125/// * `network` - The Cardano network
126/// * `milliseconds` - Optional POSIX time in milliseconds (defaults to current time)
127///
128/// # Returns
129/// The slot number as a string, or None if the network is Custom
130pub fn resolve_slot_no(network: &Network, milliseconds: Option<u64>) -> Option<String> {
131    let slot_config = get_slot_config(network)?;
132    let time = milliseconds.unwrap_or_else(|| {
133        std::time::SystemTime::now()
134            .duration_since(std::time::UNIX_EPOCH)
135            .expect("Time went backwards")
136            .as_millis() as u64
137    });
138    Some(unix_time_to_enclosing_slot(time, &slot_config).to_string())
139}
140
141/// Resolve the epoch number for a network at a given time.
142///
143/// # Arguments
144/// * `network` - The Cardano network
145/// * `milliseconds` - Optional POSIX time in milliseconds (defaults to current time)
146///
147/// # Returns
148/// The epoch number, or None if the network is Custom
149pub fn resolve_epoch_no(network: &Network, milliseconds: Option<u64>) -> Option<u64> {
150    let config = get_slot_config(network)?;
151    let time = milliseconds.unwrap_or_else(|| {
152        std::time::SystemTime::now()
153            .duration_since(std::time::UNIX_EPOCH)
154            .expect("Time went backwards")
155            .as_millis() as u64
156    });
157
158    let epoch = (time - config.zero_time) / 1000 / config.epoch_length + config.start_epoch;
159    Some(epoch)
160}