clock.rs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  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 crate::{util::time::Timestamp, Result};
  19. use log::debug;
  20. use std::{thread, time::Duration};
  21. use url::Url;
  22. pub enum Ticks {
  23. GENESIS { e: u64, sl: u64 }, //genesis epoch
  24. NEWSLOT { e: u64, sl: u64 }, // new slot
  25. NEWEPOCH { e: u64, sl: u64 }, // new epoch
  26. TOCKS, //tocks, or slot is ending
  27. IDLE, // idle clock state
  28. OUTOFSYNC, //clock, and blockchain are out of sync
  29. }
  30. const BB_SL: u64 = u64::MAX - 1; //big bang slot time (need to be negative value)
  31. const BB_E: u64 = 0; //big bang epoch time.
  32. #[derive(Debug)]
  33. pub struct Clock {
  34. pub sl: u64, // relative slot index (zero-based) [0-len[
  35. pub e: u64, // epoch index (zero-based) [0-\inf[
  36. pub tick_len: u64, // tick length in time (seconds)
  37. pub sl_len: u64, // slot length in ticks
  38. pub e_len: u64, // epoch length in slots
  39. pub peers: Vec<Url>,
  40. pub genesis_time: Timestamp,
  41. }
  42. impl Clock {
  43. pub fn new(
  44. e_len: Option<u64>,
  45. sl_len: Option<u64>,
  46. tick_len: Option<u64>,
  47. peers: Vec<Url>,
  48. ) -> Self {
  49. let gt: Timestamp = Timestamp::current_time();
  50. Self {
  51. sl: BB_SL, //necessary for genesis slot
  52. e: BB_E,
  53. tick_len: tick_len.unwrap_or(22), // 22 seconds
  54. sl_len: sl_len.unwrap_or(22), // ~8 minutes
  55. e_len: e_len.unwrap_or(3), // 24.2 minutes
  56. peers,
  57. genesis_time: gt,
  58. }
  59. }
  60. pub fn get_sl_len(&self) -> u64 {
  61. self.sl_len
  62. }
  63. pub fn get_e_len(&self) -> u64 {
  64. self.e_len
  65. }
  66. async fn time(&self) -> Result<Timestamp> {
  67. //TODO (fix) add more than ntp server to time, and take the avg
  68. Ok(Timestamp::current_time())
  69. }
  70. /// returns time since genesis in seconds.
  71. async fn time_to_genesis(&self) -> Timestamp {
  72. //TODO this value need to be assigned to kickoff time.
  73. let genesis_time = self.genesis_time.0;
  74. let abs_time = self.time().await.unwrap();
  75. Timestamp(abs_time.0 - genesis_time)
  76. }
  77. /// return absolute tick to genesis, and relative tick index in the slot.
  78. async fn tick_time(&self) -> (u64, u64, u64) {
  79. let time = self.time_to_genesis().await.0;
  80. let tick_abs: u64 = time / self.tick_len;
  81. let tick_rel: u64 = time % self.tick_len;
  82. (time, tick_rel, tick_abs)
  83. }
  84. /// return true if the clock is at the begining (before 2/3 of the slot).
  85. async fn ticking(&self) -> bool {
  86. let (abs, rel, _) = self.tick_time().await;
  87. debug!(target: "consensus::clock", "abs time to genesis ticks: {}, rel ticks: {}", abs, rel);
  88. rel < (self.tick_len) * 2 / 3
  89. }
  90. pub async fn sync(&mut self) -> Result<()> {
  91. let e = self.epoch_abs().await;
  92. let sl = self.slot_relative().await;
  93. self.sl = sl;
  94. self.e = e;
  95. Ok(())
  96. }
  97. /// returns absolute zero based slot index
  98. async fn slot_abs(&self) -> u64 {
  99. let sl_abs = self.tick_time().await.0 / self.sl_len;
  100. debug!(target: "consensus::clock", "[slot_abs] slot len: {} - slot abs: {}", self.sl_len, sl_abs);
  101. sl_abs
  102. }
  103. /// returns relative zero based slot index
  104. async fn slot_relative(&self) -> u64 {
  105. let e_abs = self.slot_abs().await % self.e_len;
  106. debug!(target: "consensus::clock", "[slot_relative] slot len: {} - slot relative: {}", self.sl_len, e_abs);
  107. e_abs
  108. }
  109. /// returns absolute zero based epoch index.
  110. async fn epoch_abs(&self) -> u64 {
  111. let res = self.slot_abs().await / self.e_len;
  112. debug!(target: "consensus::clock", "[epoch_abs] epoch len: {} - epoch abs: {}", self.e_len, res);
  113. res
  114. }
  115. /// return the ticks phase with corresponding phase parameters
  116. ///
  117. /// the Ticks enum can include epoch index, and relative slot index (zero-based)
  118. pub async fn ticks(&mut self) -> Ticks {
  119. // also debug the failing function.
  120. let e = self.epoch_abs().await;
  121. let sl = self.slot_relative().await;
  122. if self.ticking().await {
  123. debug!(
  124. target: "consensus::clock",
  125. "e/e`: {}/{} sl/sl`: {}/{}, BB_E/BB_SL: {}/{}",
  126. e, self.e, sl, self.sl, BB_E, BB_SL
  127. );
  128. if e == self.e && e == BB_E && self.sl == BB_SL {
  129. self.sl = sl + 1; // 0
  130. self.e = e; // 0
  131. debug!(target: "consensus::clock", "new genesis");
  132. Ticks::GENESIS { e, sl }
  133. } else if e == self.e && sl == self.sl + 1 {
  134. self.sl = sl;
  135. debug!(target: "consensus::clock", "new slot");
  136. Ticks::NEWSLOT { e, sl }
  137. } else if e == self.e + 1 && sl == 0 {
  138. self.e = e;
  139. self.sl = sl;
  140. debug!(target: "consensus::clock", "new epoch");
  141. Ticks::NEWEPOCH { e, sl }
  142. } else if e == self.e && sl == self.sl {
  143. debug!(target: "consensus::clock", "clock is idle");
  144. thread::sleep(Duration::from_millis(100));
  145. Ticks::IDLE
  146. } else {
  147. debug!(target: "consensus::clock", "clock is out of sync");
  148. //clock is out of sync
  149. Ticks::OUTOFSYNC
  150. }
  151. } else {
  152. debug!(target: "consensus::clock", "tocks");
  153. Ticks::TOCKS
  154. }
  155. }
  156. }
  157. #[cfg(test)]
  158. mod tests {
  159. use super::{Clock, Ticks};
  160. use futures::executor::block_on;
  161. use std::{thread, time::Duration};
  162. #[test]
  163. fn clock_works() {
  164. let clock = Clock::new(Some(9), Some(9), Some(9), vec![]);
  165. //block th for 3 secs
  166. thread::sleep(Duration::from_millis(1000));
  167. let ttg = block_on(clock.time_to_genesis()).0;
  168. assert!((1..2).contains(&ttg));
  169. }
  170. fn _clock_ticking() {
  171. let clock = Clock::new(Some(9), Some(9), Some(9), vec![]);
  172. //block th for 3 secs
  173. thread::sleep(Duration::from_millis(1000));
  174. assert!(block_on(clock.ticking()));
  175. thread::sleep(Duration::from_millis(1000));
  176. assert!(block_on(clock.ticking()));
  177. }
  178. fn _clock_ticks() {
  179. let mut clock = Clock::new(Some(9), Some(9), Some(9), vec![]);
  180. //
  181. let tick: Ticks = block_on(clock.ticks());
  182. assert!(matches!(tick, Ticks::GENESIS { e: 0, sl: 0 }));
  183. thread::sleep(Duration::from_millis(3000));
  184. let tock: Ticks = block_on(clock.ticks());
  185. assert!(matches!(tock, Ticks::TOCKS));
  186. }
  187. }