time.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2023 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use std::{fmt, time::UNIX_EPOCH};
  19. #[cfg(feature = "async-serial")]
  20. use darkfi_serial::async_trait;
  21. use darkfi_serial::{SerialDecodable, SerialEncodable};
  22. use crate::Result;
  23. const SECS_IN_DAY: u64 = 86400;
  24. const MIN_IN_HOUR: u64 = 60;
  25. const SECS_IN_HOUR: u64 = 3600;
  26. /// Helper structure providing time related calculations.
  27. /// This struct is optimized for performance and does not check
  28. /// its arithmetic: division-by-zero is possible for certain values.
  29. /// [`TimeKeeperSafe`] should be used if safety is more important than
  30. /// performance.
  31. #[derive(Clone)]
  32. pub struct TimeKeeper {
  33. /// Genesis block creation timestamp
  34. pub genesis_ts: Timestamp,
  35. /// Currently configured epoch duration
  36. pub epoch_length: u64,
  37. /// Currently configured slot duration
  38. pub slot_time: u64,
  39. /// Slot number runtime can access to verify against
  40. pub verifying_slot: u64,
  41. }
  42. impl TimeKeeper {
  43. pub fn new(
  44. genesis_ts: Timestamp,
  45. epoch_length: u64,
  46. slot_time: u64,
  47. verifying_slot: u64,
  48. ) -> Self {
  49. Self { genesis_ts, epoch_length, slot_time, verifying_slot }
  50. }
  51. /// Generate a TimeKeeper for current slot
  52. pub fn current(&self) -> Self {
  53. Self {
  54. genesis_ts: self.genesis_ts,
  55. epoch_length: self.epoch_length,
  56. slot_time: self.slot_time,
  57. verifying_slot: self.current_slot(),
  58. }
  59. }
  60. /// Calculates current epoch.
  61. pub fn current_epoch(&self) -> u64 {
  62. self.slot_epoch(self.current_slot())
  63. }
  64. /// Calculates the epoch of the provided slot.
  65. /// Only slot 0 exists in epoch 0, everything
  66. /// else is incremented by one. This practically
  67. /// means that epoch 0 has 1 slot(the genesis slot),
  68. /// epoch 1 has one less slot(the genesis slot) and
  69. /// rest epoch have the normal amount of slots.
  70. pub fn slot_epoch(&self, slot: u64) -> u64 {
  71. (slot / self.epoch_length) + 1
  72. }
  73. /// Calculates current slot, based on elapsed time from the genesis block.
  74. pub fn current_slot(&self) -> u64 {
  75. self.genesis_ts.elapsed() / self.slot_time
  76. }
  77. /// Calculates the relative number of the provided slot.
  78. pub fn relative_slot(&self, slot: u64) -> u64 {
  79. slot % self.epoch_length
  80. }
  81. /// Calculates the epoch of the verifying slot.
  82. pub fn verifying_slot_epoch(&self) -> u64 {
  83. self.slot_epoch(self.verifying_slot)
  84. }
  85. /// Calculates seconds until next Nth slot starting time.
  86. pub fn next_n_slot_start(&self, n: u64) -> u64 {
  87. assert!(n > 0);
  88. let next_slot_start = self.genesis_ts.0 + (self.current_slot() + n) * self.slot_time;
  89. next_slot_start - Timestamp::current_time().0
  90. }
  91. /// Calculate slots until next Nth epoch.
  92. /// Epoch duration is configured using the EPOCH_LENGTH value.
  93. pub fn slots_to_next_n_epoch(&self, n: u64) -> u64 {
  94. assert!(n > 0);
  95. let slots_till_next_epoch = self.epoch_length - self.relative_slot(self.current_slot());
  96. ((n - 1) * self.epoch_length) + slots_till_next_epoch
  97. }
  98. /// Calculates seconds until next Nth epoch starting time.
  99. pub fn next_n_epoch_start(&self, n: u64) -> u64 {
  100. self.next_n_slot_start(self.slots_to_next_n_epoch(n))
  101. }
  102. /// Calculates current blockchain timestamp.
  103. /// Blockchain timestamp is the time elapsed since
  104. /// Genesis timestamp, based on slot time ticking,
  105. /// therefore representing the starting timestamp of
  106. /// current slot.
  107. pub fn blockchain_timestamp(&self) -> u64 {
  108. self.genesis_ts.0 + self.current_slot() * self.slot_time
  109. }
  110. /// Calculates current system timestamp.
  111. pub fn system_timestamp(&self) -> Result<u64> {
  112. Ok(UNIX_EPOCH.elapsed()?.as_secs())
  113. }
  114. }
  115. /// Wrapper struct that allows only coherent and safe values for a [`TimeKeeper`].
  116. #[derive(Clone)]
  117. pub struct TimeKeeperSafe {
  118. timekeeper: TimeKeeper,
  119. }
  120. impl TimeKeeperSafe {
  121. pub fn new(
  122. genesis_ts: Timestamp,
  123. epoch_length: u64,
  124. slot_time: u64,
  125. verifying_slot: u64,
  126. ) -> Self {
  127. // TimeKeeper uses epoch_length and slot_time as divisors so they should
  128. // never be zero in this struct.
  129. if epoch_length == 0 {
  130. panic!("Epoch length cannot be zero");
  131. }
  132. if slot_time == 0 {
  133. panic!("Slot time cannot be zero");
  134. }
  135. Self { timekeeper: TimeKeeper { genesis_ts, epoch_length, slot_time, verifying_slot } }
  136. }
  137. /// Generate a TimeKeeperSafe for current slot
  138. pub fn current(&self) -> TimeKeeperSafe {
  139. TimeKeeperSafe::new(
  140. self.timekeeper.genesis_ts,
  141. self.timekeeper.epoch_length,
  142. self.timekeeper.slot_time,
  143. self.timekeeper.verifying_slot,
  144. )
  145. }
  146. /// Calculates current epoch.
  147. pub fn current_epoch(&self) -> u64 {
  148. self.timekeeper.current_epoch()
  149. }
  150. /// Calculates the epoch of the provided slot.
  151. /// Only slot 0 exists in epoch 0, everything
  152. /// else is incremented by one. This practically
  153. /// means that epoch 0 has 1 slot(the genesis slot),
  154. /// epoch 1 has one less slot(the genesis slot) and
  155. /// rest epoch have the normal amount of slots.
  156. pub fn slot_epoch(&self, slot: u64) -> u64 {
  157. self.timekeeper.slot_epoch(slot)
  158. }
  159. /// Calculates current slot, based on elapsed time from the genesis block.
  160. pub fn current_slot(&self) -> u64 {
  161. self.timekeeper.current_slot()
  162. }
  163. /// Calculates the relative number of the provided slot.
  164. pub fn relative_slot(&self, slot: u64) -> u64 {
  165. self.timekeeper.relative_slot(slot)
  166. }
  167. /// Calculates the epoch of the verifying slot.
  168. pub fn verifying_slot_epoch(&self) -> u64 {
  169. self.timekeeper.verifying_slot_epoch()
  170. }
  171. /// Calculates seconds until next Nth slot starting time.
  172. pub fn next_n_slot_start(&self, n: u64) -> u64 {
  173. self.timekeeper.next_n_slot_start(n)
  174. }
  175. /// Calculate slots until next Nth epoch.
  176. /// Epoch duration is configured using the EPOCH_LENGTH value.
  177. pub fn slots_to_next_n_epoch(&self, n: u64) -> u64 {
  178. self.timekeeper.slots_to_next_n_epoch(n)
  179. }
  180. /// Calculates seconds until next Nth epoch starting time.
  181. pub fn next_n_epoch_start(&self, n: u64) -> u64 {
  182. self.timekeeper.next_n_epoch_start(n)
  183. }
  184. /// Calculates current blockchain timestamp.
  185. /// Blockchain timestamp is the time elapsed since
  186. /// Genesis timestamp, based on slot time ticking,
  187. /// therefore representing the starting timestamp of
  188. /// current slot.
  189. pub fn blockchain_timestamp(&self) -> u64 {
  190. self.timekeeper.blockchain_timestamp()
  191. }
  192. /// Calculates current system timestamp.
  193. pub fn system_timestamp(&self) -> Result<u64> {
  194. self.timekeeper.system_timestamp()
  195. }
  196. }
  197. /// Wrapper struct to represent system timestamps.
  198. #[derive(Hash, Clone, Copy, Debug, SerialEncodable, SerialDecodable, PartialEq, PartialOrd, Eq)]
  199. pub struct Timestamp(pub u64);
  200. impl Timestamp {
  201. /// Generate a `Timestamp` of the current time.
  202. pub fn current_time() -> Self {
  203. Self(UNIX_EPOCH.elapsed().unwrap().as_secs())
  204. }
  205. /// Calculates elapsed time of a `Timestamp`.
  206. pub fn elapsed(&self) -> u64 {
  207. UNIX_EPOCH.elapsed().unwrap().as_secs() - self.0
  208. }
  209. /// Increment a 'Timestamp'.
  210. pub fn add(&mut self, inc: u64) {
  211. self.0 += inc;
  212. }
  213. }
  214. impl std::fmt::Display for Timestamp {
  215. fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
  216. let date = timestamp_to_date(self.0, DateFormat::DateTime);
  217. write!(f, "{}", date)
  218. }
  219. }
  220. #[derive(Clone, Copy, Debug, SerialEncodable, SerialDecodable, PartialEq, PartialOrd, Eq)]
  221. pub struct NanoTimestamp(pub u128);
  222. impl NanoTimestamp {
  223. pub fn current_time() -> Self {
  224. Self(UNIX_EPOCH.elapsed().unwrap().as_nanos())
  225. }
  226. }
  227. impl std::fmt::Display for NanoTimestamp {
  228. fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
  229. let date = timestamp_to_date(self.0.try_into().unwrap(), DateFormat::Nanos);
  230. write!(f, "{}", date)
  231. }
  232. }
  233. pub enum DateFormat {
  234. Default,
  235. Date,
  236. DateTime,
  237. Nanos,
  238. }
  239. #[derive(Clone, Debug, Default)]
  240. pub struct DateTime {
  241. pub nanos: u32,
  242. pub sec: u32,
  243. pub min: u32,
  244. pub hour: u32,
  245. pub day: u32,
  246. pub month: u32,
  247. pub year: u32,
  248. }
  249. impl DateTime {
  250. pub fn new() -> Self {
  251. Self { nanos: 0, sec: 0, min: 0, hour: 0, day: 0, month: 0, year: 0 }
  252. }
  253. pub fn date(&self) -> Date {
  254. Date { day: self.day, month: self.month, year: self.year }
  255. }
  256. pub fn from_timestamp(secs: u64, nsecs: u32) -> Self {
  257. let leap_year = |year| -> bool { year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) };
  258. static MONTHS: [[u64; 12]; 2] = [
  259. [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
  260. [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
  261. ];
  262. let mut date_time = DateTime::new();
  263. let mut year = 1970;
  264. let time = secs % SECS_IN_DAY;
  265. let mut day_number = secs / SECS_IN_DAY;
  266. date_time.nanos = nsecs;
  267. date_time.sec = (time % MIN_IN_HOUR) as u32;
  268. date_time.min = ((time % SECS_IN_HOUR) / MIN_IN_HOUR) as u32;
  269. date_time.hour = (time / SECS_IN_HOUR) as u32;
  270. loop {
  271. let year_size = if leap_year(year) { 366 } else { 365 };
  272. if day_number >= year_size {
  273. day_number -= year_size;
  274. year += 1;
  275. } else {
  276. break
  277. }
  278. }
  279. date_time.year = year;
  280. let mut month = 0;
  281. while day_number >= MONTHS[if leap_year(year) { 1 } else { 0 }][month] {
  282. day_number -= MONTHS[if leap_year(year) { 1 } else { 0 }][month];
  283. month += 1;
  284. }
  285. date_time.month = month as u32 + 1;
  286. date_time.day = day_number as u32 + 1;
  287. date_time
  288. }
  289. }
  290. impl fmt::Display for DateTime {
  291. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  292. write!(
  293. f,
  294. "{:04}-{:02}-{:02} {:02}:{:02}:{:02} UTC",
  295. self.year, self.month, self.day, self.hour, self.min, self.sec
  296. )
  297. }
  298. }
  299. #[derive(Clone, Debug, Default)]
  300. pub struct Date {
  301. pub day: u32,
  302. pub month: u32,
  303. pub year: u32,
  304. }
  305. impl fmt::Display for Date {
  306. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  307. write!(f, "{:04}-{:02}-{:02} UTC", self.year, self.month, self.day)
  308. }
  309. }
  310. pub fn timestamp_to_date(timestamp: u64, format: DateFormat) -> String {
  311. if timestamp == 0 {
  312. return "".to_string()
  313. }
  314. match format {
  315. DateFormat::Default => "".to_string(),
  316. DateFormat::Date => DateTime::from_timestamp(timestamp, 0).date().to_string(),
  317. DateFormat::DateTime => DateTime::from_timestamp(timestamp, 0).to_string(),
  318. DateFormat::Nanos => {
  319. const A_BILLION: u64 = 1_000_000_000;
  320. let dt =
  321. DateTime::from_timestamp(timestamp / A_BILLION, (timestamp % A_BILLION) as u32);
  322. format!(
  323. "{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{} UTC",
  324. dt.year, dt.month, dt.day, dt.hour, dt.min, dt.sec, dt.nanos
  325. )
  326. }
  327. }
  328. }
  329. #[cfg(test)]
  330. mod tests {
  331. use super::{TimeKeeperSafe, Timestamp};
  332. #[test]
  333. #[should_panic]
  334. fn panic_on_unsafe_epoch_length() {
  335. // Ensure panic when epoch_length is 0.
  336. TimeKeeperSafe::new(Timestamp::current_time(), 0, 1, 1);
  337. }
  338. #[test]
  339. #[should_panic]
  340. fn panic_on_unsafe_slot_time() {
  341. TimeKeeperSafe::new(Timestamp::current_time(), 1, 0, 1);
  342. }
  343. }