| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206 |
- /* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
- use crate::{util::time::Timestamp, Result};
- use log::debug;
- use std::{thread, time::Duration};
- use url::Url;
- pub enum Ticks {
- GENESIS { e: u64, sl: u64 }, //genesis epoch
- NEWSLOT { e: u64, sl: u64 }, // new slot
- NEWEPOCH { e: u64, sl: u64 }, // new epoch
- TOCKS, //tocks, or slot is ending
- IDLE, // idle clock state
- OUTOFSYNC, //clock, and blockchain are out of sync
- }
- const BB_SL: u64 = u64::MAX - 1; //big bang slot time (need to be negative value)
- const BB_E: u64 = 0; //big bang epoch time.
- #[derive(Debug)]
- pub struct Clock {
- pub sl: u64, // relative slot index (zero-based) [0-len[
- pub e: u64, // epoch index (zero-based) [0-\inf[
- pub tick_len: u64, // tick length in time (seconds)
- pub sl_len: u64, // slot length in ticks
- pub e_len: u64, // epoch length in slots
- pub peers: Vec<Url>,
- pub genesis_time: Timestamp,
- }
- impl Clock {
- pub fn new(
- e_len: Option<u64>,
- sl_len: Option<u64>,
- tick_len: Option<u64>,
- peers: Vec<Url>,
- ) -> Self {
- let gt: Timestamp = Timestamp::current_time();
- Self {
- sl: BB_SL, //necessary for genesis slot
- e: BB_E,
- tick_len: tick_len.unwrap_or(22), // 22 seconds
- sl_len: sl_len.unwrap_or(22), // ~8 minutes
- e_len: e_len.unwrap_or(3), // 24.2 minutes
- peers,
- genesis_time: gt,
- }
- }
- pub fn get_sl_len(&self) -> u64 {
- self.sl_len
- }
- pub fn get_e_len(&self) -> u64 {
- self.e_len
- }
- async fn time(&self) -> Result<Timestamp> {
- //TODO (fix) add more than ntp server to time, and take the avg
- Ok(Timestamp::current_time())
- }
- /// returns time since genesis in seconds.
- async fn time_to_genesis(&self) -> Timestamp {
- //TODO this value need to be assigned to kickoff time.
- let genesis_time = self.genesis_time.0;
- let abs_time = self.time().await.unwrap();
- Timestamp(abs_time.0 - genesis_time)
- }
- /// return absolute tick to genesis, and relative tick index in the slot.
- async fn tick_time(&self) -> (u64, u64, u64) {
- let time = self.time_to_genesis().await.0;
- let tick_abs: u64 = time / self.tick_len;
- let tick_rel: u64 = time % self.tick_len;
- (time, tick_rel, tick_abs)
- }
- /// return true if the clock is at the begining (before 2/3 of the slot).
- async fn ticking(&self) -> bool {
- let (abs, rel, _) = self.tick_time().await;
- debug!(target: "consensus::clock", "abs time to genesis ticks: {}, rel ticks: {}", abs, rel);
- rel < (self.tick_len) * 2 / 3
- }
- pub async fn sync(&mut self) -> Result<()> {
- let e = self.epoch_abs().await;
- let sl = self.slot_relative().await;
- self.sl = sl;
- self.e = e;
- Ok(())
- }
- /// returns absolute zero based slot index
- async fn slot_abs(&self) -> u64 {
- let sl_abs = self.tick_time().await.0 / self.sl_len;
- debug!(target: "consensus::clock", "[slot_abs] slot len: {} - slot abs: {}", self.sl_len, sl_abs);
- sl_abs
- }
- /// returns relative zero based slot index
- async fn slot_relative(&self) -> u64 {
- let e_abs = self.slot_abs().await % self.e_len;
- debug!(target: "consensus::clock", "[slot_relative] slot len: {} - slot relative: {}", self.sl_len, e_abs);
- e_abs
- }
- /// returns absolute zero based epoch index.
- async fn epoch_abs(&self) -> u64 {
- let res = self.slot_abs().await / self.e_len;
- debug!(target: "consensus::clock", "[epoch_abs] epoch len: {} - epoch abs: {}", self.e_len, res);
- res
- }
- /// return the ticks phase with corresponding phase parameters
- ///
- /// the Ticks enum can include epoch index, and relative slot index (zero-based)
- pub async fn ticks(&mut self) -> Ticks {
- // also debug the failing function.
- let e = self.epoch_abs().await;
- let sl = self.slot_relative().await;
- if self.ticking().await {
- debug!(
- target: "consensus::clock",
- "e/e`: {}/{} sl/sl`: {}/{}, BB_E/BB_SL: {}/{}",
- e, self.e, sl, self.sl, BB_E, BB_SL
- );
- if e == self.e && e == BB_E && self.sl == BB_SL {
- self.sl = sl + 1; // 0
- self.e = e; // 0
- debug!(target: "consensus::clock", "new genesis");
- Ticks::GENESIS { e, sl }
- } else if e == self.e && sl == self.sl + 1 {
- self.sl = sl;
- debug!(target: "consensus::clock", "new slot");
- Ticks::NEWSLOT { e, sl }
- } else if e == self.e + 1 && sl == 0 {
- self.e = e;
- self.sl = sl;
- debug!(target: "consensus::clock", "new epoch");
- Ticks::NEWEPOCH { e, sl }
- } else if e == self.e && sl == self.sl {
- debug!(target: "consensus::clock", "clock is idle");
- thread::sleep(Duration::from_millis(100));
- Ticks::IDLE
- } else {
- debug!(target: "consensus::clock", "clock is out of sync");
- //clock is out of sync
- Ticks::OUTOFSYNC
- }
- } else {
- debug!(target: "consensus::clock", "tocks");
- Ticks::TOCKS
- }
- }
- }
- #[cfg(test)]
- mod tests {
- use super::{Clock, Ticks};
- use futures::executor::block_on;
- use std::{thread, time::Duration};
- #[test]
- fn clock_works() {
- let clock = Clock::new(Some(9), Some(9), Some(9), vec![]);
- //block th for 3 secs
- thread::sleep(Duration::from_millis(1000));
- let ttg = block_on(clock.time_to_genesis()).0;
- assert!((1..2).contains(&ttg));
- }
- fn _clock_ticking() {
- let clock = Clock::new(Some(9), Some(9), Some(9), vec![]);
- //block th for 3 secs
- thread::sleep(Duration::from_millis(1000));
- assert!(block_on(clock.ticking()));
- thread::sleep(Duration::from_millis(1000));
- assert!(block_on(clock.ticking()));
- }
- fn _clock_ticks() {
- let mut clock = Clock::new(Some(9), Some(9), Some(9), vec![]);
- //
- let tick: Ticks = block_on(clock.ticks());
- assert!(matches!(tick, Ticks::GENESIS { e: 0, sl: 0 }));
- thread::sleep(Duration::from_millis(3000));
- let tock: Ticks = block_on(clock.ticks());
- assert!(matches!(tock, Ticks::TOCKS));
- }
- }
|