mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-04-28 03:00:18 -04:00
207 lines
7.2 KiB
Rust
207 lines
7.2 KiB
Rust
/* 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));
|
|
}
|
|
}
|